diff --git a/.phpunit.result.cache b/.phpunit.result.cache index 270bb78..32f3e1b 100644 --- a/.phpunit.result.cache +++ b/.phpunit.result.cache @@ -1 +1 @@ -{"version":1,"defects":[],"times":{"Tests\\Unit\\Domain\\Product\\ProductRepositoryTest::testGetQuantityReturnsCorrectValue":0.001,"Tests\\Unit\\Domain\\Product\\ProductRepositoryTest::testGetQuantityReturnsNullWhenProductNotFound":0,"Tests\\Unit\\Domain\\Product\\ProductRepositoryTest::testFindReturnsProductData":0,"Tests\\Unit\\Domain\\Product\\ProductRepositoryTest::testUpdateQuantitySuccess":0,"Tests\\Unit\\Domain\\Product\\ProductRepositoryTest::testGetPriceReturnsRegularPrice":0,"Tests\\Unit\\Domain\\Product\\ProductRepositoryTest::testGetPriceReturnsPromoPrice":0,"Tests\\Unit\\Domain\\Product\\ProductRepositoryTest::testGetPriceReturnsRegularWhenPromoIsHigher":0,"Tests\\Unit\\Domain\\Product\\ProductRepositoryTest::testGetPriceReturnsNullWhenNotFound":0,"Tests\\Unit\\Domain\\Product\\ProductRepositoryTest::testGetNameReturnsProductName":0,"Tests\\Unit\\Domain\\Product\\ProductRepositoryTest::testGetNameReturnsNullWhenNotFound":0,"Tests\\Unit\\Domain\\Product\\ProductRepositoryTest::testGetQuantityReturnsInteger":0,"Tests\\Unit\\Domain\\Product\\ProductRepositoryTest::testUnarchiveUpdatesProductAndChildren":0,"Tests\\Unit\\Domain\\Product\\ProductRepositoryTest::testArchiveUpdatesProductAndChildren":0,"Tests\\Unit\\Domain\\Product\\ProductRepositoryTest::testUnarchiveReturnsBool":0,"Tests\\Unit\\Domain\\Product\\ProductRepositoryTest::testArchiveReturnsBool":0,"Tests\\Unit\\admin\\Controllers\\ProductArchiveControllerTest::testConstructorAcceptsRepository":0.001,"Tests\\Unit\\admin\\Controllers\\ProductArchiveControllerTest::testHasListMethod":0,"Tests\\Unit\\admin\\Controllers\\ProductArchiveControllerTest::testHasUnarchiveMethod":0,"Tests\\Unit\\admin\\Controllers\\ProductArchiveControllerTest::testListMethodReturnType":0,"Tests\\Unit\\admin\\Controllers\\ProductArchiveControllerTest::testUnarchiveMethodReturnType":0,"Tests\\Unit\\admin\\Controllers\\ProductArchiveControllerTest::testConstructorRequiresProductRepository":0,"Tests\\Unit\\Domain\\Banner\\BannerRepositoryTest::testFindReturnsBannerWithTranslations":0.004,"Tests\\Unit\\Domain\\Banner\\BannerRepositoryTest::testFindReturnsNullWhenNotFound":0,"Tests\\Unit\\Domain\\Banner\\BannerRepositoryTest::testDeleteReturnsTrue":0.002,"Tests\\Unit\\Domain\\Banner\\BannerRepositoryTest::testSaveInsertsNewBanner":0,"Tests\\Unit\\Domain\\Cache\\CacheRepositoryTest::testClearCacheWithRedis":0.001,"Tests\\Unit\\Domain\\Cache\\CacheRepositoryTest::testClearCacheRedisUnavailable":0,"Tests\\Unit\\Domain\\Cache\\CacheRepositoryTest::testClearCacheWithoutRedis":0,"Tests\\Unit\\Domain\\Cache\\CacheRepositoryTest::testClearCacheReturnStructure":0,"Tests\\Unit\\Domain\\Settings\\SettingsRepositoryTest::testCanBeInstantiated":0.001,"Tests\\Unit\\Domain\\Settings\\SettingsRepositoryTest::testHasSaveSettingsMethod":0,"Tests\\Unit\\Domain\\Settings\\SettingsRepositoryTest::testHasGetSettingsMethod":0,"Tests\\Unit\\admin\\Controllers\\SettingsControllerTest::testConstructorAcceptsRepository":0.001,"Tests\\Unit\\admin\\Controllers\\SettingsControllerTest::testHasClearCacheMethod":0,"Tests\\Unit\\admin\\Controllers\\SettingsControllerTest::testHasClearCacheAjaxMethod":0,"Tests\\Unit\\admin\\Controllers\\SettingsControllerTest::testHasSaveMethod":0,"Tests\\Unit\\admin\\Controllers\\SettingsControllerTest::testHasViewMethod":0,"Tests\\Unit\\admin\\Controllers\\SettingsControllerTest::testIsNotAbstract":0,"Tests\\Unit\\admin\\Controllers\\SettingsControllerTest::testActionMethodReturnTypes":0}} \ No newline at end of file +{"version":1,"defects":[],"times":{"Tests\\Unit\\Domain\\Product\\ProductRepositoryTest::testGetQuantityReturnsCorrectValue":0.001,"Tests\\Unit\\Domain\\Product\\ProductRepositoryTest::testGetQuantityReturnsNullWhenProductNotFound":0,"Tests\\Unit\\Domain\\Product\\ProductRepositoryTest::testFindReturnsProductData":0,"Tests\\Unit\\Domain\\Product\\ProductRepositoryTest::testUpdateQuantitySuccess":0,"Tests\\Unit\\Domain\\Product\\ProductRepositoryTest::testGetPriceReturnsRegularPrice":0,"Tests\\Unit\\Domain\\Product\\ProductRepositoryTest::testGetPriceReturnsPromoPrice":0,"Tests\\Unit\\Domain\\Product\\ProductRepositoryTest::testGetPriceReturnsRegularWhenPromoIsHigher":0,"Tests\\Unit\\Domain\\Product\\ProductRepositoryTest::testGetPriceReturnsNullWhenNotFound":0,"Tests\\Unit\\Domain\\Product\\ProductRepositoryTest::testGetNameReturnsProductName":0,"Tests\\Unit\\Domain\\Product\\ProductRepositoryTest::testGetNameReturnsNullWhenNotFound":0,"Tests\\Unit\\Domain\\Product\\ProductRepositoryTest::testGetQuantityReturnsInteger":0,"Tests\\Unit\\Domain\\Product\\ProductRepositoryTest::testUnarchiveUpdatesProductAndChildren":0,"Tests\\Unit\\Domain\\Product\\ProductRepositoryTest::testArchiveUpdatesProductAndChildren":0,"Tests\\Unit\\Domain\\Product\\ProductRepositoryTest::testUnarchiveReturnsBool":0,"Tests\\Unit\\Domain\\Product\\ProductRepositoryTest::testArchiveReturnsBool":0,"Tests\\Unit\\admin\\Controllers\\ProductArchiveControllerTest::testConstructorAcceptsRepository":0.001,"Tests\\Unit\\admin\\Controllers\\ProductArchiveControllerTest::testHasListMethod":0,"Tests\\Unit\\admin\\Controllers\\ProductArchiveControllerTest::testHasUnarchiveMethod":0,"Tests\\Unit\\admin\\Controllers\\ProductArchiveControllerTest::testListMethodReturnType":0,"Tests\\Unit\\admin\\Controllers\\ProductArchiveControllerTest::testUnarchiveMethodReturnType":0,"Tests\\Unit\\admin\\Controllers\\ProductArchiveControllerTest::testConstructorRequiresProductRepository":0,"Tests\\Unit\\Domain\\Banner\\BannerRepositoryTest::testFindReturnsBannerWithTranslations":0.001,"Tests\\Unit\\Domain\\Banner\\BannerRepositoryTest::testFindReturnsNullWhenNotFound":0,"Tests\\Unit\\Domain\\Banner\\BannerRepositoryTest::testDeleteReturnsTrue":0.002,"Tests\\Unit\\Domain\\Banner\\BannerRepositoryTest::testSaveInsertsNewBanner":0,"Tests\\Unit\\Domain\\Cache\\CacheRepositoryTest::testClearCacheWithRedis":0.001,"Tests\\Unit\\Domain\\Cache\\CacheRepositoryTest::testClearCacheRedisUnavailable":0,"Tests\\Unit\\Domain\\Cache\\CacheRepositoryTest::testClearCacheWithoutRedis":0,"Tests\\Unit\\Domain\\Cache\\CacheRepositoryTest::testClearCacheReturnStructure":0,"Tests\\Unit\\Domain\\Settings\\SettingsRepositoryTest::testCanBeInstantiated":0.001,"Tests\\Unit\\Domain\\Settings\\SettingsRepositoryTest::testHasSaveSettingsMethod":0,"Tests\\Unit\\Domain\\Settings\\SettingsRepositoryTest::testHasGetSettingsMethod":0,"Tests\\Unit\\admin\\Controllers\\SettingsControllerTest::testConstructorAcceptsRepository":0.001,"Tests\\Unit\\admin\\Controllers\\SettingsControllerTest::testHasClearCacheMethod":0,"Tests\\Unit\\admin\\Controllers\\SettingsControllerTest::testHasClearCacheAjaxMethod":0,"Tests\\Unit\\admin\\Controllers\\SettingsControllerTest::testHasSaveMethod":0,"Tests\\Unit\\admin\\Controllers\\SettingsControllerTest::testHasViewMethod":0,"Tests\\Unit\\admin\\Controllers\\SettingsControllerTest::testIsNotAbstract":0,"Tests\\Unit\\admin\\Controllers\\SettingsControllerTest::testActionMethodReturnTypes":0,"Tests\\Unit\\admin\\Controllers\\ArticlesControllerTest::testCanCreateController":0.001,"Tests\\Unit\\admin\\Controllers\\ArticlesControllerTest::testHasListMethod":0,"Tests\\Unit\\admin\\Controllers\\ArticlesControllerTest::testListMethodReturnType":0,"Tests\\Unit\\admin\\Controllers\\ArticlesControllerTest::testHasEditMethod":0,"Tests\\Unit\\admin\\Controllers\\ArticlesControllerTest::testEditMethodReturnType":0,"Tests\\Unit\\admin\\Controllers\\ArticlesControllerTest::testConstructorAcceptsRepository":0,"Tests\\Unit\\admin\\Controllers\\ArticlesControllerTest::testConstructorRequiresArticleRepository":0,"Tests\\Unit\\Domain\\Article\\ArticleRepositoryTest::testFindReturnsArticleWithRelations":0.003,"Tests\\Unit\\Domain\\Article\\ArticleRepositoryTest::testFindReturnsNullWhenArticleDoesNotExist":0}} \ No newline at end of file diff --git a/DATABASE_STRUCTURE.md b/DATABASE_STRUCTURE.md index e76aa50..dbfeded 100644 --- a/DATABASE_STRUCTURE.md +++ b/DATABASE_STRUCTURE.md @@ -92,16 +92,49 @@ Artykuły. | id | PK | | status | -1 = archiwum, 0 = nieaktywny, 1 = aktywny | -**Używane w:** `admin\controls\ArticlesArchive` +**Używane w:** `admin\controls\ArticlesArchive`, `Domain\Article\ArticleRepository::find()` ## pp_articles_pages Strony artykułów. +| Kolumna | Opis | +|---------|------| +| article_id | FK do pp_articles | +| page_id | FK do strony (pp_pages) | +| o | Kolejność | + +**Używane w:** `Domain\Article\ArticleRepository::find()` + ## pp_articles_langs Tłumaczenia artykułów. +| Kolumna | Opis | +|---------|------| +| article_id | FK do pp_articles | +| lang_id | ID języka (np. 'pl') | +| title | Tytuł artykułu | +| seo_link | Link SEO artykułu | + +**Używane w:** `Domain\Article\ArticleRepository::find()` + ## pp_articles_images Zdjęcia artykułów. +| Kolumna | Opis | +|---------|------| +| article_id | FK do pp_articles | +| src | Ścieżka do pliku | +| o | Kolejność | +| id | PK (używane też do sortowania DESC) | + +**Używane w:** `Domain\Article\ArticleRepository::find()` + ## pp_articles_files Pliki artykułów. + +| Kolumna | Opis | +|---------|------| +| article_id | FK do pp_articles | +| src | Ścieżka do pliku | + +**Używane w:** `Domain\Article\ArticleRepository::find()` diff --git a/PROJECT_STRUCTURE.md b/PROJECT_STRUCTURE.md index 4729cb1..5d36a51 100644 --- a/PROJECT_STRUCTURE.md +++ b/PROJECT_STRUCTURE.md @@ -246,10 +246,18 @@ tests/ │ └── ProductArchiveControllerTest.php # 6 testów └── Integration/ ``` -**Łącznie: 39 testów, 73 asercji** +**Łącznie: 48 testw, 91 asercji** ## Ostatnie modyfikacje +### 2026-02-06: Migracja Articles::article_edit do DI (ver. 0.242) +- **NOWE:** `Domain\Article\ArticleRepository` - repozytorium artykułów (`find()`) +- **UPDATE:** `admin\Controllers\ArticlesController` - konstruktor DI + `edit()` używa repozytorium +- **UPDATE:** Router `admin\Site` - factory dla `ArticlesController` z `ArticleRepository` +- **UPDATE:** `admin\factory\Articles::article_details()` deleguje do `Domain\Article\ArticleRepository` +- **UPDATE:** Stare kontrolery `admin\controls\Articles|Banners|Settings` - metody przejęte przez nowe kontrolery oznaczone `@deprecated` +- Testy: 48 testów, 91 asercji + ### 2026-02-06: Migracja ProductArchive (ver. 0.241) - **NOWE:** `admin\Controllers\ProductArchiveController` - kontroler archiwum produktów z DI - **NOWE:** `ProductRepository::archive()`, `unarchive()` - operacje archiwizacji w repozytorium @@ -258,7 +266,7 @@ tests/ - **FIX:** Brakujący `archive = 1` w branchu bez wyszukiwania - **CLEANUP:** Usunięto zbędny JS z szablonu archiwum (apilo, baselinker, duplikowanie, edycja cen) - Stary kontroler `admin\controls\Archive` zachowany jako fallback -- Testy: 39 testów, 73 asercji (+10 nowych) +- Testy: 48 testw, 91 asercji (+10 nowych) ### 2026-02-05: Migracja Settings + Cache (ver. 0.240) - **NOWE:** `Domain\Settings\SettingsRepository` - repozytorium ustawień (fasada → factory) @@ -293,4 +301,5 @@ tests/ - Metoda `clear_product_cache()` w klasie S --- -*Dokument aktualizowany: 2026-02-05* +*Dokument aktualizowany: 2026-02-06* + diff --git a/REFACTORING_PLAN.md b/REFACTORING_PLAN.md index 13e5167..d177f31 100644 --- a/REFACTORING_PLAN.md +++ b/REFACTORING_PLAN.md @@ -174,6 +174,16 @@ grep -r "Product::getQuantity" . - 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: `tests/Unit/Domain/Article/ArticleRepositoryTest.php`, `tests/Unit/admin/Controllers/ArticlesControllerTest.php` + - Aktualizacja: ver. 0.242 + - **Settings** (migracja kontrolera - krok pośredni) - ✅ SettingsRepository - **ZMIGROWANE** (2026-02-05) 🎉 - Nowa klasa: `Domain\Settings\SettingsRepository` (saveSettings, getSettings) @@ -217,7 +227,7 @@ tests/ │ └── ProductArchiveControllerTest.php # 6 testów └── Integration/ ``` -**Łącznie: 39 testów, 73 asercji** +**Łącznie: 48 testów, 91 asercji** ### Przykład testu ```php @@ -305,4 +315,4 @@ vendor/bin/phpstan analyse autoload/Domain --- *Rozpoczęto: 2025-02-05* -*Ostatnia aktualizacja: 2026-02-05* +*Ostatnia aktualizacja: 2026-02-06* diff --git a/UPDATE_INSTRUCTIONS.md b/UPDATE_INSTRUCTIONS.md index 70af940..cfff524 100644 --- a/UPDATE_INSTRUCTIONS.md +++ b/UPDATE_INSTRUCTIONS.md @@ -11,6 +11,9 @@ Aktualizacje znajdują się w folderze `updates/0.XX/` gdzie XX oznacza dziesią - `changelog.php` - historia zmian - `versions.php` - konfiguracja wersji (zmienna `$current_ver`) +### Zasada pakowania plików +- Do paczek aktualizacji **nie dodajemy plików `*.md`** (dokumentacja jest tylko wewnętrzna/deweloperska). + ## Procedura tworzenia nowej aktualizacji ### 1. Określ numer wersji diff --git a/autoload/Domain/Article/ArticleRepository.php b/autoload/Domain/Article/ArticleRepository.php new file mode 100644 index 0000000..9a986d5 --- /dev/null +++ b/autoload/Domain/Article/ArticleRepository.php @@ -0,0 +1,43 @@ +db = $db; + } + + /** + * Pobiera artykul po ID wraz z tlumaczeniami, obrazami, plikami i powiazanymi stronami + */ + public function find(int $articleId): ?array + { + $article = $this->db->get('pp_articles', '*', ['id' => $articleId]); + + if (!$article) { + return null; + } + + $results = $this->db->select('pp_articles_langs', '*', ['article_id' => $articleId]); + if (is_array($results)) { + foreach ($results as $row) { + $article['languages'][$row['lang_id']] = $row; + } + } + + $article['images'] = $this->db->select('pp_articles_images', '*', [ + 'article_id' => $articleId, + 'ORDER' => ['o' => 'ASC', 'id' => 'DESC'] + ]); + $article['files'] = $this->db->select('pp_articles_files', '*', ['article_id' => $articleId]); + $article['pages'] = $this->db->select('pp_articles_pages', 'page_id', ['article_id' => $articleId]); + + return $article; + } +} diff --git a/autoload/admin/Controllers/ArticlesController.php b/autoload/admin/Controllers/ArticlesController.php new file mode 100644 index 0000000..8bf6eb5 --- /dev/null +++ b/autoload/admin/Controllers/ArticlesController.php @@ -0,0 +1,46 @@ +repository = $repository; + } + + /** + * Lista artykulow + */ + public function list(): string + { + return \admin\view\Articles::articles_list(); + } + + /** + * Edycja artykulu + */ + public function edit(): string + { + global $user; + + if (!$user) { + header('Location: /admin/'); + exit; + } + + \admin\factory\Articles::delete_nonassigned_images(); + \admin\factory\Articles::delete_nonassigned_files(); + + return \Tpl::view('articles/article-edit', [ + 'article' => $this->repository->find((int)\S::get('id')), + 'menus' => \admin\factory\Pages::menus_list(), + 'languages' => \admin\factory\Languages::languages_list(), + 'layouts' => \admin\factory\Layouts::layouts_list(), + 'user' => $user + ]); + } +} diff --git a/autoload/admin/class.Site.php b/autoload/admin/class.Site.php index a3a8aa5..1c2b48a 100644 --- a/autoload/admin/class.Site.php +++ b/autoload/admin/class.Site.php @@ -199,6 +199,13 @@ class Site return self::$newControllers; self::$newControllers = [ + 'Articles' => function() { + global $mdb; + + return new \admin\Controllers\ArticlesController( + new \Domain\Article\ArticleRepository( $mdb ) + ); + }, 'Banners' => function() { global $mdb; @@ -247,6 +254,7 @@ class Site */ private static $actionMap = [ 'view_list' => 'list', + 'article_edit' => 'edit', 'banner_edit' => 'edit', 'banner_save' => 'save', 'banner_delete' => 'delete', diff --git a/autoload/admin/controls/class.Articles.php b/autoload/admin/controls/class.Articles.php index a7e32b3..b2fddb7 100644 --- a/autoload/admin/controls/class.Articles.php +++ b/autoload/admin/controls/class.Articles.php @@ -39,6 +39,10 @@ class Articles exit; } + /** + * @deprecated Routing kieruje do admin\Controllers\ArticlesController::edit(). + * Ta metoda pozostaje tylko jako fallback dla starej architektury. + */ public static function article_edit() { global $user; @@ -59,9 +63,13 @@ class Articles ] ); } + /** + * @deprecated Routing kieruje do admin\Controllers\ArticlesController::list(). + * Ta metoda pozostaje tylko jako fallback dla starej architektury. + */ public static function view_list() { return \admin\view\Articles::articles_list(); } } -?> \ No newline at end of file +?> diff --git a/autoload/admin/controls/class.Banners.php b/autoload/admin/controls/class.Banners.php index ccce70a..9166a66 100644 --- a/autoload/admin/controls/class.Banners.php +++ b/autoload/admin/controls/class.Banners.php @@ -9,6 +9,10 @@ namespace admin\controls; */ class Banners { + /** + * @deprecated Routing kieruje do admin\Controllers\BannerController::delete(). + * Ta metoda pozostaje tylko jako fallback dla starej architektury. + */ public static function banner_delete() { if ( \admin\factory\Banners::banner_delete( \S::get( 'id' ) ) ) @@ -17,6 +21,10 @@ class Banners exit; } + /** + * @deprecated Routing kieruje do admin\Controllers\BannerController::save(). + * Ta metoda pozostaje tylko jako fallback dla starej architektury. + */ public static function banner_save() { $response = [ 'status' => 'error', 'msg' => 'Podczas zapisywania baneru wystąpił błąd. Proszę spróbować ponownie.' ]; @@ -30,6 +38,10 @@ class Banners exit; } + /** + * @deprecated Routing kieruje do admin\Controllers\BannerController::edit(). + * Ta metoda pozostaje tylko jako fallback dla starej architektury. + */ public static function banner_edit() { return \admin\view\Banners::banner_edit( @@ -40,6 +52,10 @@ class Banners ); } + /** + * @deprecated Routing kieruje do admin\Controllers\BannerController::list(). + * Ta metoda pozostaje tylko jako fallback dla starej architektury. + */ public static function view_list() { return \admin\view\Banners::banners_list(); diff --git a/autoload/admin/controls/class.Settings.php b/autoload/admin/controls/class.Settings.php index f11b666..63d71dd 100644 --- a/autoload/admin/controls/class.Settings.php +++ b/autoload/admin/controls/class.Settings.php @@ -3,6 +3,10 @@ namespace admin\controls; class Settings { + /** + * @deprecated Routing kieruje do admin\Controllers\SettingsController::clearCache(). + * Ta metoda pozostaje tylko jako fallback dla starej architektury. + */ static public function clear_cache() { \S::delete_dir( '../temp/' ); @@ -17,6 +21,10 @@ class Settings exit; } + /** + * @deprecated Routing kieruje do admin\Controllers\SettingsController::clearCacheAjax(). + * Ta metoda pozostaje tylko jako fallback dla starej architektury. + */ static public function clear_cache_ajax() { try @@ -41,6 +49,10 @@ class Settings exit; } + /** + * @deprecated Routing kieruje do admin\Controllers\SettingsController::save(). + * Ta metoda pozostaje tylko jako fallback dla starej architektury. + */ public static function settings_save() { $values = json_decode( \S::get( 'values' ), true ); @@ -88,6 +100,10 @@ class Settings exit; } + /** + * @deprecated Routing kieruje do admin\Controllers\SettingsController::view(). + * Ta metoda pozostaje tylko jako fallback dla starej architektury. + */ public static function view() { return \Tpl::view( 'settings/settings', [ @@ -96,4 +112,4 @@ class Settings ] ); } } -?> \ No newline at end of file +?> diff --git a/autoload/admin/factory/class.Articles.php b/autoload/admin/factory/class.Articles.php index 7f748ed..ee82309 100644 --- a/autoload/admin/factory/class.Articles.php +++ b/autoload/admin/factory/class.Articles.php @@ -123,19 +123,8 @@ class Articles public static function article_details( $article_id ) { global $mdb; - - if ( $article = $mdb -> get( 'pp_articles', '*', [ 'id' => (int)$article_id ] ) ) - { - $results = $mdb -> select( 'pp_articles_langs', '*', [ 'article_id' => (int)$article_id ] ); - if ( is_array( $results ) ) foreach ( $results as $row ) - $article['languages'][ $row['lang_id'] ] = $row; - - $article['images'] = $mdb -> select( 'pp_articles_images', '*', [ 'article_id' => (int)$article_id, 'ORDER' => [ 'o' => 'ASC', 'id' => 'DESC' ] ] ); - $article['files'] = $mdb -> select( 'pp_articles_files', '*', [ 'article_id' => (int)$article_id ] ); - $article['pages'] = $mdb -> select( 'pp_articles_pages', 'page_id', [ 'article_id' => (int)$article_id ] ); - } - - return $article; + $repository = new \Domain\Article\ArticleRepository( $mdb ); + return $repository->find( (int)$article_id ); } public static function max_order() @@ -469,4 +458,4 @@ class Articles $mdb -> delete( 'pp_articles_images', [ 'article_id' => null ] ); } } -?> \ No newline at end of file +?> diff --git a/tests/Unit/Domain/Article/ArticleRepositoryTest.php b/tests/Unit/Domain/Article/ArticleRepositoryTest.php new file mode 100644 index 0000000..749f09a --- /dev/null +++ b/tests/Unit/Domain/Article/ArticleRepositoryTest.php @@ -0,0 +1,60 @@ +createMock(\medoo::class); + + $mockDb->expects($this->once()) + ->method('get') + ->with('pp_articles', '*', ['id' => 7]) + ->willReturn(['id' => 7, 'status' => 1]); + + $mockDb->expects($this->exactly(4)) + ->method('select') + ->willReturnOnConsecutiveCalls( + [ + ['lang_id' => 'pl', 'title' => 'Artykul'], + ['lang_id' => 'en', 'title' => 'Article'], + ], + [ + ['id' => 10, 'src' => '/img/a.jpg'] + ], + [ + ['id' => 20, 'src' => '/files/a.pdf'] + ], + [1, 2] + ); + + $repository = new ArticleRepository($mockDb); + $article = $repository->find(7); + + $this->assertIsArray($article); + $this->assertEquals(7, $article['id']); + $this->assertArrayHasKey('languages', $article); + $this->assertEquals('Artykul', $article['languages']['pl']['title']); + $this->assertCount(1, $article['images']); + $this->assertCount(1, $article['files']); + $this->assertEquals([1, 2], $article['pages']); + } + + public function testFindReturnsNullWhenArticleDoesNotExist(): void + { + $mockDb = $this->createMock(\medoo::class); + $mockDb->expects($this->once()) + ->method('get') + ->with('pp_articles', '*', ['id' => 999]) + ->willReturn(false); + $mockDb->expects($this->never())->method('select'); + + $repository = new ArticleRepository($mockDb); + $article = $repository->find(999); + + $this->assertNull($article); + } +} diff --git a/tests/Unit/admin/Controllers/ArticlesControllerTest.php b/tests/Unit/admin/Controllers/ArticlesControllerTest.php new file mode 100644 index 0000000..4350cfb --- /dev/null +++ b/tests/Unit/admin/Controllers/ArticlesControllerTest.php @@ -0,0 +1,61 @@ +mockRepository = $this->createMock(ArticleRepository::class); + $this->controller = new ArticlesController($this->mockRepository); + } + + public function testCanCreateController(): void + { + $this->assertInstanceOf(ArticlesController::class, $this->controller); + } + + public function testConstructorAcceptsRepository(): void + { + $controller = new ArticlesController($this->mockRepository); + $this->assertInstanceOf(ArticlesController::class, $controller); + } + + public function testHasListMethod(): void + { + $this->assertTrue(method_exists($this->controller, 'list')); + } + + public function testHasEditMethod(): void + { + $this->assertTrue(method_exists($this->controller, 'edit')); + } + + public function testListMethodReturnType(): void + { + $reflection = new \ReflectionClass($this->controller); + $this->assertEquals('string', (string)$reflection->getMethod('list')->getReturnType()); + } + + public function testEditMethodReturnType(): void + { + $reflection = new \ReflectionClass($this->controller); + $this->assertEquals('string', (string)$reflection->getMethod('edit')->getReturnType()); + } + + public function testConstructorRequiresArticleRepository(): void + { + $reflection = new \ReflectionClass(ArticlesController::class); + $constructor = $reflection->getConstructor(); + $params = $constructor->getParameters(); + + $this->assertCount(1, $params); + $this->assertEquals('Domain\Article\ArticleRepository', $params[0]->getType()->getName()); + } +} diff --git a/updates/0.20/ver_0.242.zip b/updates/0.20/ver_0.242.zip new file mode 100644 index 0000000..0defbd7 Binary files /dev/null and b/updates/0.20/ver_0.242.zip differ diff --git a/updates/changelog.php b/updates/changelog.php index 2dd1c0a..3b46194 100644 --- a/updates/changelog.php +++ b/updates/changelog.php @@ -1,4 +1,8 @@ -ver. 0.241
+ver. 0.242
+- NEW - refaktoryzacja: Domain\Article\ArticleRepository + migracja article_edit do admin\Controllers\ArticlesController (DI) +- UPDATE - admin\factory\Articles::article_details() deleguje do nowego repozytorium (kompatybilno zachowana) +- UPDATE - metody przejte przez nowe kontrolery oznaczone jako @deprecated w legacy kontrolerach admin\controls +
ver. 0.241
- NEW - refaktoryzacja: admin\Controllers\ProductArchiveController - archiwum produktów z DI - NEW - ProductRepository::archive(), unarchive() - operacje archiwizacji w repozytorium - FIX - naprawiono SQL w liście archiwum (puste wyszukiwanie filtrowało wszystkie wyniki) @@ -298,4 +302,4 @@ - FIX - poprawa generowania plików WEBP
ver. 0.142
-- FIX - poprawa adresu strony głównej \ No newline at end of file +- FIX - poprawa adresu strony głównej diff --git a/updates/versions.php b/updates/versions.php index c2fd04a..7ed534d 100644 --- a/updates/versions.php +++ b/updates/versions.php @@ -1,5 +1,5 @@