--- phase: 03-domain-scontainers-banners plan: 01 type: execute wave: 1 depends_on: [] files_modified: - autoload/Domain/Scontainers/ScontainersRepository.php - autoload/Domain/Banners/BannersRepository.php - autoload/admin/factory/class.Scontainers.php - autoload/admin/factory/class.Banners.php - autoload/front/factory/class.Scontainers.php - autoload/front/factory/class.Banners.php autonomous: true delegation: auto --- ## Goal Create Domain\Scontainers\ScontainersRepository and Domain\Banners\BannersRepository, then convert legacy factory classes to wrapper delegation. ## Purpose Continue DDD refactoring — migrate Scontainers and Banners data access from static factory methods (global $mdb) to injected-dependency Domain repositories. Establishes wrapper delegation pattern for the first time in the project. ## Output - 2 new Domain repository files - 4 legacy factory files converted to wrappers ## Project Context @.paul/PROJECT.md @.paul/ROADMAP.md @.paul/STATE.md ## Source Files @autoload/admin/factory/class.Scontainers.php @autoload/admin/factory/class.Banners.php @autoload/front/factory/class.Scontainers.php @autoload/front/factory/class.Banners.php @autoload/Domain/Languages/LanguagesRepository.php (pattern reference) ## AC-1: ScontainersRepository exists with all methods ```gherkin Given the autoloader is configured for Domain\ namespace When ScontainersRepository is instantiated with $db (Medoo) Then it provides containerDetails(), containerSave(), containerDelete(), scontainerByLang() methods And all methods use $this->db instead of global $mdb ``` ## AC-2: BannersRepository exists with all methods ```gherkin Given the autoloader is configured for Domain\ namespace When BannersRepository is instantiated with $db (Medoo) Then it provides bannerDetails(), bannerSave(), bannerDelete(), activeBanners(), mainBanner() methods And all methods use $this->db instead of global $mdb ``` ## AC-3: Legacy admin factories delegate to repositories ```gherkin Given admin\factory\Scontainers and admin\factory\Banners exist When their static methods are called (e.g. container_save(), banner_delete()) Then they instantiate the Domain repository with global $mdb And delegate the call to the corresponding repository method And return the same result as before ``` ## AC-4: Legacy front factories delegate to repositories ```gherkin Given front\factory\Scontainers and front\factory\Banners exist When their static methods are called (e.g. scontainer_details(), banners()) Then they delegate to the Domain repository And caching behavior is preserved (Cache::fetch/store in repository) ``` Task 1: Create ScontainersRepository and BannersRepository autoload/Domain/Scontainers/ScontainersRepository.php, autoload/Domain/Banners/BannersRepository.php Create Domain\Scontainers\ScontainersRepository following LanguagesRepository pattern: - namespace Domain\Scontainers - Constructor: __construct($db) storing Medoo instance - containerDetails($containerId): get from pp_scontainers + pp_scontainers_langs (all langs) - containerSave($containerId, $title, $text, $status, $showTitle, $src, $html): insert/update pp_scontainers + pp_scontainers_langs with multi-language support. Handle single-lang vs multi-lang arrays exactly as current factory does. Call \S::delete_cache() after. - containerDelete($containerId): delete from pp_scontainers, call \S::delete_cache() - scontainerByLang($scontainerId, $langId): get container + single lang translation, use \Shared\Cache\CacheHandler::fetch/store (migrate from \Cache:: to \Shared\Cache\CacheHandler::) Create Domain\Banners\BannersRepository following same pattern: - namespace Domain\Banners - Constructor: __construct($db) - bannerDetails($bannerId): get from pp_banners + pp_banners_langs (all langs) - bannerSave($bannerId, $name, $status, $dateStart, $dateEnd, $homePage, $src, $url, $html, $text): insert/update pp_banners + pp_banners_langs. Handle single/multi lang arrays. Call \S::delete_cache(). - bannerDelete($bannerId): delete from pp_banners, call \S::delete_cache() - activeBanners($langId): active non-homepage banners with date filtering, use \Shared\Cache\CacheHandler for caching - mainBanner($langId): single active homepage banner with date filtering, cached IMPORTANT: - PHP < 8.0 compatible (no match, no named args, no union types, no str_contains) - Use $this->db->query() for complex SQL (date filtering in Banners) — keep raw SQL identical to current factory - Multi-language save pattern: query pp_langs for active languages, loop and insert translations - Status/checkbox conversion ('on' → 1, else 0) stays in repository methods php -l autoload/Domain/Scontainers/ScontainersRepository.php && php -l autoload/Domain/Banners/BannersRepository.php AC-1 and AC-2 satisfied: Both repositories exist with all methods, use injected $db Task 2: Convert legacy factories to wrapper delegation autoload/admin/factory/class.Scontainers.php, autoload/admin/factory/class.Banners.php, autoload/front/factory/class.Scontainers.php, autoload/front/factory/class.Banners.php Convert all 4 factory files to thin wrappers that delegate to Domain repositories. Pattern for each static method: ```php public static function method_name($args) { global $mdb; $repo = new \Domain\Scontainers\ScontainersRepository($mdb); return $repo->methodName($args); } ``` admin\factory\Scontainers: - container_delete($id) → $repo->containerDelete($id) - container_save(...) → $repo->containerSave(...) - container_details($id) → $repo->containerDetails($id) admin\factory\Banners: - banner_delete($id) → $repo->bannerDelete($id) - banner_save(...) → $repo->bannerSave(...) - banner_details($id) → $repo->bannerDetails($id) front\factory\Scontainers: - scontainer_details($id) → $repo->scontainerByLang($id, $lang[0]) — note: use global $lang front\factory\Banners: - banners() → $repo->activeBanners($lang[0]) - main_banner() → $repo->mainBanner($lang[0]) IMPORTANT: - Keep namespace declarations unchanged (admin\factory, front\factory) - Keep method signatures identical (same parameter names and order) - For front factories: pass $lang[0] explicitly to repository (repo does NOT use global $lang) php -l autoload/admin/factory/class.Scontainers.php && php -l autoload/admin/factory/class.Banners.php && php -l autoload/front/factory/class.Scontainers.php && php -l autoload/front/factory/class.Banners.php AC-3 and AC-4 satisfied: All legacy factories delegate to Domain repositories, signatures unchanged ## DO NOT CHANGE - autoload/autoloader.php (autoloader stable) - composer.json (PSR-4 mapping already includes Domain\) - autoload/admin/controls/class.Scontainers.php (admin controllers — Phase 10) - autoload/admin/controls/class.Banners.php (admin controllers — Phase 10) - autoload/admin/view/ (admin views — later phases) - autoload/front/view/ (front views — later phases) - autoload/class.Scontainer.php (legacy ArrayAccess entity — separate concern) - Any existing Domain\ repositories (Articles, Languages, Layouts, Pages, Settings, User) ## SCOPE LIMITS - Only factory → repository migration, NOT admin controllers or views - No new Composer dependencies - No database schema changes - Do not refactor the multi-language save pattern (keep it working as-is) Before declaring plan complete: - [ ] php -l passes for all 6 files (2 new + 4 modified) - [ ] ScontainersRepository has: containerDetails, containerSave, containerDelete, scontainerByLang - [ ] BannersRepository has: bannerDetails, bannerSave, bannerDelete, activeBanners, mainBanner - [ ] All 4 factory files are thin wrappers (no direct $mdb usage, only delegation) - [ ] No PHP 8.0+ syntax used - [ ] \S::delete_cache() calls preserved in repository methods - [ ] Caching (\Shared\Cache\CacheHandler) used in front-facing repository methods - All tasks completed - All verification checks pass - Zero regression — factory method signatures unchanged - Domain repositories follow established pattern (constructor DI, $this->db) After completion, create `.paul/phases/03-domain-scontainers-banners/03-01-SUMMARY.md`