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 0000000..699dc06 Binary files /dev/null and b/updates/0.20/ver_0.278.zip differ diff --git a/updates/changelog.php b/updates/changelog.php index 01cb936..0b4aba5 100644 --- a/updates/changelog.php +++ b/updates/changelog.php @@ -1,3 +1,7 @@ +ver. 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 @@