# Plan Refaktoryzacji shopPRO - Domain-Driven Architecture ## Cel Stopniowe przeniesienie logiki biznesowej do architektury warstwowej: - **Domain/** - logika biznesowa (core) - **Admin/** - warstwa administratora - **Frontend/** - warstwa użytkownika - **Shared/** - współdzielone narzędzia ## Docelowa struktura ``` autoload/ ├── Domain/ # Logika biznesowa (CORE) - namespace \Domain\ │ ├── Product/ │ │ ├── ProductRepository.php │ │ ├── ProductService.php # (przyszłość) │ │ └── ProductCacheService.php # (przyszłość) │ ├── Banner/ │ │ └── BannerRepository.php │ ├── Settings/ │ │ └── SettingsRepository.php │ ├── Cache/ │ │ └── CacheRepository.php │ ├── Order/ │ ├── Category/ │ └── ... │ ├── admin/ # Warstwa administratora (istniejący katalog!) │ ├── Controllers/ # Nowe kontrolery - namespace \admin\Controllers\ │ ├── controls/ # Stare kontrolery (legacy fallback) │ ├── factory/ # Stare helpery (legacy) │ └── view/ # Widoki (statyczne - OK bez zmian) │ ├── Frontend/ # Warstwa użytkownika (przyszłość) │ ├── Controllers/ │ └── Services/ │ ├── Shared/ # Współdzielone narzędzia │ ├── Cache/ │ │ ├── CacheHandler.php │ │ └── RedisConnection.php │ └── Helpers/ │ └── S.php │ └── [LEGACY] # Stare klasy (stopniowo deprecated) ├── shop/ ├── admin/factory/ └── front/factory/ ``` ### WAŻNE: Konwencja namespace → katalog (Linux case-sensitive!) - `\Domain\` → `autoload/Domain/` (duże D - nowy katalog) - `\admin\Controllers\` → `autoload/admin/Controllers/` (małe a - istniejący katalog) - NIE używać `\Admin\` (duże A) bo na serwerze Linux katalog to `admin/` (małe a) ## Zasady migracji ### 1. Stopniowość - Przenosimy **jedną funkcję na raz** - Zachowujemy kompatybilność wsteczną - Stare klasy działają jako fasady do nowych ### 2. Dependency Injection zamiast statycznych metod ```php // ❌ STARE - statyczne class Product { public static function getQuantity($id) { global $mdb; return $mdb->get('pp_shop_products', 'quantity', ['id' => $id]); } } // ✅ NOWE - instancje z DI class ProductRepository { private $db; public function __construct($db) { $this->db = $db; } public function getQuantity($id) { return $this->db->get('pp_shop_products', 'quantity', ['id' => $id]); } } ``` ### 3. Fasady dla kompatybilności ```php // Stara klasa wywołuje nową namespace shop; class Product { public static function getQuantity($id) { global $mdb; $repo = new \Domain\Product\ProductRepository($mdb); return $repo->getQuantity($id); } } ``` ## Proces migracji funkcji ### Krok 1: Wybór funkcji - Wybierz prostą funkcję statyczną - Sprawdź jej zależności - Przeanalizuj gdzie jest używana ### Krok 2: Stworzenie nowej struktury - Utwórz folder `Domain/{Module}/` - Stwórz odpowiednią klasę (Repository/Service/Entity) - Przenieś logikę ### Krok 3: Znalezienie użyć ```bash grep -r "Product::getQuantity" . ``` ### Krok 4: Aktualizacja wywołań - Opcja A: Bezpośrednie wywołanie nowej klasy - Opcja B: Fasada w starej klasie (zalecane na początek) ### Krok 5: Testy - Napisz test jednostkowy dla nowej funkcji - Sprawdź czy stare wywołania działają ## Status migracji ### ✅ Zmigrowane moduły | # | Modul | Wersja | Zakres | |---|-------|--------|--------| | 1 | Cache | 0.237, 0.282 | CacheHandler, RedisConnection, clear_product_cache, Shared\Cache namespace, eliminacja class.Cache.php | | 2 | Product | 0.238-0.252, 0.274, 0.277 | getQuantity, getPrice, getName, archive/unarchive, allProductsForMassEdit, getProductsByCategory, applyDiscountPercent, pelna migracja factory (CRUD, save, delete, duplicate, kombinacje, zdjecia/pliki, Google Feed XML) | | 3 | Banner | 0.239, 0.281 | find, delete, save, kontroler DI, frontend: banners(), mainBanner() z Redis cache, usuniete fasady front\factory + front\view | | 4 | Settings | 0.240/0.250 | saveSettings, getSettings, kontroler DI | | 5 | Dictionaries | 0.251 | listForAdmin, find, save, delete, kontroler DI | | 6 | ProductArchive | 0.252 | kontroler DI, table-list | | 7 | Filemanager | 0.252 | kontroler DI, fix Invalid Key | | 8 | Users | 0.253 | CRUD, logon, 2FA, kontroler DI | | 9 | Languages | 0.254 | languages + translations, kontroler DI | | 10 | Layouts | 0.256 | find, save, delete, menusWithPages, categoriesTree | | 11 | Newsletter | 0.257-0.258 | subskrybenci, szablony, ustawienia | | 12 | Scontainers | 0.259 | listForAdmin, find, save, delete | | 13 | ArticlesArchive | 0.260 | restore, deletePermanently | | 14 | Articles | 0.261 | pelna migracja (CRUD, AJAX, galeria, pliki) | | 15 | Pages | 0.262 | menu/page CRUD, drzewo stron, AJAX | | 16 | Integrations | 0.263 | Apilo/ShopPRO, cleanup Sellasist/Baselinker | | 17 | ShopPromotion | 0.264-0.265 | listForAdmin, find, save, delete, categoriesTree | | 18 | ShopCoupon | 0.266 | listForAdmin, find, save, delete, categoriesTree | | 19 | ShopStatuses | 0.267 | listForAdmin, find, save, color picker | | 20 | ShopPaymentMethod | 0.268 | listForAdmin, find, save, allActive, mapowanie Apilo, DI kontroler | | 21 | ShopTransport | 0.269 | listForAdmin, find, save, allActive, allForAdmin, findActiveById, getTransportCost, lowestTransportPrice, getApiloCarrierAccountId, powiazanie z PaymentMethod, DI kontroler | | 22 | ShopAttribute | 0.271 | list/edit/save/delete/values, nowy edytor wartosci, cleanup legacy, przepiecie zaleznosci kombinacji | | 23 | ShopProductSets | 0.272 | listForAdmin, find, save, delete, allSets, allProductsMap, multi-select Selectize, DI kontroler | | 24 | ShopProducer | 0.273 | listForAdmin, find, save, delete, allProducers, producerProducts, fasada shop\Producer, DI kontroler | | 25 | ShopProduct (mass_edit) | 0.274 | DI kontroler + routing dla `mass_edit`, `mass_edit_save`, `get_products_by_category`, cleanup legacy akcji | | 26 | ShopClients | 0.274 | DI kontroler + routing dla `list/details`, nowe listy na `components/table-list`, cleanup legacy controls/factory | | 27 | ShopCategory | 0.275 | CategoryRepository + DI kontroler + routing, endpointy AJAX (`save_categories_order`, `save_products_order`, `cookie_categories`), cleanup legacy controls/factory/view | | 28 | ShopOrder | 0.276 | OrderRepository + OrderAdminService + DI kontroler + routing + nowe widoki (`orders-list`, `order-details`, `order-edit`) + cleanup legacy controls/factory/view-list | | 29 | ShopProduct (factory) | 0.277 | Pelna migracja factory: ProductRepository (CRUD, save, delete, duplicate, toggleStatus, updatePrice, kombinacje, zdjecia/pliki, Google Feed XML) + DI kontroler (list, edit, save, operacje, kombinacje, zdjecia/pliki) + routing + przepiecie zaleznosci (ProductArchive, order-details, cron, cron-xml, products-list-table, stock) + usunięcie legacy (controls, factory, ajax/shop.php) | | 30 | Dashboard | 0.277 | DashboardRepository (7 metod, Redis caching) + DashboardController (DI) + cleanup legacy controls/shop | | 31 | Update | 0.277 | UpdateRepository (update, runPendingMigrations, helper methods) + UpdateController (DI) + przepisany template (panele, $.confirm) + cleanup legacy controls/factory/view | | 32 | Legacy cleanup | 0.277 | Usunieto admin/factory/Articles (martwy kod), admin/view/Page → App::render(), puste foldery controls/factory/view | | 33 | admin\App | 0.277 | Rename Site → App, usunieto fallback na controls, uproszczony routing, plik App.php bez przedrostka class. | ### Product - szczegolowy status - ✅ getQuantity (ver. 0.238) - ✅ getPrice (ver. 0.239) - ✅ getName (ver. 0.239) - ✅ archive / unarchive (ver. 0.241/0.252) - ✅ allProductsForMassEdit (ver. 0.274) - ✅ getProductsByCategory (ver. 0.274) - ✅ applyDiscountPercent (ver. 0.274) - ✅ countProducts, listForAdmin, findForAdmin, allProductsList, productCategoriesText, getParentId (ver. 0.277) - ✅ saveProduct + helpery (ver. 0.277) - ✅ delete, duplicate, toggleStatus, updatePriceBrutto/Promo, updateCustomLabel (ver. 0.277) - ✅ getPermutations, generateCombinations, deleteCombination, countCombinations, saveCombination* (ver. 0.277) - ✅ deleteImage, updateImageAlt, saveImagesOrder, deleteFile, updateFileName, generateGoogleFeedXml, generateEAN (ver. 0.277) - ✅ updateCombinationPricesFromBase (ver. 0.277) - [ ] is_product_on_promotion (frontend — osobna migracja) - [ ] getFromCache (frontend — osobna migracja) - [ ] getProductImg (frontend — osobna migracja) ### 📋 Do zrobienia - Frontend: migracja `front\factory\ShopProduct` ## Kolejność refaktoryzacji (priorytet) 1-33: ✅ Cache, Product, Banner, Settings, Dictionaries, ProductArchive, Filemanager, Users, Pages, Integrations, ShopPromotion, ShopCoupon, ShopStatuses, ShopPaymentMethod, ShopTransport, ShopAttribute, ShopProductSets, ShopProducer, ShopProduct (mass_edit), ShopClients, ShopCategory, ShopOrder, ShopProduct (factory), Dashboard, Update, Legacy cleanup, admin\App 34: ✅ Shared\Cache namespace (ver. 0.282) — CacheHandler + RedisConnection → Shared\Cache\, eliminacja class.Cache.php, przepiecie 6 plikow na CacheHandler 35: ✅ Shared\Tpl namespace (ver. 0.285) — Tpl → Shared\Tpl\Tpl, eliminacja class.Tpl.php + curl.class.php, fix thumb.php ## Form Edit System Nowy uniwersalny system formularzy edycji: - ✅ Klasy ViewModel: `FormFieldType`, `FormField`, `FormTab`, `FormAction`, `FormEditViewModel` - ✅ Walidacja: `FormValidator` z obsługą reguł per pole i sekcje językowe - ✅ Persist: `FormRequestHandler` - zapamiętywanie danych przy błędzie walidacji - ✅ Renderer: `FormFieldRenderer` - renderowanie wszystkich typów pól - ✅ Szablon: `admin/templates/components/form-edit.php` - uniwersalny layout - Wspierane typy pól: text, number, email, password, date, datetime, switch, select, textarea, editor, image, file, hidden, lang_section, color - Obsługa zakładek (vertical) i sekcji językowych (horizontal) - **Do zrobienia**: Przerobić pozostałe kontrolery/formularze (Product, Category, Pages, itd.) Pelna dokumentacja: `docs/FORM_EDIT_SYSTEM.md` ## Zasady kodu ### 1. SOLID Principles - **S**ingle Responsibility - jedna klasa = jedna odpowiedzialność - **O**pen/Closed - otwarty na rozszerzenia, zamknięty na modyfikacje - **L**iskov Substitution - podklasy mogą zastąpić nadklasy - **I**nterface Segregation - wiele małych interfejsów - **D**ependency Inversion - zależności od abstrakcji ### 2. Nazewnictwo - **Entity** - `Product.php` (reprezentuje obiekt domenowy) - **Repository** - `ProductRepository.php` (dostęp do danych) - **Service** - `ProductService.php` (logika biznesowa) - **Controller** - `ProductController.php` (obsługa requestów) ### 3. Type Hinting ```php // ✅ DOBRE public function getQuantity(int $id): ?int { return $this->db->get('pp_shop_products', 'quantity', ['id' => $id]); } // ❌ ZŁE public function getQuantity($id) { return $this->db->get('pp_shop_products', 'quantity', ['id' => $id]); } ``` ## Narzędzia pomocnicze ### Autoloader (produkcja) Autoloader w 9 entry pointach obsługuje dwie konwencje: 1. `autoload/{namespace}/class.{ClassName}.php` (legacy) 2. `autoload/{namespace}/{ClassName}.php` (PSR-4, fallback) Entry pointy: `index.php`, `ajax.php`, `api.php`, `cron.php`, `cron-turstmate.php`, `download.php`, `admin/index.php`, `admin/ajax.php`, `cron/cron-xml.php` ### Static Analysis ```bash composer require --dev phpstan/phpstan vendor/bin/phpstan analyse autoload/Domain ``` ## Testowanie ### Framework: PHPUnit ```bash composer test ``` ### Struktura testów ``` tests/ ├── Unit/ │ ├── Domain/ │ │ ├── Article/ArticleRepositoryTest.php │ │ ├── Banner/BannerRepositoryTest.php │ │ ├── Cache/CacheRepositoryTest.php │ │ ├── Coupon/CouponRepositoryTest.php │ │ ├── Dictionaries/DictionariesRepositoryTest.php │ │ ├── Integrations/IntegrationsRepositoryTest.php │ │ ├── PaymentMethod/PaymentMethodRepositoryTest.php │ │ ├── Producer/ProducerRepositoryTest.php │ │ ├── Product/ProductRepositoryTest.php │ │ ├── ProductSet/ProductSetRepositoryTest.php │ │ ├── Promotion/PromotionRepositoryTest.php │ │ ├── Settings/SettingsRepositoryTest.php │ │ ├── ShopStatus/ShopStatusRepositoryTest.php │ │ └── User/UserRepositoryTest.php │ └── admin/ │ └── Controllers/ │ ├── ArticlesControllerTest.php │ ├── DictionariesControllerTest.php │ ├── IntegrationsControllerTest.php │ ├── ProductArchiveControllerTest.php │ ├── SettingsControllerTest.php │ ├── ShopCouponControllerTest.php │ ├── ShopPaymentMethodControllerTest.php │ ├── ShopProducerControllerTest.php │ ├── ShopProductSetsControllerTest.php │ ├── ShopPromotionControllerTest.php │ ├── ShopStatusesControllerTest.php │ └── UsersControllerTest.php └── Integration/ ``` **Lacznie: 454 testow, 1449 asercji** Aktualizacja 2026-02-15 (ver. 0.273): - dodano testy `tests/Unit/Domain/Producer/ProducerRepositoryTest.php` - dodano testy `tests/Unit/admin/Controllers/ShopProducerControllerTest.php` Aktualizacja 2026-02-14 (ver. 0.271): - dodano testy `tests/Unit/Domain/Attribute/AttributeRepositoryTest.php` - dodano testy `tests/Unit/admin/Controllers/ShopAttributeControllerTest.php` Pelna dokumentacja testow: `TESTING.md` --- *Rozpoczęto: 2025-02-05* *Ostatnia aktualizacja: 2026-02-16* *Changelog zmian: `docs/CHANGELOG.md`*