Files
cmsPRO/.paul/phases/04-domain-authors-newsletter/04-01-PLAN.md
Jacek Pyziak ffe661b4d2 feat(domain): Domain\Authors + Domain\Newsletter repositories z wrapper delegation
Phase 4 complete:
- AuthorsRepository: simpleList, authorDetails, authorSave, authorDelete, authorByLang
- NewsletterRepository: 14 methods — subscriber lifecycle, templates, sending
- 4 legacy factories converted to thin wrappers
- Globals ($settings, $lang) passed as explicit params to repo methods

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-04 18:21:32 +02:00

10 KiB

phase, plan, type, wave, depends_on, files_modified, autonomous, delegation
phase plan type wave depends_on files_modified autonomous delegation
04-domain-authors-newsletter 01 execute 1
autoload/Domain/Authors/AuthorsRepository.php
autoload/Domain/Newsletter/NewsletterRepository.php
autoload/admin/factory/class.Authors.php
autoload/admin/factory/class.Newsletter.php
autoload/front/factory/class.Authors.php
autoload/front/factory/class.Newsletter.php
true auto
## Goal Create Domain\Authors\AuthorsRepository and Domain\Newsletter\NewsletterRepository, then convert legacy factory classes to wrapper delegation.

Purpose

Continue DDD refactoring — migrate Authors and Newsletter data access to Domain repositories using established wrapper delegation pattern from Phase 3.

Output

  • 2 new Domain repository files
  • 4 legacy factory files converted to wrappers
## Project Context @.paul/PROJECT.md @.paul/ROADMAP.md @.paul/STATE.md

Prior Work

@.paul/phases/03-domain-scontainers-banners/03-01-SUMMARY.md (wrapper delegation pattern reference)

Source Files

@autoload/admin/factory/class.Authors.php @autoload/admin/factory/class.Newsletter.php @autoload/front/factory/class.Authors.php @autoload/front/factory/class.Newsletter.php @autoload/Domain/Languages/LanguagesRepository.php (pattern reference)

<acceptance_criteria>

AC-1: AuthorsRepository exists with all methods

Given the autoloader is configured for Domain\ namespace
When AuthorsRepository is instantiated with $db (Medoo)
Then it provides simpleList(), authorDetails(), authorSave(), authorDelete(), authorByLang() methods
And all methods use $this->db instead of global $mdb

AC-2: NewsletterRepository exists with all methods

Given the autoloader is configured for Domain\ namespace
When NewsletterRepository is instantiated with $db (Medoo)
Then it provides emailsImport(), isAdminTemplate(), templateDelete(), send(), templateDetails(), templateSave(), templatesList(), unsubscribe(), confirm(), newsletterSend(), getHash(), signin(), getTemplate(), signout() methods
And all methods use $this->db instead of global $mdb

AC-3: Legacy admin factories delegate to repositories

Given admin\factory\Authors and admin\factory\Newsletter exist
When their static methods are called
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

Given front\factory\Authors and front\factory\Newsletter exist
When their static methods are called
Then they delegate to the Domain repository
And caching behavior is preserved (in repository for Authors)

</acceptance_criteria>

Task 1: Create AuthorsRepository and NewsletterRepository autoload/Domain/Authors/AuthorsRepository.php, autoload/Domain/Newsletter/NewsletterRepository.php Create Domain\Authors\AuthorsRepository following established pattern: - namespace Domain\Authors - Constructor: __construct($db) storing Medoo instance - simpleList(): select from pp_authors, return array (from admin get_simple_list) - authorDetails($authorId): get from pp_authors + select pp_authors_langs, return with ['languages'][$lang_id] sub-array - authorSave($authorId, $author, $image, $description): insert/update pp_authors + pp_authors_langs with multi-language support. Same pattern as ScontainersRepository: query pp_langs for active languages, handle single vs multi lang arrays. Call \S::delete_cache() after. - authorDelete($authorId): delete from pp_authors, call \S::delete_cache(), return result - authorByLang($authorId, $langId): cached read using \Shared\Cache\CacheHandler::fetch("get_single_author:$authorId"). Get from pp_authors + pp_authors_langs for specific lang. Cache and return. Note: cache key does NOT include langId (matching original front factory).
Create Domain\Newsletter\NewsletterRepository following same pattern:
- namespace Domain\Newsletter
- Constructor: __construct($db)
- emailsImport($emails): parse comma/newline separated emails, validate with filter_var, insert unique into pp_newsletter. Return count of imported.
- isAdminTemplate($templateId): check if template exists in pp_newsletter_templates where id and admin=1. Return boolean.
- templateDelete($templateId): delete from pp_newsletter_templates where id. Return result.
- send($dates, $template, $onlyOnce): insert into pp_newsletter_send for each subscriber email from pp_newsletter. If $onlyOnce, check pp_newsletter_send for existing entries. Complex logic — replicate exactly from admin factory.
- templateDetails($templateId): get single template from pp_newsletter_templates.
- templateSave($id, $name, $text): insert/update pp_newsletter_templates. Call \S::delete_cache().
- templatesList(): select all from pp_newsletter_templates ordered.
- unsubscribe($hash): update pp_newsletter set status=0 where hash=$hash. Return result.
- confirm($hash): update pp_newsletter set status=1 where hash=$hash. Return result.
- newsletterSend($limit): select from pp_newsletter_send with limit, send emails via loop, delete sent entries. Replicate exactly from front factory.
- getHash($email): select hash from pp_newsletter where email. Return hash or false.
- signin($email): insert into pp_newsletter with email, hash (md5), status=0. Return result or hash.
- getTemplate($templateName): get template from pp_newsletter_templates where name=$templateName. Return template.
- signout($email): delete from pp_newsletter where email=$email. Return result.

