ver. 0.289: ShopCategory + ShopClient frontend migration to Domain + Views + Controllers

ShopCategory: 9 frontend methods in CategoryRepository, front\Views\ShopCategory (3 methods),
deleted factory + view, updated 6 callers, +17 tests.

ShopClient: 13 frontend methods in ClientRepository, front\Views\ShopClient (8 methods),
front\Controllers\ShopClientController (15 methods + buildEmailBody helper),
deleted factory + view + controls, updated 7 callers, +36 tests.

Security fix: removed hardcoded password bypass 'Legia1916'.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-17 10:41:40 +01:00
parent 437d4c78dc
commit d29d396197
34 changed files with 2049 additions and 961 deletions

View File

@@ -15,7 +15,7 @@ Panel administratora (33 moduły) został w pełni zmigrowany na architekturę D
|-------|--------|-----------------|
| Site | Router główny | route(), check_url_params(), title() |
| ShopBasket | ZMIGROWANY do `front\Controllers\ShopBasketController` | Operacje koszyka, add/remove/quantity, checkout |
| ShopClient | Fasada | Deleguje do factory |
| ShopClient | ZMIGROWANY do `front\Controllers\ShopClientController` | Logowanie, rejestracja, odzyskiwanie hasla, adresy, zamowienia |
| ShopOrder | KRYTYCZNY | Webhooki płatności (tPay, Przelewy24, Hotpay) — bezpośrednie operacje DB |
| ShopProduct | Fasada | lazy_loading, warehouse_message, draw_product_attributes |
| ShopProducer | Fasada | list(), products() |
@@ -27,8 +27,8 @@ Panel administratora (33 moduły) został w pełni zmigrowany na architekturę D
|-------|--------|--------------------|
| ShopProduct | ORYGINALNA LOGIKA (~370 linii) | KRYTYCZNY — product_details(), promoted/top/new products |
| ShopOrder | ORYGINALNA LOGIKA (~180 linii) | KRYTYCZNY — basket_save() tworzy zamówienie |
| ShopClient | ORYGINALNA LOGIKA | KRYTYCZNY — login (BUG: hardcoded bypass 'Legia1916'), signup, recover |
| ShopCategory | ORYGINALNA LOGIKA | WYSOKI — złożone SQL z language fallback |
| ShopClient | ZMIGROWANA do `ClientRepository` + `ShopClientController` — usunięta | — |
| ShopCategory | ZMIGROWANA do `CategoryRepository` — usunięta | — |
| Articles | ORYGINALNA LOGIKA | WYSOKI — złożone SQL z language fallback |
| ShopPromotion | ORYGINALNA LOGIKA | WYSOKI — silnik promocji (5 typów) |
| ShopBasket | ZMIGROWANA do `Domain\Basket\BasketCalculator` — usunięta | — |
@@ -50,13 +50,14 @@ Panel administratora (33 moduły) został w pełni zmigrowany na architekturę D
| Klasa | Status |
|-------|--------|
| Site | KRYTYCZNY — show() ~600 linii, pattern substitution engine |
| ShopCategory | VIEW z logiką routingu (infinite scroll vs pagination) |
| ShopCategory | PRZENIESIONA do `front\Views\ShopCategory` |
| Articles | Czyste VIEW |
| Scontainers | PRZENIESIONA do `front\Views\Scontainers` |
| Menu | PRZENIESIONA do `front\Views\Menu` |
| Banners | PRZENIESIONA do `front\Views\Banners` |
| Languages, Newsletter | PRZENIESIONE do `front\Views\` (nowy namespace) |
| ShopClient, ShopOrder, ShopPaymentMethod | Czyste VIEW |
| ShopClient | PRZENIESIONA do `front\Views\ShopClient` |
| ShopOrder, ShopPaymentMethod | Czyste VIEW |
| ShopTransport | PUSTA klasa (placeholder) |
### shop/ (14 klas — encje biznesowe)
@@ -111,7 +112,7 @@ articles(8), banner(2), controls(1), menu(4), newsletter(2), scontainers(1), sho
## Znane bugi do naprawy podczas refaktoringu
1. **KRYTYCZNY** `front\factory\ShopClient::login()` — hardcoded password bypass `'Legia1916'`
1. ~~**KRYTYCZNY** `front\factory\ShopClient::login()` — hardcoded password bypass `'Legia1916'`~~ **NAPRAWIONE**`ClientRepository::authenticate()` bez bypass
2. `front\factory\Settings::get_single_settings_value()` — ignoruje `$param`, zawsze zwraca `firm_name`
3. ~~`front\factory\Newsletter::newsletter_unsubscribe()` — błędna składnia SQL w delete~~ **NAPRAWIONE**`NewsletterRepository::unsubscribe()` z poprawną składnią medoo `delete()`
4. ~~`cms\Layout::__get()` — referuje nieistniejące `$this->data`~~ **NAPRAWIONE** — klasa usunięta, zastąpiona przez `$layoutsRepo->find()`
@@ -204,17 +205,40 @@ Legacy Cleanup
---
### Etap: Category Frontend Service
### Etap: Category Frontend Service — ZREALIZOWANY
**Cel:** Migracja `front\factory\ShopCategory` do Domain.
**Cel:** Migracja `front\factory\ShopCategory` i `front\view\ShopCategory` do Domain + Views.
**UWAGA:** Zamiast tworzenia osobnego `CategoryFrontendService`, metody dodano do istniejącego `CategoryRepository` (zgodnie z wzorcem projektu).
**DODANE METODY (do istniejącej klasy `CategoryRepository`):**
- `getCategorySort(int $categoryId, string $langId): int` — z Redis cache
- `categoryName(int $categoryId, string $langId): string` — z Redis cache
- `categoryUrl(int $categoryId, string $langId): string` — z Redis cache
- `frontCategoryDetails(int $categoryId, string $langId): ?array` — z Redis cache
- `categoriesTree(int $parentId, string $langId): array` — rekurencyjne, z Redis cache
- `blogCategoryProducts(int $categoryId, string $langId, int $sortType, int $from, int $limit): ?array` — złożone SQL z language fallback
- `categoryProductsCount(int $categoryId, string $langId): int`
- `productsId(int $categoryId, string $langId, int $sortType): ?array` — złożone SQL z language fallback
- `paginatedCategoryProducts(int $categoryId, string $langId, int $sortType, int $from, int $limit): ?array`
- Stałe: `SORT_ORDER_SQL`, `PRODUCTS_PER_PAGE`, `LANGUAGE_FALLBACK_NAME_SQL`
- Testy: +17 w `CategoryRepositoryTest`
**NOWE:**
- `Domain/Category/CategoryFrontendService.php``getCategorySort()`, `categoryName()`, `categoryUrl()`, `blogCategoryProducts()`, `categoryProducts()`, `productsId()` (złożone SQL z sortowaniem/language fallback/paginacją), `categoryDetails()`, `categoriesDetails()` (rekurencyjne), `categoryProductsCount()`
- Testy: `CategoryFrontendServiceTest`
- `front\Views\ShopCategory` — czysty VIEW (`categoryDescription()`, `categoryView()`, `categories()`)
- BUG FIX: `categoryView()` `category_products_count()` wywoływane z tablicą zamiast ID
**ZMIANA:**
- `front/factory/ShopCategory`fasada
- `front/factory/ShopProduct::product_categories()` → deleguje do `CategoryFrontendService`
- `front/factory/ShopCategory`USUNIETA (logika przeniesiona do `CategoryRepository`)
- `front/view/ShopCategory` → USUNIETA (zastąpiona przez `front\Views\ShopCategory`)
- `index.php` — przepięcie `category_name` na `categoryName`
- `front\view\Site::show()` — przepięcie `categoriesTree`, `frontCategoryDetails`, `blogCategoryProducts`
- `front\controls\Site::route()` — przepięcie `categoryView`
- `templates/shop-category/categories.php` — przepięcie na `\front\Views\ShopCategory::categories()`
- `templates/menu/pages.php` — przepięcie `category_url` na `categoryUrl`
- `front\controls\ShopProduct` — przepięcie `products_id` + `get_category_sort`
**Testy:** 501 OK, 1562 asercji
---
@@ -345,21 +369,44 @@ front\factory\ShopPromotion::promotion_type_XX() → shop\Product::is_product_on
---
### Etap: Client Authentication (Security Fix)
### Etap: Client Authentication (Security Fix) — ZREALIZOWANY
**Cel:** Migracja `front\factory\ShopClient` + NAPRAWIENIE hardcoded password bypass.
**Cel:** Migracja `front\factory\ShopClient`, `front\view\ShopClient`, `front\controls\ShopClient` + NAPRAWIENIE hardcoded password bypass.
**UWAGA:** Zamiast tworzenia osobnego `ClientFrontendService`, metody dodano do istniejącego `ClientRepository` (zgodnie z wzorcem projektu). Kontroler utworzony jako `front\Controllers\ShopClientController` z DI.
**DODANE METODY (do istniejącej klasy `ClientRepository`):**
- Simple CRUD: `clientDetails()`, `clientEmail()`, `clientAddresses()`, `addressDetails()`, `addressDelete()`, `addressSave(int $clientId, ?int $addressId, array $data)`, `markAddressAsCurrent()`
- `clientOrders()` — zachowuje zależność od `\front\factory\ShopOrder::order_details()`
- `authenticate(string $email, string $password)`**BEZ** bypassa 'Legia1916', zwraca `['status' => 'ok'|'error'|'inactive', ...]`
- `createClient()` — zwraca `['id' => ..., 'hash' => ...]` lub null
- `confirmRegistration()` — zwraca email lub null
- `generateNewPassword()` — zwraca `['email' => ..., 'password' => ...]` lub null
- `initiatePasswordRecovery()` — zwraca hash lub null
- Testy: +36 w `ClientRepositoryTest` (guard clauses, authenticate 5 scenariuszy, createClient, confirmRegistration, generateNewPassword, initiatePasswordRecovery, address CRUD, markAddressAsCurrent)
**NOWE:**
- `Domain/Client/ClientFrontendService.php`:
- `login()`**BEZ** bypassa 'Legia1916', z opcjonalną migracją md5 → password_hash
- `signup()`, `registerConfirm()`, `sendPasswordRecovery()`, `resetPassword()`
- `clientDetails()`, `clientOrders()`
- CRUD adresów: `saveAddress()`, `deleteAddress()`, `getAddresses()`, `markAddressAsCurrent()`
- Testy: `ClientFrontendServiceTest`**KRYTYCZNY test: login z 'Legia1916' NIE przechodzi**
- `front\Views\ShopClient` — czysty VIEW (8 metod camelCase: addressEdit, clientAddresses, clientMenu, clientOrders, recoverPassword, miniLogin, loginForm, registerForm)
- `front\Controllers\ShopClientController` — instancyjny kontroler z DI (`ClientRepository` przez konstruktor)
- 15 metod publicznych (camelCase)
- Prywatny helper `buildEmailBody(string $templateName, array $replacements = [])` — deduplikuje 4× powtórzony wzorzec budowania emaili z newslettera
- `authenticate()` zwraca dane → kontroler obsługuje sesję/flash/redirect (separation of concerns)
- `addressSave()` przyjmuje `array $data` zamiast 6 parametrów
**ZMIANA:**
- `front/factory/ShopClient`fasada
- `front/controls/ShopClient`deleguje do serwisu
- `front/factory/ShopClient`USUNIETA (logika przeniesiona do `ClientRepository`)
- `front/view/ShopClient`USUNIETA (zastąpiona przez `front\Views\ShopClient`)
- `front/controls/ShopClient` → USUNIETA (zastąpiona przez `front\Controllers\ShopClientController`)
- `front\controls\Site::getControllerFactories()` — zarejestrowany `'ShopClient'`
- `front\factory\ShopOrder` — przepięcie `client_email` na `ClientRepository::clientEmail()`
- `front\Controllers\ShopBasketController` — przepięcie `client_addresses` na `ClientRepository::clientAddresses()`
- `front\view\Site` — przepięcie `mini_login` na `\front\Views\ShopClient::miniLogin()`
- 3 szablony `shop-client/*` — przepięcie `client_menu` na `\front\Views\ShopClient::clientMenu()`
- `templates/shop-basket/summary-view.php` — przepięcie `login_form` na `\front\Views\ShopClient::loginForm()`
**SECURITY FIX:** Hardcoded password bypass `'Legia1916'` usunięty — `authenticate()` sprawdza wyłącznie md5(register_date + password)
**Testy:** 537 OK, 1648 asercji
---
@@ -539,11 +586,11 @@ front\factory\ShopPromotion::promotion_type_XX() → shop\Product::is_product_on
| Etap | Zakres | Priorytet | Nowe klasy Domain | Testy |
|------|--------|-----------|-------------------|-------|
| Settings + Languages | Fundamenty | FUNDAMENT | 2 serwisy | 2 |
| Category Frontend | Kategorie | WYSOKI | 1 serwis | 1 |
| ~~Category Frontend~~ | ~~Kategorie~~ | ZREALIZOWANY | — | |
| ~~Banners/Menu/Pages/Articles/Layouts~~ | ~~Treści~~ | ZREALIZOWANY | — | — |
| Promotion Engine | Promocje | KRYTYCZNY | 1 serwis | 1 |
| Product Frontend | Produkty | KRYTYCZNY | 1 serwis | 1 |
| Client/Auth (security fix) | Klienci | KRYTYCZNY | 1 serwis | 1 |
| ~~Client/Auth (security fix)~~ | ~~Klienci~~ | ZREALIZOWANY | — | |
| Transport/Payment/Coupon | Dostawa/Płatności | WYSOKI | 3 serwisy | 3 |
| Basket Service | Koszyk | WYSOKI | 1 serwis | 1 |
| Product Instance + Cache | Produkt cache | ŚREDNI | 1 loader | 1 |