# 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** | |