ver. 0.294: Remove all 12 legacy autoload/shop/ classes (~2363 lines)

Complete Domain-Driven Architecture migration:
- Phase 1-4: Transport, ProductSet, Coupon, Shop, Search, Basket,
  ProductCustomField, Category, ProductAttribute, Promotion
- Phase 5: Order (~562 lines) + Product (~952 lines)
- ~20 Product methods migrated to ProductRepository
- Apilo sync migrated to OrderAdminService
- Production hotfixes: stale Redis cache (prices 0.00), unqualified
  Product:: refs in LayoutEngine, object->array template conversion
- AttributeRepository::getAttributeValueById() Redis cache added

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-18 02:05:39 +01:00
parent c72ba388a0
commit e1cb421aaf
74 changed files with 2176 additions and 2669 deletions

View File

@@ -0,0 +1,234 @@
# Plan refaktoryzacji `autoload/shop/` — usunięcie legacy klas
## Kontekst
Katalog `autoload/shop/` zawierał **12 legacy klas** (~2 363 linii kodu), które pełniły rolę fasad/wrapperów nad warstwą `Domain\`. Wszystkie klasy zostały usunięte — logika przeniesiona do warstwy `Domain\` / `Shared\`, wywołania zaktualizowane.
**Status: UKOŃCZONE** (wszystkie 12 klas usunięte, katalog `autoload/shop/` pusty)
## Status
| Faza | Klasy | Status |
|------|-------|--------|
| 1 | Transport, ProductSet, Coupon | ✅ Ukończona |
| 2 | Shop, Search, Basket | ✅ Ukończona |
| 3 | ProductCustomField, Category, ProductAttribute | ✅ Ukończona |
| 4 | Promotion | ✅ Ukończona |
| 5 | Order, Product | ✅ Ukończona |
---
## Faza 1 — Trywialne fasady (0 logiki do migracji)
### 1.1 `class.Transport.php` ✅ (~31 linii)
**Jedyne zewnętrzne użycie:**
- `admin\Controllers\ShopOrderController.php``\shop\Transport::transport_list()`
**Plan:**
- Dodać metodę `allActiveOrdered()` do `Domain\Transport\TransportRepository` (odpowiednik `transport_list()`)
- Zamienić wywołanie w `ShopOrderController`
- Usunąć `class.Transport.php`
### 1.2 `class.ProductSet.php` ✅ (~49 linii)
**Jedyne zewnętrzne użycie:**
- `admin\Controllers\ShopProductController.php:224``\shop\ProductSet::sets_list()`
**Plan:**
- `ProductSetRepository::allSets()` już istnieje — zamienić wywołanie na `$this->productSetRepo->allSets()`
- Wstrzyknąć `ProductSetRepository` do `ShopProductController` (jeśli jeszcze nie jest)
- Usunąć `class.ProductSet.php`
### 1.3 `class.Coupon.php` ✅ (~84 linii)
**Zewnętrzne użycia (3×):**
- `shop\class.Order.php:171``new \shop\Coupon($id)` (do przeniesienia w fazie Order)
- `front\Controllers\ShopOrderController.php:139``new \shop\Coupon($id)`
- `admin\Controllers\ShopOrderController.php:170``new \shop\Coupon($id)`
**Plan:**
- `CouponRepository::findByName()` / `find()` już istnieje — dodać `findById(int $id): ?array` jeśli brak
- Zamienić `new \shop\Coupon($id)``$this->couponRepo->findById($id)` w obu kontrolerach
- Użycie w `class.Order.php` — rozwiąże się w fazie Order
- Usunąć `class.Coupon.php`
---
## Faza 2 — Proste wrappery (minimalna logika do przeniesienia)
### 2.1 `class.Shop.php` ✅ (~39 linii) — utility cenowe
**Zewnętrzne użycia (~24× w szablonach):**
- `templates/shop-product/product.php`, `product-mini.php`
- `templates/shop-search/product-search.php`
- `templates/shop-basket/alert-product-sets.php`
- `templates/controls/alert-product-sets.php`
**Plan:**
- Przenieść `shortPrice()` i `isWholeNumber()` do `Shared\Helpers\Helpers` jako metody statyczne
- Zamienić `\shop\Shop::shortPrice()``\Shared\Helpers\Helpers::shortPrice()` we wszystkich szablonach (replace_all)
- Usunąć `class.Shop.php`
### 2.2 `class.Search.php` ✅ (~70 linii)
**Jedyne zewnętrzne użycie:**
- `front\LayoutEngine.php:337``\shop\Search::simple_form()`
**Plan:**
- Przenieść `simple_form()` do `front\Views\ShopSearch` (nowa klasa View, statyczna)
- Przenieść `search_results()` i `search_products()` do `front\Controllers\ShopSearchController` lub odpowiedniej istniejącej lokalizacji
- Zamienić wywołanie w `LayoutEngine`
- Usunąć `class.Search.php`
- Poprawić bug: `use shop\Produt;` (literówka — brakuje 'c')
### 2.3 `class.Basket.php` ✅ (~92 linii)
**Zewnętrzne użycia (6× w `ShopBasketController`):**
- `validate_basket()` — prosta walidacja tablicy sesji
- `check_product_quantity_in_stock()` — sprawdzenie stanów magazynowych
**Plan:**
- Przenieść obie metody do `Domain\Basket\BasketCalculator` (już istnieje)
- Zamienić wywołania w `ShopBasketController`
- Usunąć `class.Basket.php`
---
## Faza 3 — Klasy z logiką cache/atrybutów
### 3.1 `class.ProductCustomField.php` ✅ (~74 linii)
**Zewnętrzne użycia (2×):**
- `Domain\Order\OrderRepository.php:643``\shop\ProductCustomField::getFromCache()`
- `templates/shop-basket/_partials/product-custom-fields.php:3``\shop\ProductCustomField::getFromCache()`
**Plan:**
- Dodać `findCached(int $id): ?array` do `Domain\Product\ProductRepository` (lub nowy `ProductCustomFieldRepository`)
- Zamienić wywołania w OrderRepository i szablonie
- Usunąć `class.ProductCustomField.php`
### 3.2 `class.Category.php` ✅ (~84 linii)
**Zewnętrzne użycia (2×):**
- `index.php:225``\shop\Category::get_category_products_id()` (FB Pixel)
- `templates/shop-category/category.php:19``\shop\Category::get_subcategory_by_category()`
**Plan:**
- `CategoryRepository::productsId()` prawdopodobnie istnieje — sprawdzić i użyć lub dodać
- Dodać `subcategoriesCached(int $categoryId): array` do `CategoryRepository`
- Zamienić wywołania
- Usunąć `class.Category.php`
### 3.3 `class.ProductAttribute.php` ✅ (~119 linii)
**Zewnętrzne użycia (3 lokalizacje):**
- `shop\class.Product.php` — użycia wewnętrzne (rozwiążą się w fazie Product)
- `admin/templates/shop-product/product-combination.php:32``getAttributeName()`, `get_value_name()`
- `templates/shop-product/_partial/product-attribute.php:13``get_value_name()`
**Plan:**
- `AttributeRepository::getAttributeNameById()` i `getAttributeValueById()` już istnieją
- Dodać brakujące metody do `AttributeRepository`: `isValueDefault()`, `getAttributeOrder()`, `getAttributeNameByValue()` (z Redis cache)
- Zamienić wywołania w szablonach
- Usunąć `class.ProductAttribute.php`
---
## Faza 4 — Klasy z logiką promocji
### 4.1 `class.Promotion.php` ✅ (~107 linii)
**Zewnętrzne użycia (3 lokalizacje):**
- `front\Controllers\ShopBasketController.php` (6×) → `\shop\Promotion::find_promotion()`
- `admin\Controllers\ShopPromotionController.php``::$condition_type`, `::$discount_type`
**Plan:**
- Przenieść `get_active_promotions()` do `PromotionRepository` (z Redis cache)
- Przenieść `find_promotion()` do `PromotionRepository` — orkiestracja logiki applyType*
- Przenieść stałe `$condition_type` / `$discount_type` jako stałe klasowe do `PromotionRepository`
- Zamienić wywołania w `ShopBasketController` i `ShopPromotionController`
- Usunąć `class.Promotion.php`
---
## Faza 5 — Duże klasy z wieloma zależnościami
### 5.1 `class.Order.php` ✅ (~562 linii)
**Główna logika do migracji:**
- `OrderAdminService` ma już: `setOrderAsPaid()`, `setOrderAsUnpaid()`, `changeStatus()`, `resendConfirmationEmail()`, `saveOrderByAdmin()`, `deleteOrder()`, `saveNotes()`
- **Brakuje:** logika Apilo sync (`process_apilo_sync_queue()`, `sync_apilo_payment()`, `sync_apilo_status()`, queue management)
- **Brakuje:** `send_status_change_email()` (ale może być zintegrowane w `changeStatus()`)
**Plan:**
1. Sprawdzić, czy `OrderAdminService` obsługuje już Apilo sync (metoda `sendOrderToApilo()` istnieje)
2. Przenieść `process_apilo_sync_queue()` + kolejkę Apilo do `Domain\Integrations\ApiloService` (lub do `IntegrationsRepository`)
3. Przenieść `send_status_change_email()` do `OrderAdminService` (jeśli nie jest częścią `changeStatus()`)
4. Usunąć referencję do `new \shop\Coupon()` — użyć `CouponRepository` (z fazy 1.3)
5. Zaktualizować `cron.php` (jeśli woła `process_apilo_sync_queue`)
6. Usunąć `class.Order.php`
### 5.2 `class.Product.php` ✅ (~952 linii) — NAJWIĘKSZA KLASA
**Stan:** Większość logiki zmigowana do `ProductRepository`, ale klasa pełni rolę entity z ArrayAccess (używana w szablonach jako `$product['name']`).
**Metody do przeniesienia:**
- `getFromCache()` — cache wrappera entity → przenieść do `ProductRepository::findCached()`
- `getDefaultCombinationPrices()``ProductRepository`
- `generateSubtitleFromAttributes()``ProductRepository`
- `getProductDataBySelectedAttributes()``ProductRepository`
- `is_product_on_promotion()``ProductRepository`
- `product_sets_when_add_to_basket()``ProductSetRepository`
- `add_visit()``ProductRepository`
- `getProductImg()` / `getProductUrl()``ProductRepository`
- `searchProductsByName()` / `searchProductByNameAjax()` / `searchProductsByNameCount()``ProductRepository`
- `is_stock_0_buy()``ProductRepository`
- `get_product_permutation_quantity_options()``ProductRepository`
- `calculate_basket_product_price()``Domain\Basket\BasketCalculator`
- `get_product_id_by_attributes()` / `get_product_permutation_hash()` / `get_product_attributes()``ProductRepository` lub `AttributeRepository`
- `array_cartesian()` / `permutations()``ProductRepository` (helper prywatny)
- `generate_sku_code()``ProductRepository`
- `product_meta()``ProductRepository`
**Kluczowy problem:** szablony używają `$product['field']` przez ArrayAccess. Po migracji szablony będą dostawać zwykłe `array` z `ProductRepository` — to jest kompatybilne, bo `$product['field']` działa tak samo na tablicy.
**Plan:**
1. Etapami przenosić metody statyczne do `ProductRepository` / `AttributeRepository`
2. Przenieść `calculate_basket_product_price()` do `BasketCalculator`
3. Zamienić `Product::getFromCache()``ProductRepository::findCached()` (zwraca tablicę)
4. Zaktualizować szablony i kontrolery
5. Usunąć `class.Product.php`
---
## Pliki do modyfikacji (podsumowanie)
| Plik | Zmiany |
|------|--------|
| `autoload/Domain/Transport/TransportRepository.php` | + `allActiveOrdered()` |
| `autoload/Domain/Basket/BasketCalculator.php` | + `validateBasket()`, `checkStock()`, `calculateProductPrice()` |
| `autoload/Domain/Promotion/PromotionRepository.php` | + `getActivePromotions()`, `findPromotion()`, stałe |
| `autoload/Domain/Product/ProductRepository.php` | + ~15 metod z class.Product.php |
| `autoload/Domain/Attribute/AttributeRepository.php` | + `isValueDefault()`, `getAttributeOrder()`, `getAttributeNameByValue()` |
| `autoload/Domain/Category/CategoryRepository.php` | + `subcategoriesCached()`, `categoryProductIds()` |
| `autoload/Domain/Order/OrderAdminService.php` | + email statusu, sprawdzenie Apilo |
| `autoload/Domain/Integrations/` | + `ApiloSyncService` (queue Apilo) |
| `autoload/Domain/Coupon/CouponRepository.php` | + `findById()` |
| `autoload/Shared/Helpers/Helpers.php` | + `shortPrice()`, `isWholeNumber()` |
| `autoload/front/Views/ShopSearch.php` | NOWY — `simple_form()` |
| `autoload/front/LayoutEngine.php` | zamiana `\shop\Search``\front\Views\ShopSearch` |
| `autoload/front/Controllers/ShopBasketController.php` | zamiana `\shop\Basket`, `\shop\Promotion` |
| `autoload/admin/Controllers/ShopOrderController.php` | zamiana `\shop\Coupon`, `\shop\Transport` |
| `autoload/admin/Controllers/ShopProductController.php` | zamiana `\shop\ProductSet` |
| `autoload/admin/Controllers/ShopPromotionController.php` | zamiana `::$condition_type`, `::$discount_type` |
| `autoload/front/Controllers/ShopOrderController.php` | zamiana `\shop\Coupon` |
| Szablony (`templates/`) | zamiana `\shop\Shop::shortPrice()`, `\shop\ProductAttribute::*`, `\shop\Category::*` |
| `index.php` | zamiana `\shop\Category::get_category_products_id()` |
| `autoload/Domain/Order/OrderRepository.php` | zamiana `\shop\ProductCustomField` |
## Weryfikacja
Po każdej fazie:
1. Uruchomić `./test.ps1` — wszystkie 610+ testów muszą przechodzić
2. Grep po `\shop\` w całym codebase — upewnić się, że usunięta klasa nie jest nigdzie używana
3. Po zakończeniu wszystkich faz: `grep -r "\\\\shop\\\\" autoload/ templates/ admin/ index.php ajax.php cron.php api.php` powinien zwracać 0 wyników
## Szacunek złożoności
| Faza | Klasy | Linii do usunięcia | Złożoność |
|------|-------|---------------------|-----------|
| 1 | Transport, ProductSet, Coupon | ~164 | Niska |
| 2 | Shop, Search, Basket | ~201 | Niska-średnia |
| 3 | ProductCustomField, Category, ProductAttribute | ~277 | Średnia |
| 4 | Promotion | ~107 | Średnia |
| 5 | Order, Product | ~1 514 | Wysoka |
| **Razem** | **12 klas** | **~2 363** | |