12 KiB
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 toadmin/(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
// ❌ 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
// 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ć
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 | CacheHandler, RedisConnection, clear_product_cache |
| 2 | Product | 0.238-0.252, 0.274 | getQuantity, getPrice, getName, archive/unarchive, allProductsForMassEdit, getProductsByCategory, applyDiscountPercent |
| 3 | Banner | 0.239 | find, delete, save, kontroler DI |
| 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 |
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)
- is_product_on_promotion
- getFromCache
- getProductImg
📋 Do zrobienia
- ShopProduct (factory)
Kolejność refaktoryzacji (priorytet)
1-28: ✅ Cache, Product, Banner, Settings, Dictionaries, ProductArchive, Filemanager, Users, Pages, Integrations, ShopPromotion, ShopCoupon, ShopStatuses, ShopPaymentMethod, ShopTransport, ShopAttribute, ShopProductSets, ShopProducer, ShopProduct (mass_edit), ShopClients, ShopCategory, ShopOrder
Nastepne: 29. ShopProduct (factory)
Form Edit System
Nowy uniwersalny system formularzy edycji:
- ✅ Klasy ViewModel:
FormFieldType,FormField,FormTab,FormAction,FormEditViewModel - ✅ Walidacja:
FormValidatorz 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
- Single Responsibility - jedna klasa = jedna odpowiedzialność
- Open/Closed - otwarty na rozszerzenia, zamknięty na modyfikacje
- Liskov Substitution - podklasy mogą zastąpić nadklasy
- Interface Segregation - wiele małych interfejsów
- Dependency 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
// ✅ 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:
autoload/{namespace}/class.{ClassName}.php(legacy)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
composer require --dev phpstan/phpstan
vendor/bin/phpstan analyse autoload/Domain
Testowanie
Framework: PHPUnit
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: 338 testow, 1063 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-15
Changelog zmian: docs/CHANGELOG.md