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`)
+