Files
shopPRO/REFACTORING_PLAN.md

349 lines
14 KiB
Markdown
Raw Blame History

# 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 # ✅ Zmigrowane (getQuantity, getPrice, getName, find, updateQuantity, archive, unarchive)
│ │ ├── ProductService.php # Logika biznesowa (przyszłość)
│ │ └── ProductCacheService.php # Cache produktu (przyszłość)
│ ├── Banner/
│ │ └── BannerRepository.php # ✅ Zmigrowane (find, delete, save)
│ ├── Settings/
│ │ └── SettingsRepository.php # ✅ Zmigrowane (saveSettings, getSettings) - fasada → factory
│ ├── Cache/
│ │ └── CacheRepository.php # ✅ Zmigrowane (clearCache)
│ ├── Order/
│ ├── Category/
│ └── ...
├── admin/ # Warstwa administratora (istniejący katalog!)
│ ├── Controllers/ # Nowe kontrolery - namespace \admin\Controllers\
│ │ ├── BannerController.php
│ │ └── SettingsController.php
│ ├── 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
- **Cache** (częściowo)
- ✅ CacheHandler - ma delete/deletePattern
- ✅ RedisConnection - singleton
- ✅ S::clear_product_cache() - nowa metoda
### 🔄 W trakcie
- **Product**
- ✅ get_product_quantity() - **ZMIGROWANE** (2025-02-05) 🎉
- Nowa klasa: `Domain\Product\ProductRepository::getQuantity()`
- Fasada w: `shop\Product::get_product_quantity()`
- Test: `tests/Unit/Domain/Product/ProductRepositoryTest.php`
- Testy: ✅ 5/5 przechodzą
- Aktualizacja: ver. 0.238
- Użycie DI: ✅ Konstruktor przyjmuje `$db`
- ✅ get_product_price() - **ZMIGROWANE** (2026-02-05) 🎉
- Nowa metoda: `Domain\Product\ProductRepository::getPrice()`
- Fasada w: `shop\Product::get_product_price()`
- Testy: ✅ 4 nowe testy (cena regularna, promocyjna, promo wyższa, nie znaleziono)
- Użycie: `front\factory\ShopPromotion` (linia 132)
- Aktualizacja: ver. 0.239
- ✅ get_product_name() - **ZMIGROWANE** (2026-02-05) 🎉
- Nowa metoda: `Domain\Product\ProductRepository::getName()`
- Fasada w: `shop\Product::get_product_name()`
- Testy: ✅ 2 nowe testy (nazwa znaleziona, nie znaleziona)
- Użycie: brak aktywnych wywołań (przygotowane na przyszłość)
- Aktualizacja: ver. 0.239
- ✅ archive() / unarchive() - **ZMIGROWANE** (2026-02-06) 🎉
- Nowe metody: `Domain\Product\ProductRepository::archive()`, `unarchive()`
- Nowy kontroler: `admin\Controllers\ProductArchiveController` (DI, instancyjny)
- Szablony: `admin/templates/product_archive/` (rename z `archive/`)
- Testy: ✅ 4 nowe testy repozytorium + 6 testów kontrolera
- FIX: SQL bug w `ajax_products_list_archive()` (puste wyszukiwanie + brak `archive = 1`)
- Aktualizacja: ver. 0.241
- [ ] is_product_on_promotion() - NASTĘPNA 👉
- **Banner** (DEMO pełnej migracji kontrolera)
- ✅ BannerRepository - **ZMIGROWANE** (2026-02-05) 🎉
- Nowa klasa: `Domain\Banner\BannerRepository` (find, delete, save, saveTranslations)
- Nowy kontroler: `admin\Controllers\BannerController` (DI, instancyjny)
- Router: `admin\Site::route()` → sprawdza nowy kontroler → fallback na stary
- Testy: ✅ 4 testy (find z tłumaczeniami, not found, delete, save)
- Stary kontroler `admin\controls\Banners` działa jako niezależny fallback
- Stara factory `admin\factory\Banners` zachowana bez zmian (fallback)
- Aktualizacja: ver. 0.239
- **Articles** (migracja kontrolera - etap edit/details)
- ✅ ArticleRepository::find() - **ZMIGROWANE** (2026-02-06) 🎉
- Nowa klasa: `Domain\Article\ArticleRepository` (find: artykul + relacje)
- Nowy kontroler: `admin\Controllers\ArticlesController` (DI, instancyjny)
- Zmigrowana akcja: `article_edit` -> `edit` (mapowanie w `admin\Site::$actionMap`)
- Kompatybilność: `admin\factory\Articles::article_details()` deleguje do nowego repozytorium
- Legacy cleanup: metody przejęte przez nowe kontrolery oznaczone `@deprecated` w `admin\controls\Articles|Banners|Settings`
- Testy repozytorium rozszerzone o czyszczenie nieprzypisanych plik<69>w/zdj<64><6A>
- Aktualizacja: ver. 0.243
- ✅ ArticleRepository::save() - **ZMIGROWANE** (2026-02-06) 🎉
- Metoda `save()` z prywatnych helperow (buildArticleRow, buildLangRow, saveTranslations, savePages, assignTempFiles, assignTempImages, deleteMarkedFiles, deleteMarkedImages)
- Zmigrowana akcja: `article_save` -> `save` (mapowanie w `admin\Site::$actionMap`)
- Kompatybilnosc: `admin\factory\Articles::article_save()` deleguje do repozytorium
- Testy: 7 nowych testow save (create, update, translations, pages, marked delete)
- Aktualizacja: ver. 0.244
- ✅ ArticleRepository::archive() - **ZMIGROWANE** (2026-02-06) 🎉
- Metoda `archive()` (ustawia status = -1)
- Zmigrowana akcja: `article_delete` -> `delete` (mapowanie w `admin\Site::$actionMap`)
- Kompatybilnosc: `admin\factory\Articles::articles_set_archive()` deleguje do repozytorium
- Testy: 2 nowe testy archive (success, failure)
- Aktualizacja: ver. 0.245
- ✅ ArticlesController::browseList() - **ZMIGROWANE** (2026-02-07) 🎉
- Nowa metoda kontrolera: `browseList()` (DI, instancyjna)
- Zmigrowana akcja: `browse_list` -> `browseList` (mapowanie w `admin\Site::$actionMap`)
- Legacy cleanup: usuniety `autoload/admin/controls/class.Articles.php` (brak fallback dla modułu Articles)
- Testy: 2 nowe testy kontraktu kontrolera (method exists + return type)
- ✅ ArticlesController::galleryOrderSave() - **ZMIGROWANE** (2026-02-07) 🎉
- Nowa metoda kontrolera: `galleryOrderSave()` (AJAX)
- Zmigrowana akcja: `gallery_order_save` -> `galleryOrderSave` (mapowanie w `admin\Site::$actionMap`)
- Implementacja: używa `Domain\Article\ArticleRepository::saveGalleryOrder()`
- Testy: 2 nowe testy kontraktu kontrolera (method exists + return type)
- ✅ Usuniecie legacy kontrolera Articles - **ZMIGROWANE** (2026-02-07) 🎉
- Usuniety plik: `autoload/admin/controls/class.Articles.php`
- Wymaganie dla aktualizacji: dodac wpis do `ver_X.XXX_files.txt`
- Wpis do usuniecia: `F: ../autoload/admin/controls/class.Articles.php`
- ✅ Stabilizacja generatora `.htaccess` - **ZMIGROWANE** (2026-02-07) 🎉
- FIX: regula admin ma `QSA` (query string dla sortowania/filtrow)
- FIX: `\S::htacces()` komentuje `AddHandler|SetHandler|ForceType` dla zgodnosci z hostingiem
- UPDATE: `libraries/htaccess.conf` zaktualizowany, aby poprawki nie znikaly po regeneracji
- **Settings** (migracja kontrolera - krok pośredni)
- ✅ SettingsRepository - **ZMIGROWANE** (2026-02-05) 🎉
- Nowa klasa: `Domain\Settings\SettingsRepository` (saveSettings, getSettings)
- Krok pośredni: fasada nad `admin\factory\Settings` (docelowo DI z $db)
- Nowy kontroler: `admin\Controllers\SettingsController` (DI, instancyjny)
- Testy: ✅ 3 testy (instancja, metody)
- Stary kontroler `admin\controls\Settings` zachowany jako fallback
- Aktualizacja: ver. 0.240
- ✅ CacheRepository - **ZMIGROWANE** (2026-02-05) 🎉
- Nowa klasa: `Domain\Cache\CacheRepository` (clearCache)
- Używa `\S::delete_dir()` + `\RedisConnection`
- Testy: ✅ 4 testy (z Redis, bez Redis, niedostępny, struktura)
- Aktualizacja: ver. 0.240
### 📋 Do zrobienia
- Order
- Category
- ShopAttribute
- ShopProduct (factory)
## Testowanie
### Framework: PHPUnit
Instalacja:
```bash
composer require --dev phpunit/phpunit
```
### Struktura testów
```
tests/
├── Unit/
│ ├── Domain/
│ │ ├── Product/ProductRepositoryTest.php # 15 testów
│ │ ├── Banner/BannerRepositoryTest.php # 4 testy
│ │ ├── Settings/SettingsRepositoryTest.php # 3 testy
│ │ └── Cache/CacheRepositoryTest.php # 4 testy
│ └── admin/
│ └── Controllers/
│ ├── SettingsControllerTest.php # 7 testów
│ └── ProductArchiveControllerTest.php # 6 testów
└── Integration/
```
**Łącznie: 48 testów, 91 asercji**
### Przykład testu
```php
// tests/Unit/Domain/Product/ProductRepositoryTest.php
use PHPUnit\Framework\TestCase;
use Domain\Product\ProductRepository;
class ProductRepositoryTest extends TestCase {
public function testGetQuantityReturnsCorrectValue() {
// Arrange
$mockDb = $this->createMock(\medoo::class);
$mockDb->method('get')->willReturn(10);
$repo = new ProductRepository($mockDb);
// Act
$quantity = $repo->getQuantity(123);
// Assert
$this->assertEquals(10, $quantity);
}
}
```
## 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
```
## Kolejność refaktoryzacji (priorytet)
1. **Cache**
2. **Product** (w trakcie)
- ✅ getQuantity (ver. 0.238)
- ✅ getPrice (ver. 0.239)
- ✅ getName (ver. 0.239)
- ✅ archive / unarchive (ver. 0.241)
- [ ] is_product_on_promotion - NASTĘPNA 👉
- [ ] getFromCache
- [ ] getProductImg
3. **Banner** ✅ (pełna migracja kontrolera, ver. 0.239)
4. **Settings** ✅ (migracja kontrolera - krok pośredni, ver. 0.240)
5. **ProductArchive** ✅ (migracja kontrolera + cleanup szablonów, ver. 0.241)
6. **Order**
5. **Category**
6. **ShopAttribute**
---
*Rozpoczęto: 2025-02-05*
*Ostatnia aktualizacja: 2026-02-07*