Files
shopPRO/docs/LEGACY_SHOP_REFACTORING_PLAN.md
Jacek Pyziak e1cb421aaf 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>
2026-02-18 02:05:39 +01:00

235 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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** | |