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>
12 KiB
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()doDomain\Transport\TransportRepository(odpowiedniktransport_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ąć
ProductSetRepositorydoShopProductController(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): ?arrayjeś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.phptemplates/shop-search/product-search.phptemplates/shop-basket/alert-product-sets.phptemplates/controls/alert-product-sets.php
Plan:
- Przenieść
shortPrice()iisWholeNumber()doShared\Helpers\Helpersjako 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()dofront\Views\ShopSearch(nowa klasa View, statyczna) - Przenieść
search_results()isearch_products()dofront\Controllers\ShopSearchControllerlub 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 sesjicheck_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): ?arraydoDomain\Product\ProductRepository(lub nowyProductCustomFieldRepository) - 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): arraydoCategoryRepository - 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()igetAttributeValueById()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()doPromotionRepository(z Redis cache) - Przenieść
find_promotion()doPromotionRepository— orkiestracja logiki applyType* - Przenieść stałe
$condition_type/$discount_typejako stałe klasowe doPromotionRepository - Zamienić wywołania w
ShopBasketControlleriShopPromotionController - 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:
OrderAdminServicema 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 wchangeStatus())
Plan:
- Sprawdzić, czy
OrderAdminServiceobsługuje już Apilo sync (metodasendOrderToApilo()istnieje) - Przenieść
process_apilo_sync_queue()+ kolejkę Apilo doDomain\Integrations\ApiloService(lub doIntegrationsRepository) - Przenieść
send_status_change_email()doOrderAdminService(jeśli nie jest częściąchangeStatus()) - Usunąć referencję do
new \shop\Coupon()— użyćCouponRepository(z fazy 1.3) - Zaktualizować
cron.php(jeśli wołaprocess_apilo_sync_queue) - 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ść doProductRepository::findCached()getDefaultCombinationPrices()→ProductRepositorygenerateSubtitleFromAttributes()→ProductRepositorygetProductDataBySelectedAttributes()→ProductRepositoryis_product_on_promotion()→ProductRepositoryproduct_sets_when_add_to_basket()→ProductSetRepositoryadd_visit()→ProductRepositorygetProductImg()/getProductUrl()→ProductRepositorysearchProductsByName()/searchProductByNameAjax()/searchProductsByNameCount()→ProductRepositoryis_stock_0_buy()→ProductRepositoryget_product_permutation_quantity_options()→ProductRepositorycalculate_basket_product_price()→Domain\Basket\BasketCalculatorget_product_id_by_attributes()/get_product_permutation_hash()/get_product_attributes()→ProductRepositorylubAttributeRepositoryarray_cartesian()/permutations()→ProductRepository(helper prywatny)generate_sku_code()→ProductRepositoryproduct_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:
- Etapami przenosić metody statyczne do
ProductRepository/AttributeRepository - Przenieść
calculate_basket_product_price()doBasketCalculator - Zamienić
Product::getFromCache()→ProductRepository::findCached()(zwraca tablicę) - Zaktualizować szablony i kontrolery
- 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:
- Uruchomić
./test.ps1— wszystkie 610+ testów muszą przechodzić - Grep po
\shop\w całym codebase — upewnić się, że usunięta klasa nie jest nigdzie używana - Po zakończeniu wszystkich faz:
grep -r "\\\\shop\\\\" autoload/ templates/ admin/ index.php ajax.php cron.php api.phppowinien 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 |