docs: add frontend refactoring plan for shopPRO

This commit is contained in:
2026-02-16 09:56:30 +01:00
parent a63a08ff1e
commit 7fcac87a58

View File

@@ -0,0 +1,476 @@
# Plan refaktoryzacji frontendu shopPRO
## Kontekst
Panel administratora (33 moduły) został w pełni zmigrowany na architekturę Domain + DI + Controllers (ver. 0.2370.277). Teraz kolej na frontend: klasy `front/`, `shop/`, `cms/`, klasy utility z `autoload/` oraz szablony `templates/`.
**Cel:** przenieść logikę biznesową z warstwy frontendowej do warstwy domenowej (`Domain/`), zachowując kompatybilność wsteczną przez fasady. Zastosować te same wzorce co w adminie (DI, repozytoria/serwisy, testy PHPUnit).
---
## Inwentaryzacja — co istnieje
### front/controls/ (8 klas — handlery requestów)
| Klasa | Status | Logika biznesowa |
|-------|--------|-----------------|
| Site | Router główny | route(), check_url_params(), title() |
| ShopBasket | MIXED | Operacje koszyka, add/remove/quantity, checkout |
| ShopClient | Fasada | Deleguje do factory |
| 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() |
| ShopCoupon | Fasada | use_coupon(), delete_coupon() |
| Newsletter | Fasada | signin(), confirm(), unsubscribe() |
### front/factory/ (20 klas — pobieranie danych + logika)
| Klasa | Status | Priorytet migracji |
|-------|--------|--------------------|
| 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 |
| Articles | ORYGINALNA LOGIKA | WYSOKI — złożone SQL z language fallback |
| ShopPromotion | ORYGINALNA LOGIKA | WYSOKI — silnik promocji (5 typów) |
| ShopBasket | Fasada | ŚREDNI — summary_price, count |
| ShopTransport | CZĘŚCIOWO zmigrowana | ŚREDNI — transport_methods z filtrowaniem |
| ShopPaymentMethod | ZMIGROWANA (Domain) | — |
| ShopStatuses | ZMIGROWANA (Domain) | — |
| Scontainers | ZMIGROWANA (Domain) | — |
| Newsletter | CZĘŚCIOWO zmigrowana | ŚREDNI |
| Settings | Fasada (BUG: get_single_settings_value ignoruje $param) | NISKI |
| Languages | Fasada | NISKI |
| Layouts | Fasada | NISKI |
| Banners | Fasada | NISKI |
| Menu | Fasada | NISKI |
| Pages | Fasada | NISKI |
| ShopAttribute | Fasada | NISKI |
| ShopCoupon | Model danych | NISKI |
### front/view/ (12 klas — renderowanie)
| Klasa | Status |
|-------|--------|
| Site | KRYTYCZNY — show() ~600 linii, pattern substitution engine |
| ShopCategory | VIEW z logiką routingu (infinite scroll vs pagination) |
| Articles, Banners, Languages, Menu, Newsletter, Scontainers | Czyste VIEW |
| ShopClient, ShopOrder, ShopPaymentMethod | Czyste VIEW |
| ShopTransport | PUSTA klasa (placeholder) |
### shop/ (14 klas — encje biznesowe)
| Klasa | Linii | Status | Priorytet |
|-------|-------|--------|-----------|
| Product | ~950 | KRYTYCZNY — pricing, atrybuty, search, cache, permutacje | KRYTYCZNY |
| Order | ~500+ | KRYTYCZNY — statusy, Apilo sync, emaile, webhooki | KRYTYCZNY |
| Promotion | ~200 | WYSOKI — matching aktywnych promocji + delegacja do factory | WYSOKI |
| Basket | ~80 | ŚREDNI — walidacja stanów magazynowych | ŚREDNI |
| Category | ~60 | NISKI — proste zapytania, pusty konstruktor | NISKI |
| Search | ~80 | NISKI — wrapper na szablony | NISKI |
| Coupon | ~60 | NISKI — niekompletne metody | NISKI |
| Transport | ~30 | NISKI — transport_list() | NISKI |
| PaymentMethod | — | ZMIGROWANA (fasada do Domain) | — |
| Producer | — | ZMIGROWANA (fasada do Domain) | — |
| ProductSet | — | ZMIGROWANA (fasada do Domain) | — |
| ProductAttribute | ~100 | OK — dobry caching | — |
| ProductCustomField | ~50 | OK — Redis caching | — |
| Shop | ~30 | OK — utility do cen | — |
### autoload/ root (klasy utility)
| Klasa | Linii | Status |
|-------|-------|--------|
| S | ~1130 | htacces() ~500 linii — generowanie .htaccess; reszta to utility |
| Tpl | ~90 | OK — silnik szablonów, bez zmian |
| CacheHandler | ~50 | OK — Redis wrapper |
| RedisConnection | ~40 | OK — singleton |
| Email | ~100 | OK — PHPMailer wrapper (drobne poprawki) |
| Log | ~20 | OK — audit logging |
| DbModel | ~60 | OK — base ORM |
| Cache | ~50 | LEGACY — file-based cache, rozważyć usunięcie |
| Html | ~80 | OK — form helpers |
| Image | ~100 | OK — GD wrapper |
| Mobile_Detect | — | Third-party, bez zmian |
### cms/ (1 klasa)
| Klasa | Status |
|-------|--------|
| Layout | BUG w __get() — referuje $this->data które nie istnieje |
### templates/ (75 plików w 15 modułach)
articles(8), banner(2), controls(1), menu(4), newsletter(2), scontainers(1), shop-basket(9), shop-category(6), shop-client(8), shop-coupon(1), shop-order(3), shop-producer(3), shop-product(12), shop-search(3), site(11)
### Entry points
- `index.php` — główny frontend (autoload, sesja, DB, routing, DOM post-processing)
- `ajax.php` — AJAX handler (koszyk, transport, kontakt)
- `api.php` — REST API (Ekomi CSV export)
- `download.php` — pobieranie plików
- `cron-turstmate.php` — TrustMate integracja
---
## Znane bugi do naprawy podczas refaktoringu
1. **KRYTYCZNY** `front\factory\ShopClient::login()` — hardcoded password bypass `'Legia1916'`
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
4. `cms\Layout::__get()` — referuje nieistniejące `$this->data`
5. `shop\Search` — typo w use: `shop\Produt` (brak 'c')
---
## Kolejność migracji (graf zależności)
```
Settings, Languages (liście — brak zależności)
Banners, Menu, Pages, Articles, Layouts (zależą od Languages)
Category (zależy od Languages)
Promotion (zależy od Category — rozbicie circular dep)
Product Frontend (zależy od Category, Promotion, Languages)
Client/Auth (standalone + security fix)
Transport, Payment, Coupon (frontend serwisy)
Basket (zależy od Product, Promotion, Transport, Coupon)
Order Creation (zależy od Basket, Product, Transport, Payment)
Payment Webhooks (zależy od Order)
shop\Order instance + Apilo (zależy od Order Operations)
shop\Product instance + cache (zależy od ProductFrontendService)
Frontend App + Controllers (nowa warstwa DI)
Site Layout Engine (zależy od wszystkiego)
Entry Point Unification (index.php, ajax.php)
Legacy Cleanup
```
---
## Plan wersjonowany
### ver. 0.278 — Settings + Languages Frontend Services
**Cel:** Stworzyć serwisy domenowe dla Settings i Languages, naprawić buga Settings.
**NOWE:**
- `Domain/Settings/SettingsFrontendService.php``allSettings()`, `getSingleValue($param)` (FIX: używa poprawnie $param)
- `Domain/Languages/LanguagesFrontendService.php``defaultLanguage()`, `activeLanguages()`, `translations($lang)`
- Testy: `SettingsFrontendServiceTest`, `LanguagesFrontendServiceTest`
**ZMIANA:**
- `front/factory/Settings` → fasada delegująca do `SettingsFrontendService`
- `front/factory/Languages` → fasada delegująca do `LanguagesFrontendService`
**BUG FIX:** `get_single_settings_value()` — zmiana `['param' => 'firm_name']` na `['param' => $param]`
---
### ver. 0.279 — Category Frontend Service
**Cel:** Migracja `front\factory\ShopCategory` do Domain.
**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`
**ZMIANA:**
- `front/factory/ShopCategory` → fasada
- `front/factory/ShopProduct::product_categories()` → deleguje do `CategoryFrontendService`
---
### ver. 0.280 — Banners, Menu, Pages, Articles, Layouts Frontend Services
**Cel:** Migracja pozostałych fabryk "liściowych".
**NOWE:**
- `Domain/Banner/BannerFrontendService.php``mainBanner()`, `banners()` (filtrowanie po datach)
- `Domain/Menu/MenuFrontendService.php``menuDetails()`, `menuPages()` (rekurencja)
- `Domain/Pages/PagesFrontendService.php``pageDetails()`, `mainPageId()`, `langUrl()`, `pageSort()`
- `Domain/Article/ArticleFrontendService.php``articleDetails()`, `news()`, `pageArticles()`, `pageArticlesCount()`, `generateTableOfContents()`, `generateHeadersIds()`
- `Domain/Layouts/LayoutsFrontendService.php``activeLayout()`, `articleLayout()`, `productLayout()`, `categoryLayout()`, `defaultLayout()`, `categoryDefaultLayout()`
- Testy: 5 plików testowych
**ZMIANA:**
- `front/factory/Banners`, `Menu`, `Pages`, `Articles`, `Layouts` → fasady
**BUG FIX:** `cms\Layout::__get()` — poprawka referencji do `$this->data`
---
### ver. 0.281 — Promotion Engine (rozbicie circular dependency)
**Cel:** Przeniesienie silnika promocji do Domain. Rozbicie cyklicznej zależności `shop\Promotion ↔ front\factory\ShopPromotion`.
**Obecna zależność cykliczna:**
```
shop\Promotion::find_promotion() → front\factory\ShopPromotion::promotion_type_XX()
front\factory\ShopPromotion::promotion_type_XX() → shop\Product::is_product_on_promotion()
```
**Rozwiązanie:** Wszystko w jednym serwisie `PromotionFrontendService`.
**NOWE:**
- `Domain/Promotion/PromotionFrontendService.php`:
- `getActivePromotions()` (z `shop\Promotion`)
- `applyPromotions(array $basket)` (z `shop\Promotion::find_promotion()`)
- `private applyType01..05()` (z `front\factory\ShopPromotion`)
- `private isProductOnPromotion()` (z `shop\Product` — proste zapytanie)
- Testy: `PromotionFrontendServiceTest` (7 typów promocji + brak aktywnych)
**ZMIANA:**
- `shop/Promotion` → fasada do `PromotionFrontendService::applyPromotions()`
- `front/factory/ShopPromotion` → fasada
---
### ver. 0.282 — Product Frontend Service
**Cel:** Migracja `front\factory\ShopProduct` i statycznych metod `shop\Product` do Domain.
**NOWE:**
- `Domain/Product/ProductFrontendService.php`:
- Z `front\factory\ShopProduct`: `productDetails()` (~330 linii z Redis), `promotedProducts()`, `topProducts()`, `newProducts()`, `productUrl()`, `getMinimalPrice()`, `isProductActive()`, `getProductSku()`, `getProductEan()`, `productImage()`, `productWp()`, `randomProducts()`, `warehouseMessageZero()`, `warehouseMessageNonzero()`
- Z `shop\Product` (statyczne): `isProductOnPromotion()`, `getProductImg()`, `getProductUrl()`, `addVisit()`, `productSetsForBasket()`, `searchProductsByName()`, `searchProductByNameAjax()`
- Testy: `ProductFrontendServiceTest`
**ZMIANA:**
- `front/factory/ShopProduct` → fasada
- `shop/Product` statyczne metody → fasady do `ProductFrontendService`
**UWAGA:** Konstruktor `shop\Product`, `getFromCache()`, `calculate_basket_product_price()` — zostają na razie (instancyjne, używane w szablonach). Migracja w ver. 0.286.
---
### ver. 0.283 — Client Authentication (Security Fix)
**Cel:** Migracja `front\factory\ShopClient` + NAPRAWIENIE hardcoded password bypass.
**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**
**ZMIANA:**
- `front/factory/ShopClient` → fasada
- `front/controls/ShopClient` → deleguje do serwisu
---
### ver. 0.284 — Transport, Payment, Coupon Frontend Services
**Cel:** Frontend serwisy dla transportu, płatności i kuponów.
**NOWE:**
- `Domain/Transport/TransportFrontendService.php``transportMethods()` (filtrowanie WP, free delivery), `transportCost()`, `transportDetails()`
- `Domain/PaymentMethod/PaymentMethodFrontendService.php``activePaymentMethods()`, `paymentMethodsByTransport()`, `paymentMethodDetails()`
- `Domain/Coupon/CouponFrontendService.php``validateCoupon()`, `applyCoupon()`, `markCouponUsed()`
- Testy: 3 pliki testowe
**ZMIANA:**
- `front/factory/ShopTransport` → fasada
- `front/factory/ShopCoupon` → fasada
- `shop/Coupon` → fasada
- `shop/Transport` → fasada
---
### ver. 0.285 — Basket Service
**Cel:** Migracja logiki koszyka do `Domain\Basket\BasketService`.
**NOWE:**
- `Domain/Basket/BasketService.php`:
- `addProduct()`, `removeProduct()`, `changeQuantity()`, `increaseQuantity()`, `decreaseQuantity()`
- `summaryPrice()`, `countProducts()`, `countProductsText()`
- `validateStock()` (z `shop\Basket::check_product_quantity_in_stock()`)
- `calculateProductPrice()` (z `shop\Product::calculate_basket_product_price()` ~112 linii)
- Testy: `BasketServiceTest` (pricing z promocjami/kuponami, walidacja stanów)
**ZMIANA:**
- `front/factory/ShopBasket` → fasada
- `front/controls/ShopBasket` — wewnętrznie używa `BasketService`
- `shop/Basket` → fasada
- `shop/Product::calculate_basket_product_price()` → fasada
---
### ver. 0.286 — shop\Product Instance + Cache
**Cel:** Refaktoring konstruktora `shop\Product` i `getFromCache()`.
**NOWE:**
- `Domain/Product/ProductDataLoader.php`:
- `loadFull(int $productId, ?string $langId, ?string $permutationHash)` — ładowanie pełnych danych produktu
- `loadCached()` — Redis cache wrapper
- Testy: `ProductDataLoaderTest`
**ZMIANA:**
- `shop/Product` — konstruktor i `getFromCache()` delegują do `ProductDataLoader`
- ArrayAccess interface zachowany (szablony dalej używają `$product->property`)
---
### ver. 0.287 — Order Creation Frontend Service
**Cel:** Migracja `front\factory\ShopOrder::basket_save()` (~180 linii).
**NOWE:**
- `Domain/Order/OrderFrontendService.php`:
- `createOrder()` — tworzenie zamówienia z koszyka (walidacja, kalkulacja cen, insert, redukcja stanów, obsługa kuponu, wysyłka emaili, auto-status dla pobrania)
- `generateOrderNumber()` — format YYYY/MM/NNN
- `orderDetails()`, `orderIdByHash()`, `orderHashById()`
- Testy: `OrderFrontendServiceTest`
**ZMIANA:**
- `front/factory/ShopOrder` → fasada
---
### ver. 0.288 — Payment Webhook Service
**Cel:** Wyodrębnienie webhooków płatności z `front\controls\ShopOrder`.
**NOWE:**
- `Domain/Payment/PaymentWebhookService.php`:
- `processTpay(array $params)` — weryfikacja tPay
- `processPrzelewy24(array $params)` — weryfikacja przez API + walidacja kwoty
- `processHotpay(array $params)` — walidacja SHA256 hash
- `private markOrderPaid()` — wspólna logika (update status + email + Apilo sync)
- Testy: `PaymentWebhookServiceTest`
**ZMIANA:**
- `front/controls/ShopOrder` — webhooki stają się thin wrappers
**POPRAWA:** Zamiana `file_put_contents('tpay.txt')` na `\Log::save_log()`
---
### ver. 0.289 — shop\Order Instance + Apilo Service
**Cel:** Refaktoring `shop\Order` instancyjnych metod + wyodrębnienie integracji Apilo.
**NOWE:**
- `Domain/Integrations/ApiloService.php`:
- `syncPayment()`, `syncStatus()`, `processQueue()`
- `private queueSync()`, `private loadQueue()`, `private saveQueue()`
- `Domain/Order/OrderOperationsService.php`:
- `setAsPaid()`, `setAsUnpaid()`, `updateStatus()`, `sendStatusChangeEmail()`, `resendConfirmationEmail()`
- Testy: `ApiloServiceTest`, `OrderOperationsServiceTest`
**ZMIANA:**
- `shop/Order` instancyjne metody → fasady do `OrderOperationsService`
- Konstruktor i ArrayAccess bez zmian
---
### ver. 0.290 — Frontend App + Controllers (DI layer)
**Cel:** Stworzenie `front\App` (wzorowanego na `admin\App`) z mapą kontrolerów i DI.
**NOWE:**
- `autoload/front/App.php``render()`, `handleAjax()`, `getControllerFactories()` (DI wiring)
- `autoload/front/Controllers/ShopBasketController.php` — DI z `BasketService`, `ProductFrontendService`
- `autoload/front/Controllers/ShopOrderController.php` — DI z `OrderFrontendService`, `PaymentWebhookService`
- `autoload/front/Controllers/ShopClientController.php` — DI z `ClientFrontendService`
- Testy: 3 pliki testowe kontrolerów
**ZMIANA:**
- `front/controls/ShopBasket`, `ShopOrder`, `ShopClient` → delegują do nowych kontrolerów
---
### ver. 0.291 — Site Layout Engine
**Cel:** Refaktoring `front\view\Site::show()` (~600 linii) na testowalny `LayoutEngine`.
**NOWE:**
- `autoload/front/View/LayoutEngine.php`:
- `resolveLayout(array $page, array $params)` — wybór layoutu (product > category > article > page > default)
- `processPatterns(string $html, array $context)` — zamiana tagów szablonowych
- Osobne prywatne metody: `replaceCategories()`, `replaceProducts()`, `replaceMenus()`, `replaceLanguageTags()`, `replaceBanners()`, `replaceArticles()`, `replaceContainers()`, `replaceMetaTags()`, etc.
- Testy: `LayoutEngineTest`
**ZMIANA:**
- `front/view/Site::show()` → thin wrapper na `LayoutEngine`
---
### ver. 0.292 — Entry Point Unification
**Cel:** Ujednolicenie bootstrapu `index.php` i `ajax.php`.
**NOWE:**
- `autoload/front/Bootstrap.php``init()` (autoloader, config, sesja, DB, lang/settings)
- `autoload/front/PostProcessor.php` — ekstrakcja DOM manipulacji z `index.php` (GTM, Facebook Pixel, WebP, lazy loading)
**ZMIANA:**
- `index.php` → uproszczony: `Bootstrap::init()``App::render()``PostProcessor::process()`
- `ajax.php` → uproszczony: `Bootstrap::init()``App::handleAjax()`
---
### ver. 0.293 — Legacy Cleanup
**Cel:** Finalne porządki.
**USUNIĘCIE:**
- Martwy kod `eval()` dla `[PHP]` bloków (jeśli nieużywany)
- `class.Cache.php` (legacy file-based) jeśli wszystkie użycia przeniesione na `CacheHandler`
- Pusta klasa `front\view\ShopTransport`
**ZMIANA:**
- Dodanie `@deprecated` do wszystkich metod-fasad w `front/factory/`, `front/controls/`, `shop/`
- PHPDoc do wszystkich nowych klas Domain z `@since`
- Aktualizacja `tests/bootstrap.php`
- BUG FIX: `shop\Search` — typo `shop\Produt``shop\Product`
- BUG FIX: `front\factory\Newsletter::newsletter_unsubscribe()` — poprawka SQL
---
## Podsumowanie
| Wersja | Zakres | Priorytet | Nowe klasy Domain | Testy |
|--------|--------|-----------|-------------------|-------|
| 0.278 | Settings + Languages | FUNDAMENT | 2 serwisy | 2 |
| 0.279 | Category Frontend | WYSOKI | 1 serwis | 1 |
| 0.280 | Banners/Menu/Pages/Articles/Layouts | ŚREDNI | 5 serwisów | 5 |
| 0.281 | Promotion Engine | KRYTYCZNY | 1 serwis | 1 |
| 0.282 | Product Frontend | KRYTYCZNY | 1 serwis | 1 |
| 0.283 | Client/Auth (security fix) | KRYTYCZNY | 1 serwis | 1 |
| 0.284 | Transport/Payment/Coupon | WYSOKI | 3 serwisy | 3 |
| 0.285 | Basket Service | WYSOKI | 1 serwis | 1 |
| 0.286 | Product Instance + Cache | ŚREDNI | 1 loader | 1 |
| 0.287 | Order Creation | WYSOKI | 1 serwis | 1 |
| 0.288 | Payment Webhooks | WYSOKI | 1 serwis | 1 |
| 0.289 | Order Instance + Apilo | ŚREDNI | 2 serwisy | 2 |
| 0.290 | Frontend App + Controllers | WYSOKI | App + 3 kontrolery | 3 |
| 0.291 | Layout Engine | ŚREDNI | 1 engine | 1 |
| 0.292 | Entry Point Unification | ŚREDNI | Bootstrap + PostProcessor | 1 |
| 0.293 | Legacy Cleanup | NISKI | — | — |
**Łącznie:** 16 wersji, ~24 nowe klasy Domain, ~25 plików testowych
### Wzorce do przestrzegania (z migracji admin)
- Konstruktor DI z `$db` (medoo)
- CacheHandler (Redis) dla cachingu — unifikacja (usunięcie starego `Cache`)
- Fasady w `shop\*` i `front\factory\*` dla kompatybilności wstecznej
- Testy PHPUnit z mockami medoo
- Namespace `\Domain\*``autoload/Domain/*/`
- Namespace `\front\Controllers\``autoload/front/Controllers/`
### Weryfikacja po każdej wersji
1. `composer test` (pełny suite PHPUnit)
2. Manualne sprawdzenie frontendu: strona główna, kategoria, produkt, koszyk, zamówienie
3. Sprawdzenie AJAX: dodawanie do koszyka, zmiana transportu, kupon
4. Sprawdzenie webhooków płatności (tPay, Przelewy24, Hotpay)