diff --git a/.phpunit.result.cache b/.phpunit.result.cache index 32f3e1b..7917679 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.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 +{"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,"Tests\\Unit\\Domain\\Article\\ArticleRepositoryTest::testDeleteNonassignedFilesDeletesDbRows":0,"Tests\\Unit\\Domain\\Article\\ArticleRepositoryTest::testDeleteNonassignedImagesDeletesDbRows":0}} \ No newline at end of file diff --git a/DATABASE_STRUCTURE.md b/DATABASE_STRUCTURE.md index dbfeded..462452c 100644 --- a/DATABASE_STRUCTURE.md +++ b/DATABASE_STRUCTURE.md @@ -103,7 +103,7 @@ Strony artykułów. | page_id | FK do strony (pp_pages) | | o | Kolejność | -**Używane w:** `Domain\Article\ArticleRepository::find()` +**Używane w:** `Domain\Article\ArticleRepository::find()`, `Domain\Article\ArticleRepository::deleteNonassignedImages()` ## pp_articles_langs Tłumaczenia artykułów. @@ -115,7 +115,7 @@ Tłumaczenia artykułów. | title | Tytuł artykułu | | seo_link | Link SEO artykułu | -**Używane w:** `Domain\Article\ArticleRepository::find()` +**Używane w:** `Domain\Article\ArticleRepository::find()`, `Domain\Article\ArticleRepository::deleteNonassignedFiles()` ## pp_articles_images Zdjęcia artykułów. diff --git a/PROJECT_STRUCTURE.md b/PROJECT_STRUCTURE.md index 5d36a51..b0132ee 100644 --- a/PROJECT_STRUCTURE.md +++ b/PROJECT_STRUCTURE.md @@ -246,10 +246,16 @@ tests/ │ └── ProductArchiveControllerTest.php # 6 testów └── Integration/ ``` -**Łącznie: 48 testw, 91 asercji** +**Łącznie: 50 tests, 95 assertions** ## Ostatnie modyfikacje +### 2026-02-06: Articles cleanup moved to repository (ver. 0.243) +- **UPDATE:** `Domain\Article\ArticleRepository` - added `deleteNonassignedImages()` and `deleteNonassignedFiles()` +- **UPDATE:** `admin\Controllers\ArticlesController::edit()` uses repository cleanup methods +- **UPDATE:** `admin\factory\Articles::delete_nonassigned_images()` and `delete_nonassigned_files()` delegate to repository (backward compatibility) +- Testy: 50 tests, 95 assertions + ### 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 @@ -266,7 +272,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: 48 testw, 91 asercji (+10 nowych) +- Testy: 50 tests, 95 assertions (+10 nowych) ### 2026-02-05: Migracja Settings + Cache (ver. 0.240) - **NOWE:** `Domain\Settings\SettingsRepository` - repozytorium ustawień (fasada → factory) diff --git a/REFACTORING_PLAN.md b/REFACTORING_PLAN.md index d177f31..80fdf23 100644 --- a/REFACTORING_PLAN.md +++ b/REFACTORING_PLAN.md @@ -181,8 +181,8 @@ grep -r "Product::getQuantity" . - 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 + - Testy repozytorium rozszerzone o czyszczenie nieprzypisanych plikw/zdj + - Aktualizacja: ver. 0.243 - **Settings** (migracja kontrolera - krok pośredni) - ✅ SettingsRepository - **ZMIGROWANE** (2026-02-05) 🎉 @@ -315,4 +315,4 @@ vendor/bin/phpstan analyse autoload/Domain --- *Rozpoczęto: 2025-02-05* -*Ostatnia aktualizacja: 2026-02-06* +*Ostatnia aktualizacja: 2026-02-06* \ No newline at end of file diff --git a/UPDATE_INSTRUCTIONS.md b/UPDATE_INSTRUCTIONS.md index cfff524..790d973 100644 --- a/UPDATE_INSTRUCTIONS.md +++ b/UPDATE_INSTRUCTIONS.md @@ -13,6 +13,7 @@ Aktualizacje znajdują się w folderze `updates/0.XX/` gdzie XX oznacza dziesią ### Zasada pakowania plików - Do paczek aktualizacji **nie dodajemy plików `*.md`** (dokumentacja jest tylko wewnętrzna/deweloperska). +- Do paczek aktualizacji **nie dodajemy `updates/changelog.php`** (to plik serwisowy po stronie repozytorium aktualizacji, nie runtime klienta). ## Procedura tworzenia nowej aktualizacji diff --git a/autoload/Domain/Article/ArticleRepository.php b/autoload/Domain/Article/ArticleRepository.php index 9a986d5..de36849 100644 --- a/autoload/Domain/Article/ArticleRepository.php +++ b/autoload/Domain/Article/ArticleRepository.php @@ -40,4 +40,38 @@ class ArticleRepository return $article; } + + /** + * Usuwa nieprzypisane pliki artykulow (article_id = null) wraz z plikami z dysku. + */ + public function deleteNonassignedFiles(): void + { + $results = $this->db->select('pp_articles_files', '*', ['article_id' => null]); + if (is_array($results)) { + foreach ($results as $row) { + if (file_exists('../' . $row['src'])) { + unlink('../' . $row['src']); + } + } + } + + $this->db->delete('pp_articles_files', ['article_id' => null]); + } + + /** + * Usuwa nieprzypisane zdjecia artykulow (article_id = null) wraz z plikami z dysku. + */ + public function deleteNonassignedImages(): void + { + $results = $this->db->select('pp_articles_images', '*', ['article_id' => null]); + if (is_array($results)) { + foreach ($results as $row) { + if (file_exists('../' . $row['src'])) { + unlink('../' . $row['src']); + } + } + } + + $this->db->delete('pp_articles_images', ['article_id' => null]); + } } diff --git a/autoload/admin/Controllers/ArticlesController.php b/autoload/admin/Controllers/ArticlesController.php index 8bf6eb5..affee8a 100644 --- a/autoload/admin/Controllers/ArticlesController.php +++ b/autoload/admin/Controllers/ArticlesController.php @@ -32,8 +32,8 @@ class ArticlesController exit; } - \admin\factory\Articles::delete_nonassigned_images(); - \admin\factory\Articles::delete_nonassigned_files(); + $this->repository->deleteNonassignedImages(); + $this->repository->deleteNonassignedFiles(); return \Tpl::view('articles/article-edit', [ 'article' => $this->repository->find((int)\S::get('id')), diff --git a/autoload/admin/factory/class.Articles.php b/autoload/admin/factory/class.Articles.php index ee82309..605c53b 100644 --- a/autoload/admin/factory/class.Articles.php +++ b/autoload/admin/factory/class.Articles.php @@ -433,29 +433,15 @@ class Articles public static function delete_nonassigned_files() { global $mdb; - - $results = $mdb -> select( 'pp_articles_files', '*', [ 'article_id' => null ] ); - if ( is_array( $results ) ) foreach ( $results as $row ) - { - if ( file_exists( '../' . $row['src'] ) ) - unlink( '../' . $row['src'] ); - } - - $mdb -> delete( 'pp_articles_files', [ 'article_id' => null ] ); + $repository = new \Domain\Article\ArticleRepository( $mdb ); + $repository->deleteNonassignedFiles(); } public static function delete_nonassigned_images() { global $mdb; - - $results = $mdb -> select( 'pp_articles_images', '*', [ 'article_id' => null ] ); - if ( is_array( $results ) ) foreach ( $results as $row ) - { - if ( file_exists( '../' . $row['src'] ) ) - unlink( '../' . $row['src'] ); - } - - $mdb -> delete( 'pp_articles_images', [ 'article_id' => null ] ); + $repository = new \Domain\Article\ArticleRepository( $mdb ); + $repository->deleteNonassignedImages(); } } ?> diff --git a/tests/Unit/Domain/Article/ArticleRepositoryTest.php b/tests/Unit/Domain/Article/ArticleRepositoryTest.php index 749f09a..b040292 100644 --- a/tests/Unit/Domain/Article/ArticleRepositoryTest.php +++ b/tests/Unit/Domain/Article/ArticleRepositoryTest.php @@ -57,4 +57,46 @@ class ArticleRepositoryTest extends TestCase $this->assertNull($article); } + + public function testDeleteNonassignedFilesDeletesDbRows(): void + { + $mockDb = $this->createMock(\medoo::class); + + $mockDb->expects($this->once()) + ->method('select') + ->with('pp_articles_files', '*', ['article_id' => null]) + ->willReturn([ + ['id' => 1, 'src' => '/this/path/does/not/exist-file.tmp'] + ]); + + $mockDb->expects($this->once()) + ->method('delete') + ->with('pp_articles_files', ['article_id' => null]); + + $repository = new ArticleRepository($mockDb); + $repository->deleteNonassignedFiles(); + + $this->assertTrue(true); + } + + public function testDeleteNonassignedImagesDeletesDbRows(): void + { + $mockDb = $this->createMock(\medoo::class); + + $mockDb->expects($this->once()) + ->method('select') + ->with('pp_articles_images', '*', ['article_id' => null]) + ->willReturn([ + ['id' => 1, 'src' => '/this/path/does/not/exist-image.tmp'] + ]); + + $mockDb->expects($this->once()) + ->method('delete') + ->with('pp_articles_images', ['article_id' => null]); + + $repository = new ArticleRepository($mockDb); + $repository->deleteNonassignedImages(); + + $this->assertTrue(true); + } } diff --git a/updates/0.20/ver_0.243.zip b/updates/0.20/ver_0.243.zip new file mode 100644 index 0000000..6557f56 Binary files /dev/null and b/updates/0.20/ver_0.243.zip differ diff --git a/updates/changelog.php b/updates/changelog.php index 3b46194..c752bac 100644 --- a/updates/changelog.php +++ b/updates/changelog.php @@ -1,7 +1,10 @@ -ver. 0.242
+ver. 0.243
+- UPDATE - refaktoryzacja: cleanup nieprzypisanych plików/zdjęć artykułów przeniesiony do Domain\Article\ArticleRepository +- UPDATE - ArticlesController::edit() używa repozytorium do cleanupu, a admin\factory\Articles zachowuje delegowanie (kompatybilność) +
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 +- UPDATE - admin\factory\Articles::article_details() deleguje do nowego repozytorium (kompatybilność zachowana) +- UPDATE - metody przejęte 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 @@ -14,8 +17,7 @@ - NEW - refaktoryzacja: Domain\Cache\CacheRepository - czyszczenie cache z obsługą Redis - FIX - komunikat potwierdzenia zapisu ustawień w panelu administratora - FIX - naprawiono element #content w layoucie admina (powiadomienia grid.js) -
-ver. 0.239
+
ver. 0.239
- NEW - refaktoryzacja: Domain\Banner\BannerRepository + admin\Controllers\BannerController (pełna migracja kontrolera) - NEW - refaktoryzacja: Domain\Product\ProductRepository::getPrice(), getName() - migracja kolejnych metod - NEW - router admin z obsługą nowych kontrolerów (fallback na stare) @@ -229,7 +231,7 @@ - NEW - archiwum produktów
ver. 0.167
-- NEW - dodanie obługi cen i stanów magazynowych kombinacji produktów +- NEW - dodanie obsługi cen i stanów magazynowych kombinacji produktów
ver. 0.166
- NEW - współpraca z GTM @@ -303,3 +305,6 @@
ver. 0.142
- FIX - poprawa adresu strony głównej + + + diff --git a/updates/versions.php b/updates/versions.php index 7ed534d..a94dc14 100644 --- a/updates/versions.php +++ b/updates/versions.php @@ -1,5 +1,5 @@