ver. 0.293: Code review fixes — 6 repositories, 16 fixes

- ArticleRepository: SQL injection fix (addslashes→parameterized), DRY refactor topArticles/newsListArticles
- AttributeRepository: dead class_exists('\S') blocking cache/temp clear
- CategoryRepository: dead class_exists('\S') blocking SEO link generation (critical)
- BannerRepository: parameterize $today in SQL + null guard on query()
- BasketCalculator: null guard checkProductQuantityInStock + optional DI params
- PromotionRepository: null guard on $basket (production fatal)
- OrderRepository/ShopBasketController/ajax.php: explicit DI in BasketCalculator callers

614 tests, 1821 assertions (+4 new)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-19 01:07:39 +01:00
parent 29821bccf2
commit 054b1b4a34
19 changed files with 297 additions and 218 deletions

View File

@@ -17,101 +17,140 @@ Wygenerowano: 2026-02-18
<a id="domain"></a>
## 1. Domain/ — Warstwa domenowa
### Domain\Article\ArticleRepository
### Domain\Article\ArticleRepository ✅ REVIEWED
File: `autoload/Domain/Article/ArticleRepository.php`
Properties:
- `private $db`
- `private const MAX_PER_PAGE = 100`
Methods:
- `public function __construct($db)`
- `public function listForAdmin(array $filters, string $sortColumn = 'id', string $sortDir = 'DESC', int $page = 1, int $perPage = 15): array`
- `public function find(int $articleId): array`
- `public function save(array $data): ?int`
- `public function delete(int $articleId): bool`
- `public function toggleStatus(int $articleId): bool`
- `public function detailsForLanguage(int $articleId, string $langId): ?array`
- `public function frontArticleDetails(int $articleId, string $langId): array`
- `public function frontArticleDetailsBySeoLink(string $seoLink, string $langId): ?array`
- `public function frontArticleList(string $langId, int $page = 1, int $perPage = 12): array`
- `public function frontArticleListAll(string $langId): array`
- `public function getFirstImageCached(int $articleId)`
- `private function baseListSelect(): string`
- `private function translationsMap(int $articleId): array`
- `private function extractTranslations(array $data): array`
- `private function toSwitchValue($value): int`
- `private function defaultArticle(): array`
- `private function clearFrontCache(int $articleId): void`
- `private function saveSeoRedirects(int $articleId, string $langId, string $newSeoLink, string $currentSeoLink): void`
Public Methods:
- `public function __construct($db)`
- `public function find(int $articleId): ?array` — try/catch na brak kolumny `o` (kompatybilność)
- `public function save(int $articleId, array $data, int $userId): int`
- `public function archive(int $articleId): bool`
- `public function restore(int $articleId): bool`
- `public function deletePermanently(int $articleId): bool`
- `public function listForAdmin(array $filters, string $sortColumn = 'date_add', string $sortDir = 'DESC', int $page = 1, int $perPage = 15): array`
- `public function listArchivedForAdmin(array $filters, string $sortColumn = 'date_add', string $sortDir = 'DESC', int $page = 1, int $perPage = 15): array`
- `public function saveGalleryOrder(int $articleId, string $order): bool`
- `public function saveFilesOrder(int $articleId, string $order): bool`
- `public function pagesSummaryForArticles(array $articleIds): array`
- `public function updateImageAlt(int $imageId, string $imageAlt): bool`
- `public function updateFileName(int $fileId, string $fileName): bool`
- `public function markFileToDelete(int $fileId): bool`
- `public function markImageToDelete(int $imageId): bool`
- `public function deleteNonassignedFiles(): void`
- `public function deleteNonassignedImages(): void`
- 🔧 `public function articlesByDateAdd(string $dateStart, string $dateEnd, string $langId = 'pl'): array` — naprawiono SQL injection (addslashes→parameterized), dodano parametr $langId
- 🔧 `public function articleDetailsFrontend(int $articleId, string $langId): ?array` — select+foreach → get() (uproszczono)
- 🔧 `public function articlesIds(int $pageId, string $langId, int $limit, int $sortType, int $from): ?array` — parametryzacja $langId
- 🔧 `public function pageArticlesCount(int $pageId, string $langId): int` — parametryzacja $langId
-`public function pageArticles(array $page, string $langId, int $bs): array`
-`public function news(int $pageId, int $limit, string $langId): ?array`
-`public function articleNoindex(int $articleId, string $langId): bool`
- 🔧 `public function topArticles(int $pageId, int $limit, string $langId): ?array` — parametryzacja $langId + DRY via fetchArticlesByPage()
- 🔧 `public function newsListArticles(int $pageId, int $limit, string $langId): ?array` — parametryzacja $langId + DRY via fetchArticlesByPage()
Private Methods:
-`private function createArticle(array $data, int $userId): int`
-`private function updateArticle(int $articleId, array $data, int $userId): int`
-`private function buildArticleRow(array $data, int $userId, bool $isNew): array`
-`private function buildLangRow($langId, array $data): array`
-`private function applyGalleryOrderIfProvided(int $articleId, array $data): void`
-`private function applyFilesOrderIfProvided(int $articleId, array $data): void`
-`private function saveTranslations(int $articleId, array $data, bool $isNew): void`
-`private function savePages(int $articleId, $pages, bool $isNew): void`
-`private function assignTempFiles(int $articleId): void`
-`private function assignTempImages(int $articleId): void`
-`private function deleteMarkedImages(int $articleId): void`
-`private function deleteMarkedFiles(int $articleId): void`
-`private function maxPageOrder(): int`
-`private function isCheckedValue($value): bool`
-`private function appendDateRangeFilter(array &$where, array &$params, string $column, string $fromKey, string $toKey, array $filters): void`
- 🔧 `private function fetchArticlesByPage(string $cachePrefix, int $pageId, int $limit, string $langId, string $orderBy): ?array` — NOWA, wspólna logika topArticles/newsListArticles
---
### Domain\Attribute\AttributeRepository
### Domain\Attribute\AttributeRepository ✅ REVIEWED
File: `autoload/Domain/Attribute/AttributeRepository.php`
Properties:
- `private $db`
- `private ?string $defaultLangId = null`
- `private const MAX_PER_PAGE = 100`
Methods:
- `public function __construct($db)`
- `public function listForAdmin(array $filters, string $sortColumn = 'id', string $sortDir = 'DESC', int $page = 1, int $perPage = 15): array`
- `public function find(int $attributeId): array`
- `public function save(array $data): ?int`
- `public function delete(int $attributeId): bool`
- `public function detailsForLanguage(int $attributeId, string $langId): ?array`
- `public function getAttributeValues(int $attributeId): array`
- `public function findValue(int $valueId): array`
- `public function saveValue(array $data): ?int`
- `public function deleteValue(int $valueId): bool`
- `public function detailsForValueLanguage(int $valueId, string $langId): ?array`
- `public function allForAdmin(): array`
- `public function allValuesForAttribute(int $attributeId): array`
- `public function productAttributes(int $productId): array`
- `public function valueDetails(int $valueId): ?array`
- `public function isValueDefault(int $valueId): int`
- `public function getAttributeOrder(int $attributeId): int`
- `public function getAttributeNameById(int $attributeId, string $langId): string`
- `public function getAttributeValueById(int $valueId, string $langId): string`
- `private function baseListSelect(): string`
- `private function translationsMap(int $attributeId): array`
- `private function extractTranslations(array $data): array`
- `private function valueTranslationsMap(int $valueId): array`
- `private function extractValueTranslations(array $data): array`
- `private function toSwitchValue($value): int`
- `private function defaultAttribute(): array`
- `private function defaultValue(): array`
Public Methods:
- `public function __construct($db)`
- `public function listForAdmin(array $filters, string $sortColumn = 'o', string $sortDir = 'ASC', int $page = 1, int $perPage = 15): array`
- `public function findAttribute(int $attributeId): array`
- `public function saveAttribute(array $data): ?int`
- `public function deleteAttribute(int $attributeId): bool`
- `public function findValues(int $attributeId): array`
- `public function saveValues(int $attributeId, array $payload): bool`
- `public function saveLegacyValues(int $attributeId, array $names, array $values, array $ids, $defaultValue, array $impactOnThePrice): ?int`
- `public function valueDetails(int $valueId): array`
- `public function getAttributeNameById(int $attributeId, ?string $langId = null): string`
- `public function getAttributeValueById(int $valueId, ?string $langId = null): string` — z cache
- `public function getAttributesListForCombinations(): array`
- `public function frontAttributeDetails(int $attributeId, string $langId): array` — z cache
- `public function frontValueDetails(int $valueId, string $langId): array` — z cache
- `public function isValueDefault(int $valueId)`
- `public function getAttributeOrder(int $attributeId)`
- `public function getAttributeNameByValue(int $valueId, string $langId)` — parametryzowane query
Private Methods:
- `private function buildAdminWhere(array $filters): array`
- `private function saveAttributeTranslations(int $attributeId, array $names): void`
- `private function saveValueTranslations(int $valueId, array $translations): void`
- `private function valueBelongsToAttribute(int $valueId, int $attributeId): bool`
- `private function normalizeValueRows(array $rows): array`
- `private function refreshCombinationPricesForValue(int $valueId, ?string $impactOnThePrice): void`
- `private function toSwitchValue($value): int`
- `private function toTypeValue($value): int`
-`private function toNullableNumeric($value): ?string`
-`private function defaultAttribute(): array`
-`private function nextOrder(): int`
-`private function defaultLanguageId(): string`
-`private function clearFrontCache(int $id, string $type): void`
- 🔧 `private function clearTempAndCache(): void` — usunięto martwy check `class_exists('\S')`
-`private function normalizeDecimal(float $value, int $precision = 2): float`
---
### Domain\Banner\BannerRepository
### Domain\Banner\BannerRepository ✅ REVIEWED
File: `autoload/Domain/Banner/BannerRepository.php`
Properties:
- `private $db`
- `private const MAX_PER_PAGE = 100`
Methods:
- `public function __construct($db)`
- `public function listForAdmin(array $filters = [], string $sortColumn = 'id', string $sortDir = 'DESC', int $page = 1, int $perPage = 15): array`
- `public function find(int $bannerId): ?array`
- `public function save(array $data): ?int`
- `public function delete(int $bannerId): bool`
- `public function allActiveCached(): array`
- `private function toSwitchValue($value): int`
Public Methods:
- `public function __construct($db)`
- `public function find(int $bannerId): ?array` — pobiera baner + tłumaczenia
- `public function delete(int $bannerId): bool`
- `public function save(array $data)` — insert/update, obsługuje nowy i legacy format
- `public function listForAdmin(array $filters, string $sortColumn = 'name', string $sortDir = 'ASC', int $page = 1, int $perPage = 15): array` — z thumbnailami
- 🔧 `public function banners(string $langId): ?array` — parametryzacja $today + null guard na query()
- 🔧 `public function mainBanner(string $langId): ?array` — parametryzacja $today + null guard na query()
Private Methods:
-`private function fetchThumbnailsByBannerIds(array $bannerIds): array` — parametryzowane IN
-`private function saveTranslations(int $bannerId, array $src, array $url, array $html, array $text): void` — legacy format
-`private function saveTranslationsFromArray(int $bannerId, array $translations): void` — nowy format
-`private function upsertTranslation(int $bannerId, $langId, array $fields): void` — count+insert/update
---
### Domain\Basket\BasketCalculator
### Domain\Basket\BasketCalculator ✅ REVIEWED
File: `autoload/Domain/Basket/BasketCalculator.php`
Properties: (brak)
Properties: (brak — klasa statyczna)
Methods:
- `public static function summaryPrice($basket, $coupon): float`
- `public static function summaryPriceWithTransport($basket, $coupon, $transport_cost): float`
- `public static function summaryQty($basket): int`
- `public static function summaryWp($basket): float`
- `public static function summaryCoupon($basket, $coupon): float`
- `public static function summaryNetto($basket): float`
- `public static function summaryBrutto($basket): float`
- `public static function summaryWp($basket)` — suma wag, is_array guard
- `public static function countProductsText($count)` — polska pluralizacja
- 🔧 `public static function summaryPrice($basket, $coupon = null, $langId = null, $productRepo = null)` — dodano opcjonalne DI params z fallbackiem do globals
- `public static function countProducts($basket)` — suma ilości, is_array guard
- `public static function validateBasket($basket)` — null guard
- 🔧 `public static function checkProductQuantityInStock($basket, bool $message = false)` — dodano is_array guard (bug: foreach na null)
- 🔧 `public static function calculateBasketProductPrice(float $price_brutto_promo, float $price_brutto, $coupon, $basket_position, $productRepo = null)` — dodano opcjonalny $productRepo z fallbackiem do globals
---