From 782dd35d5bfe1c9512b57213673c388f68ac48e1 Mon Sep 17 00:00:00 2001 From: Jacek Pyziak Date: Mon, 16 Feb 2026 13:50:27 +0100 Subject: [PATCH] ver. 0.278: Settings + Languages frontend migration, bug fix get_single_settings_value - Add cached frontend methods to existing Domain repositories (allSettings, getSingleValue, defaultLanguage, activeLanguages, translations) - Convert front\factory\Settings and Languages to facades delegating to Domain repositories - Fix get_single_settings_value() - was hardcoded to 'firm_name', now uses $param correctly - Add CacheHandler stub methods (get/set/exists) to test bootstrap - Establish architectural rule: Domain classes are shared between admin and frontend Co-Authored-By: Claude Opus 4.6 --- .../Domain/Languages/LanguagesRepository.php | 79 ++++++++++ .../Domain/Settings/SettingsRepository.php | 54 ++++++- autoload/front/factory/class.Languages.php | 83 +++-------- autoload/front/factory/class.Settings.php | 45 ++---- docs/CHANGELOG.md | 15 ++ docs/FRONTEND_REFACTORING_PLAN.md | 92 ++++++------ docs/TESTING.md | 9 +- docs/UPDATE_INSTRUCTIONS.md | 15 +- .../Languages/LanguagesRepositoryTest.php | 136 ++++++++++++++++++ .../Settings/SettingsRepositoryTest.php | 110 ++++++++++++-- tests/bootstrap.php | 3 + updates/0.20/ver_0.278.zip | Bin 0 -> 9775 bytes updates/changelog.php | 4 + updates/versions.php | 2 +- 14 files changed, 483 insertions(+), 164 deletions(-) create mode 100644 updates/0.20/ver_0.278.zip diff --git a/autoload/Domain/Languages/LanguagesRepository.php b/autoload/Domain/Languages/LanguagesRepository.php index cce748d..483dc12 100644 --- a/autoload/Domain/Languages/LanguagesRepository.php +++ b/autoload/Domain/Languages/LanguagesRepository.php @@ -332,6 +332,85 @@ class LanguagesRepository return $translationId; } + /** + * Zwraca ID domyslnego jezyka (z flaga start=1) z cache Redis. + */ + public function defaultLanguage(): string + { + $cacheHandler = new \CacheHandler(); + $cacheKey = 'Domain\Languages\LanguagesRepository::defaultLanguage'; + + $objectData = $cacheHandler->get($cacheKey); + if ($objectData) { + return unserialize($objectData); + } + + $results = $this->db->query( + 'SELECT id FROM pp_langs WHERE status = 1 ORDER BY start DESC, o ASC LIMIT 1' + )->fetchAll(); + + $defaultLanguage = $results[0][0] ?? 'pl'; + + $cacheHandler->set($cacheKey, $defaultLanguage); + + return $defaultLanguage; + } + + /** + * Zwraca liste aktywnych jezykow z cache Redis. + */ + public function activeLanguages(): array + { + $cacheHandler = new \CacheHandler(); + $cacheKey = 'Domain\Languages\LanguagesRepository::activeLanguages'; + + $objectData = $cacheHandler->get($cacheKey); + if ($objectData) { + return unserialize($objectData); + } + + $activeLanguages = $this->db->select( + 'pp_langs', + ['id', 'name'], + ['status' => 1, 'ORDER' => ['o' => 'ASC']] + ); + + if (!is_array($activeLanguages)) { + $activeLanguages = []; + } + + $cacheHandler->set($cacheKey, $activeLanguages); + + return $activeLanguages; + } + + /** + * Zwraca tlumaczenia dla danego jezyka z cache Redis. + */ + public function translations(string $language = 'pl'): array + { + $cacheHandler = new \CacheHandler(); + $cacheKey = "Domain\Languages\LanguagesRepository::translations:$language"; + + $objectData = $cacheHandler->get($cacheKey); + if ($objectData) { + return unserialize($objectData); + } + + $translations = ['0' => $language]; + + $results = $this->db->select('pp_langs_translations', ['text', $language]); + if (is_array($results)) { + foreach ($results as $row) { + $translations[$row['text']] = $row[$language]; + } + } + + $cacheHandler->set($cacheKey, $translations); + + return $translations; + } + private function sanitizeLanguageId(string $languageId): ?string { $languageId = strtolower(trim($languageId)); diff --git a/autoload/Domain/Settings/SettingsRepository.php b/autoload/Domain/Settings/SettingsRepository.php index 64e7acb..da559c4 100644 --- a/autoload/Domain/Settings/SettingsRepository.php +++ b/autoload/Domain/Settings/SettingsRepository.php @@ -2,7 +2,7 @@ namespace Domain\Settings; /** - * Repozytorium ustawien panelu administratora. + * Repozytorium ustawien — wspolne dla admin i frontendu. */ class SettingsRepository { @@ -141,6 +141,58 @@ class SettingsRepository return $settings; } + /** + * Pobranie wszystkich ustawien z cache Redis. + * + * @param bool $skipCache Pomija cache (np. przy generowaniu htaccess) + */ + public function allSettings(bool $skipCache = false): array + { + $cacheHandler = new \CacheHandler(); + $cacheKey = 'Domain\Settings\SettingsRepository::allSettings'; + + if (!$skipCache) { + $objectData = $cacheHandler->get($cacheKey); + if ($objectData) { + return unserialize($objectData); + } + } + + $results = $this->db->select('pp_settings', '*'); + $settings = []; + + if (is_array($results)) { + foreach ($results as $row) { + $settings[$row['param']] = $row['value']; + } + } + + $cacheHandler->set($cacheKey, $settings); + + return $settings; + } + + /** + * Pobranie pojedynczej wartosci ustawienia po nazwie parametru. + */ + public function getSingleValue(string $param): string + { + $cacheHandler = new \CacheHandler(); + $cacheKey = "Domain\Settings\SettingsRepository::getSingleValue:$param"; + + $objectData = $cacheHandler->get($cacheKey); + if ($objectData) { + return unserialize($objectData); + } + + $value = $this->db->get('pp_settings', 'value', ['param' => $param]); + $value = (string)($value ?? ''); + + $cacheHandler->set($cacheKey, $value); + + return $value; + } + private function isEnabled($value): bool { if (is_bool($value)) { diff --git a/autoload/front/factory/class.Languages.php b/autoload/front/factory/class.Languages.php index c3bab6c..543755b 100644 --- a/autoload/front/factory/class.Languages.php +++ b/autoload/front/factory/class.Languages.php @@ -1,78 +1,29 @@ get($cacheKey); - - if ( !$objectData ) + public static function default_language() { - $results = $mdb -> query( 'SELECT id FROM pp_langs WHERE status = 1 ORDER BY start DESC, o ASC LIMIT 1' ) -> fetchAll(); - $default_language = $results[0][0]; - - $cacheHandler -> set( $cacheKey, $default_language ); - } - else - { - return unserialize($objectData); - } - return $default_language; - } - - public static function active_languages() - { - global $mdb; - - $cacheHandler = new \CacheHandler(); - $cacheKey = "\front\factory\Languages::active_languages"; - - $objectData = $cacheHandler -> get( $cacheKey ); - - if ( !$objectData ) - { - $active_languages = $mdb -> select( 'pp_langs', [ 'id', 'name' ], [ 'status' => 1, 'ORDER' => [ 'o' => 'ASC' ] ] ); - - $cacheHandler -> set( $cacheKey, $active_languages ); - } - else - { - return unserialize( $objectData ); + global $mdb; + $repo = new \Domain\Languages\LanguagesRepository($mdb); + return $repo->defaultLanguage(); } - return $active_languages; - } - - public static function lang_translations( $language = 'pl' ) - { - global $mdb; - - $cacheHandler = new \CacheHandler(); - $cacheKey = "\front\factory\Languages::lang_translations:$language"; - - $objectData = $cacheHandler -> get( $cacheKey ); - - if ( !$objectData ) + public static function active_languages() { - $translations[ '0' ] = $language; - - $results = $mdb -> select( 'pp_langs_translations', [ 'text', $language ] ); - if ( is_array( $results ) ) foreach ( $results as $row ) - $translations[ $row['text'] ] = $row[ $language ]; - - $cacheHandler -> set( $cacheKey, $translations ); - } - else - { - return unserialize( $objectData ); + global $mdb; + $repo = new \Domain\Languages\LanguagesRepository($mdb); + return $repo->activeLanguages(); } - return $translations; - } + public static function lang_translations($language = 'pl') + { + global $mdb; + $repo = new \Domain\Languages\LanguagesRepository($mdb); + return $repo->translations($language); + } } diff --git a/autoload/front/factory/class.Settings.php b/autoload/front/factory/class.Settings.php index 72e359c..1c48f69 100644 --- a/autoload/front/factory/class.Settings.php +++ b/autoload/front/factory/class.Settings.php @@ -1,43 +1,22 @@ get($cacheKey); - - if ( !$objectData or $admin ) + public static function settings_details($admin = false) { - $results = $mdb -> select( 'pp_settings', '*' ); - if ( is_array( $results ) ) foreach ( $results as $row ) - $settings[ $row['param'] ] = $row['value']; - - $cacheHandler -> set( $cacheKey, $settings ); + global $mdb; + $repo = new \Domain\Settings\SettingsRepository($mdb); + return $repo->allSettings($admin); } - else + + public static function get_single_settings_value($param) { - return unserialize( $objectData ); + global $mdb; + $repo = new \Domain\Settings\SettingsRepository($mdb); + return $repo->getSingleValue($param); } - - return $settings; - } - - static public function get_single_settings_value( $param ) { - - global $mdb; - - if ( !$value = \Cache::fetch( "get_single_settings_value:$param" ) ) { - - $value = $mdb -> get( 'pp_settings', 'value', [ 'param' => 'firm_name' ] ); - \Cache::store( "get_single_settings_value:$param", $value ); - } - - return $value; - } } diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index bbbdcbd..9d1ab8a 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -4,6 +4,21 @@ Logi zmian z migracji na Domain-Driven Architecture. Najnowsze na gorze. --- +## ver. 0.278 (2026-02-16) - Settings + Languages frontend migration + +- **Settings + Languages (frontend)** — pierwszy etap refaktoringu frontendu + - NOWE METODY: `SettingsRepository::allSettings($skipCache)` — pobranie ustawien z cache Redis + - NOWE METODY: `SettingsRepository::getSingleValue($param)` — pobranie pojedynczej wartosci ustawienia + - NOWE METODY: `LanguagesRepository::defaultLanguage()` — domyslny jezyk z cache Redis + - NOWE METODY: `LanguagesRepository::activeLanguages()` — lista aktywnych jezykow z cache Redis + - NOWE METODY: `LanguagesRepository::translations($lang)` — tlumaczenia z cache Redis + - UPDATE: `front\factory\Settings` → fasada delegujaca do `SettingsRepository` + - UPDATE: `front\factory\Languages` → fasada delegujaca do `LanguagesRepository` + - FIX: `get_single_settings_value()` — parametr `$param` poprawnie uzywany (wczesniej hardcoded `'firm_name'`) + - Testy: 427 OK, 1378 asercji (+13 nowych) + +--- + ## ver. 0.277 (2026-02-16) - ShopProduct factory, Dashboard, Update, legacy cleanup, admin\App - **ShopProduct (factory)** - pelna migracja modulu #29 na Domain + DI diff --git a/docs/FRONTEND_REFACTORING_PLAN.md b/docs/FRONTEND_REFACTORING_PLAN.md index 6e2cf90..4f2edf3 100644 --- a/docs/FRONTEND_REFACTORING_PLAN.md +++ b/docs/FRONTEND_REFACTORING_PLAN.md @@ -153,26 +153,27 @@ Legacy Cleanup --- -## Plan wersjonowany +## Etapy migracji -### ver. 0.278 — Settings + Languages Frontend Services +### Etap: Settings + Languages — ZREALIZOWANY -**Cel:** Stworzyć serwisy domenowe dla Settings i Languages, naprawić buga Settings. +**Cel:** Dodac metody frontendowe (z cache Redis) do istniejacych repozytoriow Domain. -**NOWE:** -- `Domain/Settings/SettingsFrontendService.php` — `allSettings()`, `getSingleValue($param)` (FIX: używa poprawnie $param) -- `Domain/Languages/LanguagesFrontendService.php` — `defaultLanguage()`, `activeLanguages()`, `translations($lang)` -- Testy: `SettingsFrontendServiceTest`, `LanguagesFrontendServiceTest` +**DODANE METODY (do istniejacych klas):** +- `Domain/Settings/SettingsRepository` — `allSettings($skipCache)`, `getSingleValue($param)` (FIX: uzywa poprawnie $param) +- `Domain/Languages/LanguagesRepository` — `defaultLanguage()`, `activeLanguages()`, `translations($lang)` +- Testy: dopisane do `SettingsRepositoryTest` (6 testow), `LanguagesRepositoryTest` (7 testow) **ZMIANA:** -- `front/factory/Settings` → fasada delegująca do `SettingsFrontendService` -- `front/factory/Languages` → fasada delegująca do `LanguagesFrontendService` +- `front/factory/Settings` → fasada delegujaca do `SettingsRepository` +- `front/factory/Languages` → fasada delegujaca do `LanguagesRepository` +- `tests/bootstrap.php` — uzupelniony stub CacheHandler o `get()`/`set()`/`exists()` **BUG FIX:** `get_single_settings_value()` — zmiana `['param' => 'firm_name']` na `['param' => $param]` --- -### ver. 0.279 — Category Frontend Service +### Etap: Category Frontend Service **Cel:** Migracja `front\factory\ShopCategory` do Domain. @@ -186,7 +187,7 @@ Legacy Cleanup --- -### ver. 0.280 — Banners, Menu, Pages, Articles, Layouts Frontend Services +### Etap: Banners, Menu, Pages, Articles, Layouts Frontend Services **Cel:** Migracja pozostałych fabryk "liściowych". @@ -205,7 +206,7 @@ Legacy Cleanup --- -### ver. 0.281 — Promotion Engine (rozbicie circular dependency) +### Etap: Promotion Engine (rozbicie circular dependency) **Cel:** Przeniesienie silnika promocji do Domain. Rozbicie cyklicznej zależności `shop\Promotion ↔ front\factory\ShopPromotion`. @@ -231,7 +232,7 @@ front\factory\ShopPromotion::promotion_type_XX() → shop\Product::is_product_on --- -### ver. 0.282 — Product Frontend Service +### Etap: Product Frontend Service **Cel:** Migracja `front\factory\ShopProduct` i statycznych metod `shop\Product` do Domain. @@ -245,11 +246,11 @@ front\factory\ShopPromotion::promotion_type_XX() → shop\Product::is_product_on - `front/factory/ShopProduct` → fasada - `shop/Product` statyczne metody → fasady do `ProductFrontendService` -**UWAGA:** Konstruktor `shop\Product`, `getFromCache()`, `calculate_basket_product_price()` — zostają na razie (instancyjne, używane w szablonach). Migracja w ver. 0.286. +**UWAGA:** Konstruktor `shop\Product`, `getFromCache()`, `calculate_basket_product_price()` — zostają na razie (instancyjne, używane w szablonach). Migracja w etapie "Product Instance + Cache". --- -### ver. 0.283 — Client Authentication (Security Fix) +### Etap: Client Authentication (Security Fix) **Cel:** Migracja `front\factory\ShopClient` + NAPRAWIENIE hardcoded password bypass. @@ -267,7 +268,7 @@ front\factory\ShopPromotion::promotion_type_XX() → shop\Product::is_product_on --- -### ver. 0.284 — Transport, Payment, Coupon Frontend Services +### Etap: Transport, Payment, Coupon Frontend Services **Cel:** Frontend serwisy dla transportu, płatności i kuponów. @@ -285,7 +286,7 @@ front\factory\ShopPromotion::promotion_type_XX() → shop\Product::is_product_on --- -### ver. 0.285 — Basket Service +### Etap: Basket Service **Cel:** Migracja logiki koszyka do `Domain\Basket\BasketService`. @@ -305,7 +306,7 @@ front\factory\ShopPromotion::promotion_type_XX() → shop\Product::is_product_on --- -### ver. 0.286 — shop\Product Instance + Cache +### Etap: shop\Product Instance + Cache **Cel:** Refaktoring konstruktora `shop\Product` i `getFromCache()`. @@ -321,7 +322,7 @@ front\factory\ShopPromotion::promotion_type_XX() → shop\Product::is_product_on --- -### ver. 0.287 — Order Creation Frontend Service +### Etap: Order Creation Frontend Service **Cel:** Migracja `front\factory\ShopOrder::basket_save()` (~180 linii). @@ -337,7 +338,7 @@ front\factory\ShopPromotion::promotion_type_XX() → shop\Product::is_product_on --- -### ver. 0.288 — Payment Webhook Service +### Etap: Payment Webhook Service **Cel:** Wyodrębnienie webhooków płatności z `front\controls\ShopOrder`. @@ -356,7 +357,7 @@ front\factory\ShopPromotion::promotion_type_XX() → shop\Product::is_product_on --- -### ver. 0.289 — shop\Order Instance + Apilo Service +### Etap: shop\Order Instance + Apilo Service **Cel:** Refaktoring `shop\Order` instancyjnych metod + wyodrębnienie integracji Apilo. @@ -374,7 +375,7 @@ front\factory\ShopPromotion::promotion_type_XX() → shop\Product::is_product_on --- -### ver. 0.290 — Frontend App + Controllers (DI layer) +### Etap: Frontend App + Controllers (DI layer) **Cel:** Stworzenie `front\App` (wzorowanego na `admin\App`) z mapą kontrolerów i DI. @@ -390,7 +391,7 @@ front\factory\ShopPromotion::promotion_type_XX() → shop\Product::is_product_on --- -### ver. 0.291 — Site Layout Engine +### Etap: Site Layout Engine **Cel:** Refaktoring `front\view\Site::show()` (~600 linii) na testowalny `LayoutEngine`. @@ -406,7 +407,7 @@ front\factory\ShopPromotion::promotion_type_XX() → shop\Product::is_product_on --- -### ver. 0.292 — Entry Point Unification +### Etap: Entry Point Unification **Cel:** Ujednolicenie bootstrapu `index.php` i `ajax.php`. @@ -420,7 +421,7 @@ front\factory\ShopPromotion::promotion_type_XX() → shop\Product::is_product_on --- -### ver. 0.293 — Legacy Cleanup +### Etap: Legacy Cleanup **Cel:** Finalne porządki. @@ -440,26 +441,26 @@ front\factory\ShopPromotion::promotion_type_XX() → shop\Product::is_product_on ## Podsumowanie -| Wersja | Zakres | Priorytet | Nowe klasy Domain | Testy | -|--------|--------|-----------|-------------------|-------| -| 0.278 | Settings + Languages | FUNDAMENT | 2 serwisy | 2 | -| 0.279 | Category Frontend | WYSOKI | 1 serwis | 1 | -| 0.280 | Banners/Menu/Pages/Articles/Layouts | ŚREDNI | 5 serwisów | 5 | -| 0.281 | Promotion Engine | KRYTYCZNY | 1 serwis | 1 | -| 0.282 | Product Frontend | KRYTYCZNY | 1 serwis | 1 | -| 0.283 | Client/Auth (security fix) | KRYTYCZNY | 1 serwis | 1 | -| 0.284 | Transport/Payment/Coupon | WYSOKI | 3 serwisy | 3 | -| 0.285 | Basket Service | WYSOKI | 1 serwis | 1 | -| 0.286 | Product Instance + Cache | ŚREDNI | 1 loader | 1 | -| 0.287 | Order Creation | WYSOKI | 1 serwis | 1 | -| 0.288 | Payment Webhooks | WYSOKI | 1 serwis | 1 | -| 0.289 | Order Instance + Apilo | ŚREDNI | 2 serwisy | 2 | -| 0.290 | Frontend App + Controllers | WYSOKI | App + 3 kontrolery | 3 | -| 0.291 | Layout Engine | ŚREDNI | 1 engine | 1 | -| 0.292 | Entry Point Unification | ŚREDNI | Bootstrap + PostProcessor | 1 | -| 0.293 | Legacy Cleanup | NISKI | — | — | +| Etap | Zakres | Priorytet | Nowe klasy Domain | Testy | +|------|--------|-----------|-------------------|-------| +| Settings + Languages | Fundamenty | FUNDAMENT | 2 serwisy | 2 | +| Category Frontend | Kategorie | WYSOKI | 1 serwis | 1 | +| Banners/Menu/Pages/Articles/Layouts | Treści | ŚREDNI | 5 serwisów | 5 | +| Promotion Engine | Promocje | KRYTYCZNY | 1 serwis | 1 | +| Product Frontend | Produkty | KRYTYCZNY | 1 serwis | 1 | +| Client/Auth (security fix) | Klienci | KRYTYCZNY | 1 serwis | 1 | +| Transport/Payment/Coupon | Dostawa/Płatności | WYSOKI | 3 serwisy | 3 | +| Basket Service | Koszyk | WYSOKI | 1 serwis | 1 | +| Product Instance + Cache | Produkt cache | ŚREDNI | 1 loader | 1 | +| Order Creation | Zamówienia | WYSOKI | 1 serwis | 1 | +| Payment Webhooks | Webhooki | WYSOKI | 1 serwis | 1 | +| Order Instance + Apilo | Zamówienie + Apilo | ŚREDNI | 2 serwisy | 2 | +| Frontend App + Controllers | DI layer | WYSOKI | App + 3 kontrolery | 3 | +| Layout Engine | Silnik layoutu | ŚREDNI | 1 engine | 1 | +| Entry Point Unification | Entry points | ŚREDNI | Bootstrap + PostProcessor | 1 | +| Legacy Cleanup | Porządki | NISKI | — | — | -**Łącznie:** 16 wersji, ~24 nowe klasy Domain, ~25 plików testowych +**Łącznie:** 16 etapów, ~24 nowe klasy Domain, ~25 plików testowych ### Wzorce do przestrzegania (z migracji admin) - Konstruktor DI z `$db` (medoo) @@ -468,8 +469,9 @@ front\factory\ShopPromotion::promotion_type_XX() → shop\Product::is_product_on - Testy PHPUnit z mockami medoo - Namespace `\Domain\*` → `autoload/Domain/*/` - Namespace `\front\Controllers\` → `autoload/front/Controllers/` +- **Klasy Domain sa wspolne dla admin i frontendu** — NIE tworzymy osobnych FrontendService/AdminService. Metody frontendowe (z cache Redis) dodajemy do istniejacych repozytoriow/serwisow Domain. Klasy sa ladowane lazy (instancja tworzona dopiero przy wywolaniu), wiec nie wplywaja na wydajnosc. -### Weryfikacja po każdej wersji +### Weryfikacja po każdym etapie 1. `composer test` (pełny suite PHPUnit) 2. Manualne sprawdzenie frontendu: strona główna, kategoria, produkt, koszyk, zamówienie 3. Sprawdzenie AJAX: dodawanie do koszyka, zmiana transportu, kupon diff --git a/docs/TESTING.md b/docs/TESTING.md index 083229d..a44d000 100644 --- a/docs/TESTING.md +++ b/docs/TESTING.md @@ -36,7 +36,14 @@ Alternatywnie (Git Bash): Ostatnio zweryfikowano: 2026-02-16 ```text -OK (414 tests, 1335 assertions) +OK (427 tests, 1378 assertions) +``` + +Aktualizacja po migracji Settings + Languages frontend (2026-02-16, ver. 0.278): +```text +Pelny suite: OK (427 tests, 1378 assertions) +Nowe testy: SettingsRepositoryTest (+6: allSettings, getSingleValue, bugfix param), LanguagesRepositoryTest (+7: defaultLanguage, activeLanguages, translations) +Zaktualizowane: tests/bootstrap.php (stub CacheHandler: get/set/exists) ``` Aktualizacja po migracji Dashboard + Update + legacy cleanup (2026-02-16, ver. 0.277): diff --git a/docs/UPDATE_INSTRUCTIONS.md b/docs/UPDATE_INSTRUCTIONS.md index f5dff70..3f86ee0 100644 --- a/docs/UPDATE_INSTRUCTIONS.md +++ b/docs/UPDATE_INSTRUCTIONS.md @@ -18,20 +18,19 @@ Aktualizacje znajdują się w folderze `updates/0.XX/` gdzie XX oznacza dziesią ## Procedura tworzenia nowej aktualizacji -## Status biezacej aktualizacji (ver. 0.277) +## Status biezacej aktualizacji (ver. 0.278) -- Wersja udostepniona: `0.277` (data: 2026-02-16). +- Wersja udostepniona: `0.278` (data: 2026-02-16). - Pliki publikacyjne: - - `updates/0.20/ver_0.277.zip` - - `updates/0.20/ver_0.277_files.txt` + - `updates/0.20/ver_0.278.zip` - Pliki metadanych aktualizacji: - - `updates/changelog.php` (dodany wpis `ver. 0.277`) - - `updates/versions.php` (`$current_ver = 277`) + - `updates/changelog.php` (dodany wpis `ver. 0.278`) + - `updates/versions.php` (`$current_ver = 278`) - Weryfikacja testow przed publikacja: - - `OK (414 tests, 1335 assertions)` + - `OK (427 tests, 1378 assertions)` ### 1. Określ numer wersji -Sprawdź ostatnią wersję w `temp/` i zwiększ o 1. +Sprawdź ostatnią wersję w `updates/` i zwiększ o 1. ### 2. Utwórz folder tymczasowy ze strukturą w katalogu temp ```bash diff --git a/tests/Unit/Domain/Languages/LanguagesRepositoryTest.php b/tests/Unit/Domain/Languages/LanguagesRepositoryTest.php index 97375f2..f074eef 100644 --- a/tests/Unit/Domain/Languages/LanguagesRepositoryTest.php +++ b/tests/Unit/Domain/Languages/LanguagesRepositoryTest.php @@ -145,4 +145,140 @@ class LanguagesRepositoryTest extends TestCase $this->assertSame('en', $repository->defaultLanguageId()); $this->assertSame('pl', $repository->defaultLanguageId()); } + + // --- Metody frontendowe (z cache Redis) --- + + public function testDefaultLanguageReturnsId(): void + { + $mockDb = $this->createMock(\medoo::class); + + $mockStmt = $this->createMock(\PDOStatement::class); + $mockStmt->expects($this->once()) + ->method('fetchAll') + ->willReturn([['pl']]); + + $mockDb->expects($this->once()) + ->method('query') + ->with('SELECT id FROM pp_langs WHERE status = 1 ORDER BY start DESC, o ASC LIMIT 1') + ->willReturn($mockStmt); + + $repository = new LanguagesRepository($mockDb); + $langId = $repository->defaultLanguage(); + + $this->assertEquals('pl', $langId); + } + + public function testDefaultLanguageReturnsFallbackWhenEmpty(): void + { + $mockDb = $this->createMock(\medoo::class); + + $mockStmt = $this->createMock(\PDOStatement::class); + $mockStmt->expects($this->once()) + ->method('fetchAll') + ->willReturn([]); + + $mockDb->expects($this->once()) + ->method('query') + ->willReturn($mockStmt); + + $repository = new LanguagesRepository($mockDb); + $langId = $repository->defaultLanguage(); + + $this->assertEquals('pl', $langId); + } + + public function testActiveLanguagesReturnsList(): void + { + $mockDb = $this->createMock(\medoo::class); + + $mockDb->expects($this->once()) + ->method('select') + ->with( + 'pp_langs', + ['id', 'name'], + ['status' => 1, 'ORDER' => ['o' => 'ASC']] + ) + ->willReturn([ + ['id' => 'pl', 'name' => 'Polski'], + ['id' => 'en', 'name' => 'English'], + ]); + + $repository = new LanguagesRepository($mockDb); + $languages = $repository->activeLanguages(); + + $this->assertIsArray($languages); + $this->assertCount(2, $languages); + $this->assertEquals('pl', $languages[0]['id']); + $this->assertEquals('English', $languages[1]['name']); + } + + public function testActiveLanguagesReturnsEmptyArrayWhenNone(): void + { + $mockDb = $this->createMock(\medoo::class); + + $mockDb->expects($this->once()) + ->method('select') + ->willReturn(null); + + $repository = new LanguagesRepository($mockDb); + $languages = $repository->activeLanguages(); + + $this->assertIsArray($languages); + $this->assertEmpty($languages); + } + + public function testTranslationsReturnsArray(): void + { + $mockDb = $this->createMock(\medoo::class); + + $mockDb->expects($this->once()) + ->method('select') + ->with('pp_langs_translations', ['text', 'pl']) + ->willReturn([ + ['text' => 'basket', 'pl' => 'Koszyk'], + ['text' => 'order', 'pl' => 'Zamowienie'], + ]); + + $repository = new LanguagesRepository($mockDb); + $translations = $repository->translations('pl'); + + $this->assertIsArray($translations); + $this->assertEquals('pl', $translations['0']); + $this->assertEquals('Koszyk', $translations['basket']); + $this->assertEquals('Zamowienie', $translations['order']); + } + + public function testTranslationsDefaultsToPl(): void + { + $mockDb = $this->createMock(\medoo::class); + + $mockDb->expects($this->once()) + ->method('select') + ->with('pp_langs_translations', ['text', 'pl']) + ->willReturn([]); + + $repository = new LanguagesRepository($mockDb); + $translations = $repository->translations(); + + $this->assertIsArray($translations); + $this->assertEquals('pl', $translations['0']); + } + + public function testTranslationsForDifferentLanguage(): void + { + $mockDb = $this->createMock(\medoo::class); + + $mockDb->expects($this->once()) + ->method('select') + ->with('pp_langs_translations', ['text', 'en']) + ->willReturn([ + ['text' => 'basket', 'en' => 'Cart'], + ]); + + $repository = new LanguagesRepository($mockDb); + $translations = $repository->translations('en'); + + $this->assertEquals('en', $translations['0']); + $this->assertEquals('Cart', $translations['basket']); + } } diff --git a/tests/Unit/Domain/Settings/SettingsRepositoryTest.php b/tests/Unit/Domain/Settings/SettingsRepositoryTest.php index 9cb76c7..c47d185 100644 --- a/tests/Unit/Domain/Settings/SettingsRepositoryTest.php +++ b/tests/Unit/Domain/Settings/SettingsRepositoryTest.php @@ -4,15 +4,6 @@ namespace Tests\Unit\Domain\Settings; use PHPUnit\Framework\TestCase; use Domain\Settings\SettingsRepository; -/** - * Testy dla SettingsRepository - * - * UWAGA: SettingsRepository jest krokiem pośrednim migracji - deleguje do - * statycznych metod admin\factory\Settings. Pełne testy jednostkowe z mockami - * będą możliwe po migracji do DI (jak ProductRepository/BannerRepository). - * - * Na razie testujemy tylko to, co da się zweryfikować bez bazy danych. - */ class SettingsRepositoryTest extends TestCase { public function testCanBeInstantiated(): void @@ -30,4 +21,105 @@ class SettingsRepositoryTest extends TestCase { $this->assertTrue(method_exists(SettingsRepository::class, 'getSettings')); } + + public function testAllSettingsReturnsAssociativeArray(): void + { + $mockDb = $this->createMock(\medoo::class); + + $mockDb->expects($this->once()) + ->method('select') + ->with('pp_settings', '*') + ->willReturn([ + ['param' => 'firm_name', 'value' => 'Test Shop'], + ['param' => 'contact_email', 'value' => 'test@example.com'], + ['param' => 'ssl', 'value' => '1'], + ]); + + $service = new SettingsRepository($mockDb); + $settings = $service->allSettings(true); + + $this->assertIsArray($settings); + $this->assertEquals('Test Shop', $settings['firm_name']); + $this->assertEquals('test@example.com', $settings['contact_email']); + $this->assertEquals('1', $settings['ssl']); + $this->assertCount(3, $settings); + } + + public function testAllSettingsReturnsEmptyArrayWhenNoSettings(): void + { + $mockDb = $this->createMock(\medoo::class); + + $mockDb->expects($this->once()) + ->method('select') + ->with('pp_settings', '*') + ->willReturn([]); + + $service = new SettingsRepository($mockDb); + $settings = $service->allSettings(true); + + $this->assertIsArray($settings); + $this->assertEmpty($settings); + } + + public function testAllSettingsHandlesNullFromDb(): void + { + $mockDb = $this->createMock(\medoo::class); + + $mockDb->expects($this->once()) + ->method('select') + ->with('pp_settings', '*') + ->willReturn(null); + + $service = new SettingsRepository($mockDb); + $settings = $service->allSettings(true); + + $this->assertIsArray($settings); + $this->assertEmpty($settings); + } + + public function testGetSingleValueReturnsCorrectParam(): void + { + $mockDb = $this->createMock(\medoo::class); + + $mockDb->expects($this->once()) + ->method('get') + ->with('pp_settings', 'value', ['param' => 'contact_email']) + ->willReturn('test@example.com'); + + $service = new SettingsRepository($mockDb); + $value = $service->getSingleValue('contact_email'); + + $this->assertEquals('test@example.com', $value); + } + + public function testGetSingleValueUsesParamNotHardcoded(): void + { + $mockDb = $this->createMock(\medoo::class); + + // Kluczowy test: sprawdza ze param NIE jest hardcoded na 'firm_name' + $mockDb->expects($this->once()) + ->method('get') + ->with('pp_settings', 'value', ['param' => 'ssl']) + ->willReturn('1'); + + $service = new SettingsRepository($mockDb); + $value = $service->getSingleValue('ssl'); + + $this->assertEquals('1', $value); + } + + public function testGetSingleValueReturnsEmptyStringWhenNotFound(): void + { + $mockDb = $this->createMock(\medoo::class); + + $mockDb->expects($this->once()) + ->method('get') + ->with('pp_settings', 'value', ['param' => 'nonexistent']) + ->willReturn(null); + + $service = new SettingsRepository($mockDb); + $value = $service->getSingleValue('nonexistent'); + + $this->assertEquals('', $value); + } } diff --git a/tests/bootstrap.php b/tests/bootstrap.php index 67f7798..43ea4eb 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -78,6 +78,9 @@ if (!class_exists('Redis')) { if (!class_exists('CacheHandler')) { class CacheHandler { + public function get($key) { return null; } + public function set($key, $value, $ttl = 86400) {} + public function exists($key) { return false; } public function delete($key) {} public function deletePattern($pattern) {} } diff --git a/updates/0.20/ver_0.278.zip b/updates/0.20/ver_0.278.zip new file mode 100644 index 0000000000000000000000000000000000000000..699dc06d35baa97dcfc80d8d9a5d13fce774c187 GIT binary patch literal 9775 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. 0.278 - 16.02.2026
+- UPDATE - migracja Settings + Languages do wspolnych klas Domain (z cache Redis) +- FIX - `get_single_settings_value()` — parametr `$param` poprawnie uzywany (wczesniej hardcoded `firm_name`) +
ver. 0.277 - 16.02.2026
- NEW - migracja modulu `ShopProduct` (factory) — pelna migracja ~40 metod do `ProductRepository` + ~30 akcji w `ShopProductController` - NEW - migracja modulu `Dashboard` do Domain + DI (`DashboardRepository`, `DashboardController`) diff --git a/updates/versions.php b/updates/versions.php index 176ae77..4d0809e 100644 --- a/updates/versions.php +++ b/updates/versions.php @@ -1,5 +1,5 @@