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>
This commit is contained in:
@@ -7,7 +7,7 @@ Autorski system CMS z panelem administracyjnym (17 modułów admin, 13 modułów
|
||||
Autorski system CMS umożliwiający zarządzanie treściami i stronami internetowymi.
|
||||
|
||||
## Already Completed
|
||||
- Domain (8 repos): Articles, Languages, Layouts, Pages, Settings, User, Scontainers, Banners
|
||||
- Domain (10 repos): Articles, Languages, Layouts, Pages, Settings, User, Scontainers, Banners, Authors, Newsletter
|
||||
- Shared (7 modules): Cache, Helpers, Html, Image, Tpl, Email, Security
|
||||
- Form Edit System: FormEditViewModel, multi-tab, validation, persistence
|
||||
- PHPUnit base: Bootstrap, 3 test files
|
||||
@@ -46,4 +46,4 @@ Autorski system CMS umożliwiający zarządzanie treściami i stronami interneto
|
||||
|
||||
---
|
||||
*Created: 2026-04-04*
|
||||
*Last updated: 2026-04-04 after Phase 3*
|
||||
*Last updated: 2026-04-04 after Phase 4*
|
||||
|
||||
@@ -6,7 +6,7 @@ Pełna refaktoryzacja cmsPRO do architektury DDD wzorowanej na shopPRO. Wzorzec:
|
||||
## Current Milestone
|
||||
**v0.1 Refaktoryzacja** (v0.1.0)
|
||||
Status: In progress
|
||||
Phases: 3 of 19 complete
|
||||
Phases: 4 of 19 complete
|
||||
|
||||
## Already Completed (before PAUL)
|
||||
- **Domain (6 repos):** Articles, Languages, Layouts, Pages, Settings, User
|
||||
@@ -21,7 +21,7 @@ Phases: 3 of 19 complete
|
||||
| 1 | Infrastructure & Autoloader | 1 | Complete | 2026-04-04 |
|
||||
| 2 | Shared: Email + Security | 1 | Complete | 2026-04-04 |
|
||||
| 3 | Domain: Scontainers + Banners | 1 | Complete | 2026-04-04 |
|
||||
| 4 | Domain: Authors + Newsletter | 1 | Not started | - |
|
||||
| 4 | Domain: Authors + Newsletter | 1 | Complete | 2026-04-04 |
|
||||
| 5 | Domain: SeoAdditional + Cron + Releases | 1 | Not started | - |
|
||||
| 6 | Admin: Base Infrastructure | 1 | Not started | - |
|
||||
| 7 | Admin: Articles + ArticlesArchive | 1 | Not started | - |
|
||||
|
||||
@@ -5,18 +5,18 @@
|
||||
See: .paul/PROJECT.md (updated 2026-04-04)
|
||||
|
||||
**Core value:** Autorski system CMS umożliwiający zarządzanie treściami i stronami internetowymi.
|
||||
**Current focus:** Phase 3 complete — ready for Phase 4
|
||||
**Current focus:** Phase 4 complete — ready for Phase 5
|
||||
|
||||
## Current Position
|
||||
|
||||
Milestone: v0.1 Refaktoryzacja
|
||||
Phase: 3 of 19 (Domain: Scontainers + Banners) — Complete
|
||||
Plan: 03-01 complete
|
||||
Phase: 4 of 19 (Domain: Authors + Newsletter) — Complete
|
||||
Plan: 04-01 complete
|
||||
Status: Loop closed, ready for next PLAN
|
||||
Last activity: 2026-04-04 — Phase 3 complete, UNIFY done
|
||||
Last activity: 2026-04-04 — Phase 4 complete, UNIFY done
|
||||
|
||||
Progress:
|
||||
- Milestone: [▓▓░░░░░░░░] 15%
|
||||
- Milestone: [▓▓░░░░░░░░] 20%
|
||||
|
||||
## Loop Position
|
||||
|
||||
@@ -29,8 +29,8 @@ PLAN ──▶ APPLY ──▶ UNIFY
|
||||
## Performance Metrics
|
||||
|
||||
**Velocity:**
|
||||
- Total plans completed: 3
|
||||
- Total execution time: ~20min
|
||||
- Total plans completed: 4
|
||||
- Total execution time: ~22min
|
||||
|
||||
**By Phase:**
|
||||
|
||||
@@ -39,6 +39,7 @@ PLAN ──▶ APPLY ──▶ UNIFY
|
||||
| 01-infrastructure | 1/1 | ~10min | ~10min |
|
||||
| 02-shared-email-security | 1/1 | ~8min | ~8min |
|
||||
| 03-domain-scontainers-banners | 1/1 | ~2min | ~2min |
|
||||
| 04-domain-authors-newsletter | 1/1 | ~2min | ~2min |
|
||||
|
||||
## Accumulated Context
|
||||
|
||||
@@ -50,6 +51,7 @@ PLAN ──▶ APPLY ──▶ UNIFY
|
||||
- Wrapper delegation: factory creates new repo per call (no singleton)
|
||||
- Front repos: $lang[0] passed explicitly, repos don't use globals
|
||||
- Front caching: migrated from \Cache:: to \Shared\Cache\CacheHandler::
|
||||
- Newsletter: globals ($settings, $lang) passed as explicit params to repo methods
|
||||
|
||||
### Deferred Issues
|
||||
None.
|
||||
@@ -60,9 +62,9 @@ None.
|
||||
## Session Continuity
|
||||
|
||||
Last session: 2026-04-04
|
||||
Stopped at: Phase 3 complete, loop closed
|
||||
Next action: Run /paul:plan for Phase 4 (Domain: Authors + Newsletter)
|
||||
Resume file: .paul/phases/03-domain-scontainers-banners/03-01-SUMMARY.md
|
||||
Stopped at: Phase 4 complete, loop closed
|
||||
Next action: Run /paul:plan for Phase 5 (Domain: SeoAdditional + Cron + Releases)
|
||||
Resume file: .paul/phases/04-domain-authors-newsletter/04-01-SUMMARY.md
|
||||
|
||||
---
|
||||
*STATE.md — Updated after every significant action*
|
||||
|
||||
216
.paul/phases/04-domain-authors-newsletter/04-01-PLAN.md
Normal file
216
.paul/phases/04-domain-authors-newsletter/04-01-PLAN.md
Normal file
@@ -0,0 +1,216 @@
|
||||
---
|
||||
phase: 04-domain-authors-newsletter
|
||||
plan: 01
|
||||
type: execute
|
||||
wave: 1
|
||||
depends_on: []
|
||||
files_modified:
|
||||
- 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
|
||||
autonomous: true
|
||||
delegation: auto
|
||||
---
|
||||
|
||||
<objective>
|
||||
## 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
|
||||
</objective>
|
||||
|
||||
<context>
|
||||
## 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)
|
||||
</context>
|
||||
|
||||
<acceptance_criteria>
|
||||
|
||||
## AC-1: AuthorsRepository exists with all methods
|
||||
```gherkin
|
||||
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
|
||||
```gherkin
|
||||
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
|
||||
```gherkin
|
||||
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
|
||||
```gherkin
|
||||
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>
|
||||
|
||||
<tasks>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 1: Create AuthorsRepository and NewsletterRepository</name>
|
||||
<files>autoload/Domain/Authors/AuthorsRepository.php, autoload/Domain/Newsletter/NewsletterRepository.php</files>
|
||||
<action>
|
||||
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
|
||||
</action>
|
||||
<verify>php -l autoload/Domain/Authors/AuthorsRepository.php && php -l autoload/Domain/Newsletter/NewsletterRepository.php</verify>
|
||||
<done>AC-1 and AC-2 satisfied: Both repositories exist with all methods, use injected $db</done>
|
||||
</task>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 2: Convert legacy factories to wrapper delegation</name>
|
||||
<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</files>
|
||||
<action>
|
||||
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
|
||||
</action>
|
||||
<verify>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</verify>
|
||||
<done>AC-3 and AC-4 satisfied: All legacy factories delegate to Domain repositories</done>
|
||||
</task>
|
||||
|
||||
</tasks>
|
||||
|
||||
<boundaries>
|
||||
|
||||
## 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
|
||||
|
||||
</boundaries>
|
||||
|
||||
<verification>
|
||||
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
|
||||
</verification>
|
||||
|
||||
<success_criteria>
|
||||
- All tasks completed
|
||||
- All verification checks pass
|
||||
- Zero regression — factory method signatures unchanged
|
||||
- Domain repositories follow established pattern
|
||||
</success_criteria>
|
||||
|
||||
<output>
|
||||
After completion, create `.paul/phases/04-domain-authors-newsletter/04-01-SUMMARY.md`
|
||||
</output>
|
||||
111
.paul/phases/04-domain-authors-newsletter/04-01-SUMMARY.md
Normal file
111
.paul/phases/04-domain-authors-newsletter/04-01-SUMMARY.md
Normal file
@@ -0,0 +1,111 @@
|
||||
---
|
||||
phase: 04-domain-authors-newsletter
|
||||
plan: 01
|
||||
subsystem: domain
|
||||
tags: [medoo, repository, authors, newsletter, wrapper-delegation]
|
||||
|
||||
requires:
|
||||
- phase: 01-infrastructure
|
||||
provides: PSR-4 autoloader for Domain\ namespace
|
||||
|
||||
provides:
|
||||
- Domain\Authors\AuthorsRepository
|
||||
- Domain\Newsletter\NewsletterRepository
|
||||
|
||||
affects: [phase-10-admin-banners-authors-scontainers, phase-11-admin-newsletter-emails-seoadditional]
|
||||
|
||||
tech-stack:
|
||||
added: []
|
||||
patterns: [wrapper-delegation, globals-to-parameters]
|
||||
|
||||
key-files:
|
||||
created:
|
||||
- autoload/Domain/Authors/AuthorsRepository.php
|
||||
- autoload/Domain/Newsletter/NewsletterRepository.php
|
||||
modified:
|
||||
- autoload/admin/factory/class.Authors.php
|
||||
- autoload/admin/factory/class.Newsletter.php
|
||||
- autoload/front/factory/class.Authors.php
|
||||
- autoload/front/factory/class.Newsletter.php
|
||||
|
||||
key-decisions:
|
||||
- "Newsletter methods using global $settings/$lang now take them as explicit parameters"
|
||||
- "authorByLang cache key preserved from original (no langId in key)"
|
||||
|
||||
patterns-established:
|
||||
- "Globals-to-parameters: when repo method needs $settings or $lang, wrapper passes them explicitly"
|
||||
|
||||
duration: ~2min
|
||||
started: 2026-04-04T00:00:00Z
|
||||
completed: 2026-04-04T00:00:00Z
|
||||
---
|
||||
|
||||
# Phase 4 Plan 01: Authors + Newsletter Repositories Summary
|
||||
|
||||
**Domain repositories for Authors (5 methods) and Newsletter (14 methods) with wrapper delegation and globals-to-parameters pattern.**
|
||||
|
||||
## Performance
|
||||
|
||||
| Metric | Value |
|
||||
|--------|-------|
|
||||
| Duration | ~2min |
|
||||
| Tasks | 2 completed (delegated) |
|
||||
| Files created | 2 |
|
||||
| Files modified | 4 |
|
||||
|
||||
## Acceptance Criteria Results
|
||||
|
||||
| Criterion | Status | Notes |
|
||||
|-----------|--------|-------|
|
||||
| AC-1: AuthorsRepository exists with all methods | Pass | 5 methods, 156 lines |
|
||||
| AC-2: NewsletterRepository exists with all methods | Pass | 14 methods, 281 lines |
|
||||
| AC-3: Legacy admin factories delegate to repositories | Pass | 11 static methods → wrappers |
|
||||
| AC-4: Legacy front factories delegate to repositories | Pass | 8 static methods → wrappers, globals passed as params |
|
||||
|
||||
## Accomplishments
|
||||
|
||||
- Created AuthorsRepository with simpleList, authorDetails, authorSave, authorDelete, authorByLang
|
||||
- Created NewsletterRepository with full subscriber lifecycle + template CRUD + sending
|
||||
- Established globals-to-parameters pattern for methods needing $settings/$lang
|
||||
|
||||
## Files Created/Modified
|
||||
|
||||
| File | Change | Purpose |
|
||||
|------|--------|---------|
|
||||
| `autoload/Domain/Authors/AuthorsRepository.php` | Created | Domain repository for authors CRUD + cached front read |
|
||||
| `autoload/Domain/Newsletter/NewsletterRepository.php` | Created | Domain repository for newsletter subscriber lifecycle, templates, sending |
|
||||
| `autoload/admin/factory/class.Authors.php` | Modified | Wrapper: 4 methods delegate to AuthorsRepository |
|
||||
| `autoload/admin/factory/class.Newsletter.php` | Modified | Wrapper: 7 methods delegate to NewsletterRepository |
|
||||
| `autoload/front/factory/class.Authors.php` | Modified | Wrapper: 1 method delegates |
|
||||
| `autoload/front/factory/class.Newsletter.php` | Modified | Wrapper: 7 methods delegate, passing $settings/$lang |
|
||||
|
||||
## Decisions Made
|
||||
|
||||
| Decision | Rationale | Impact |
|
||||
|----------|-----------|--------|
|
||||
| Globals as parameters for newsletterSend/signin | Repos should not depend on globals | Front wrappers pass $settings, $lang explicitly |
|
||||
| Preserve original cache key for authorByLang | Backward compatibility with existing cache | Cache key "get_single_author:$id" without langId |
|
||||
|
||||
## Deviations from Plan
|
||||
|
||||
None — plan executed exactly as written.
|
||||
|
||||
## Issues Encountered
|
||||
|
||||
None.
|
||||
|
||||
## Next Phase Readiness
|
||||
|
||||
**Ready:**
|
||||
- Domain\Authors and Domain\Newsletter available for Admin controllers (Phases 10, 11)
|
||||
- All Domain repos for phases 3-4 complete
|
||||
|
||||
**Concerns:**
|
||||
- None
|
||||
|
||||
**Blockers:**
|
||||
- None
|
||||
|
||||
---
|
||||
*Phase: 04-domain-authors-newsletter, Plan: 01*
|
||||
*Completed: 2026-04-04*
|
||||
Reference in New Issue
Block a user