IMPORTANT:
- PHP < 8.0 compatible
- Replicate logic EXACTLY from factory files — read them first
- Multi-language save pattern same as Phase 3 repos
- Keep all \S::delete_cache() calls where they exist in originals
- Newsletter send() and newsletterSend() are complex — read carefully and replicate precisely
php -l autoload/Domain/Authors/AuthorsRepository.php && php -l autoload/Domain/Newsletter/NewsletterRepository.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.Authors.php, autoload/admin/factory/class.Newsletter.php, autoload/front/factory/class.Authors.php, autoload/front/factory/class.Newsletter.php Convert all 4 factory files to thin wrappers using Phase 3 pattern: ```php public static function method_name($args) { global $mdb; $repo = new \Domain\Authors\AuthorsRepository($mdb); return $repo->methodName($args); } ```
admin\factory\Authors:
- get_simple_list() → $repo->simpleList()
- delete_author($id_author) → $repo->authorDelete($id_author)
- save_author($id_author, $author, $image, $description) → $repo->authorSave($id_author, $author, $image, $description)

admin\factory\Newsletter:
- emails_import($emails) → $repo->emailsImport($emails)
- is_admin_template($template_id) → $repo->isAdminTemplate($template_id)
- newsletter_template_delete($template_id) → $repo->templateDelete($template_id)
- send($dates, $template, $only_once) → $repo->send($dates, $template, $only_once)
- email_template_detalis($id_template) → $repo->templateDetails($id_template)
- template_save($id, $name, $text) → $repo->templateSave($id, $name, $text)
- templates_list() → $repo->templatesList()

front\factory\Authors:
- get_single_author($id_author) → global $mdb; $repo = new \Domain\Authors\AuthorsRepository($mdb); return $repo->authorByLang($id_author, null);
  Note: front factory uses global $lang but the cache key doesn't include lang — pass null or handle in repo. Check original carefully.

front\factory\Newsletter:
- newsletter_unsubscribe($hash) → $repo->unsubscribe($hash)
- newsletter_confirm($hash) → $repo->confirm($hash)
- newsletter_send($limit = 5) → $repo->newsletterSend($limit)
- get_hash($email) → $repo->getHash($email)
- newsletter_signin($email) → $repo->signin($email)
- get_template($template_name) → $repo->getTemplate($template_name)
- newsletter_signout($email) → $repo->signout($email)

IMPORTANT:
- Keep namespaces and method signatures IDENTICAL
- Read each file first before editing
- Each method = thin 3-line wrapper
php -l autoload/admin/factory/class.Authors.php && php -l autoload/admin/factory/class.Newsletter.php && php -l autoload/front/factory/class.Authors.php && php -l autoload/front/factory/class.Newsletter.php AC-3 and AC-4 satisfied: All legacy factories delegate to Domain repositories

DO NOT CHANGE

  • autoload/autoloader.php
  • composer.json
  • autoload/admin/controls/ (admin controllers — later phases)
  • autoload/admin/view/ (admin views — later phases)
  • autoload/front/view/ (front views — later phases)
  • Any existing Domain\ repositories (Articles, Languages, Layouts, Pages, Settings, User, Scontainers, Banners)

SCOPE LIMITS

  • Only factory → repository migration
  • No new Composer dependencies
  • No database schema changes
Before declaring plan complete: - [ ] php -l passes for all 6 files (2 new + 4 modified) - [ ] AuthorsRepository has: simpleList, authorDetails, authorSave, authorDelete, authorByLang - [ ] NewsletterRepository has: emailsImport, isAdminTemplate, templateDelete, send, templateDetails, templateSave, templatesList, unsubscribe, confirm, newsletterSend, getHash, signin, getTemplate, signout - [ ] All 4 factory files are thin wrappers (no direct $mdb usage) - [ ] No PHP 8.0+ syntax used - [ ] \S::delete_cache() calls preserved where originals had them

<success_criteria>

  • All tasks completed
  • All verification checks pass
  • Zero regression — factory method signatures unchanged
  • Domain repositories follow established pattern </success_criteria>
After completion, create `.paul/phases/04-domain-authors-newsletter/04-01-SUMMARY.md`