update
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
# shopPRO — Koniec Pracy (release workflow)
|
||||
# shopPRO - Koniec Pracy (release workflow)
|
||||
|
||||
Execute the full release workflow for shopPRO. This is a sequential pipeline — each step depends on the previous one succeeding. Stop and report if any step fails.
|
||||
Execute the full release workflow for shopPRO. This is a sequential pipeline - each step depends on the previous one succeeding. Stop and report if any step fails.
|
||||
|
||||
## Step 1: Run tests
|
||||
|
||||
@@ -8,7 +8,7 @@ Run the full PHPUnit test suite:
|
||||
```bash
|
||||
php phpunit.phar
|
||||
```
|
||||
All tests must pass. If any test fails, stop here — do not proceed to commit. Report the failures and wait for instructions.
|
||||
All tests must pass. If any test fails, stop here - do not proceed to commit. Report the failures and wait for instructions.
|
||||
|
||||
## Step 1b: SonarQube scan
|
||||
|
||||
@@ -19,20 +19,20 @@ sonar-scanner
|
||||
|
||||
After the scan completes, query the SonarQube issues via MCP tool `mcp__sonarqube__issues` with `project_key: "shopPRO"` and `resolved: false`. Fetch all open issues (bugs, vulnerabilities, code smells).
|
||||
|
||||
Then open `docs/TODO.md` and append the found issues at the bottom under a new section:
|
||||
Then open `.paul/docs/TODO.md` and append the found issues at the bottom under a new section:
|
||||
|
||||
```markdown
|
||||
## SonarQube — {VERSION} ({DATE})
|
||||
## SonarQube - {VERSION} ({DATE})
|
||||
|
||||
- [ ] [SEVERITY] FILENAME:LINE — description (rule)
|
||||
- [ ] [SEVERITY] FILENAME:LINE - description (rule)
|
||||
- [ ] ...
|
||||
```
|
||||
|
||||
Rules:
|
||||
- Only add issues that are NOT already present in `docs/TODO.md`
|
||||
- Only add issues that are NOT already present in `.paul/docs/TODO.md`
|
||||
- Group by type: first Bugs/Vulnerabilities, then Code Smells
|
||||
- Skip INFO severity Code Smells — only include MINOR and above
|
||||
- If there are no new issues, write: `## SonarQube — {VERSION} — brak nowych issues`
|
||||
- Skip INFO severity Code Smells - only include MINOR and above
|
||||
- If there are no new issues, write: `## SonarQube - {VERSION} - brak nowych issues`
|
||||
|
||||
## Step 2: Determine version
|
||||
|
||||
@@ -40,24 +40,24 @@ Read the latest git tag to determine the current version number:
|
||||
```bash
|
||||
git tag --sort=-v:refname | head -1
|
||||
```
|
||||
The new version is the previous version incremented by 1 (e.g., v0.333 → v0.334). Use this version number throughout the remaining steps.
|
||||
The new version is the previous version incremented by 1 (e.g., v0.333 -> v0.334). Use this version number throughout the remaining steps.
|
||||
|
||||
## Step 3: Update documentation
|
||||
|
||||
Update these docs files **only if** changes in this session affect them:
|
||||
Update these docs files **only if** changes in this session affect them.
|
||||
Do not update files in root `docs/` directory.
|
||||
|
||||
| File | When to update |
|
||||
|------|---------------|
|
||||
| `docs/CHANGELOG.md` | Always — add a new version entry at the top describing what changed |
|
||||
| `docs/TESTING.md` | If tests were added/removed — update test count and structure |
|
||||
| `CLAUDE.md` | If test count changed — update the "Current suite" line |
|
||||
| `docs/DATABASE_STRUCTURE.md` | If database schema changed |
|
||||
| `docs/PROJECT_STRUCTURE.md` | If architecture/files changed significantly |
|
||||
| `docs/FORM_EDIT_SYSTEM.md` | If form system was modified |
|
||||
| `.paul/docs/CHANGELOG.md` | Always - add a new version entry at the top describing what changed |
|
||||
| `.paul/docs/TESTING.md` | If tests were added/removed - update test count and structure |
|
||||
| `.paul/docs/DB_SCHEMA.md` | If database schema changed |
|
||||
| `.paul/docs/ARCHITECTURE.md` | If architecture/files changed significantly |
|
||||
| `.paul/docs/FORMS.md` | If form system was modified |
|
||||
|
||||
## Step 4: SQL migrations
|
||||
|
||||
If database schema changes were made, create a migration file at `migrations/{version}.sql` (e.g., `migrations/0.334.sql`). Do NOT put SQL files in `updates/` — the build script reads from `migrations/` automatically.
|
||||
If database schema changes were made, create a migration file at `migrations/{version}.sql` (e.g., `migrations/0.334.sql`). Do NOT put SQL files in `updates/` - the build script reads from `migrations/` automatically.
|
||||
|
||||
If no DB changes were made, skip this step.
|
||||
|
||||
|
||||
@@ -2,11 +2,11 @@
|
||||
|
||||
## What This Is
|
||||
|
||||
Autorski silnik sklepu internetowego pisany od podstaw — odpowiednik WooCommerce lub PrestaShop, ale bez zależności od zewnętrznych platform. Składa się z panelu administratora (zarządzanie zamówieniami, produktami, klientami) oraz części frontowej dla klienta końcowego.
|
||||
Autorski silnik sklepu internetowego pisany od podstaw - odpowiednik WooCommerce lub PrestaShop, ale bez zależności od zewnętrznych platform. Składa się z panelu administratora (zarządzanie zamówieniami, produktami, klientami) oraz części frontowej dla klienta końcowego.
|
||||
|
||||
## Core Value
|
||||
|
||||
Właściciel sklepu internetowego ma pełną kontrolę nad sprzedażą online — produktami, zamówieniami i klientami — w jednym spójnym systemie pisanym od podstaw, bez narzutów zewnętrznych platform.
|
||||
Właściciel sklepu internetowego ma pełną kontrolę nad sprzedażą online - produktami, zamówieniami i klientami - w jednym spójnym systemie pisanym od podstaw, bez narzutów zewnętrznych platform.
|
||||
|
||||
## Current State
|
||||
|
||||
@@ -20,16 +20,16 @@ Właściciel sklepu internetowego ma pełną kontrolę nad sprzed
|
||||
|
||||
### Validated (Shipped)
|
||||
|
||||
- [x] Panel administratora — zarządzanie produktami, kategoriami, atrybutami
|
||||
- [x] Panel administratora — zarządzanie zamówieniami
|
||||
- [x] Panel administratora — zarządzanie klientami
|
||||
- [x] Część frontowa — przeglądanie i kupowanie produktów
|
||||
- [x] Koszyk i składanie zamówień
|
||||
- [x] Integracje płatności i dostaw
|
||||
- [x] Panel administratora - zarządzanie produktami, kategoriami, atrybutami
|
||||
- [x] Panel administratora - zarządzanie zamówieniami
|
||||
- [x] Panel administratora - zarządzanie klientami
|
||||
- [x] Część frontowa - przeglądanie i kupowanie produktów
|
||||
- [x] Koszyk i składanie zamówień
|
||||
- [x] Integracje płatności i dostaw
|
||||
- [x] REST API (ordersPRO + Ekomi)
|
||||
- [x] Redis caching
|
||||
- [x] Ochrona przed podwójnym składaniem zamówienia
|
||||
- [x] Domain-Driven Architecture (migracja z legacy zakończona)
|
||||
- [x] Ochrona przed podwójnym składaniem zamówienia
|
||||
- [x] Domain-Driven Architecture (migracja z legacy zakończona)
|
||||
|
||||
### Active (In Progress)
|
||||
|
||||
@@ -41,16 +41,16 @@ Właściciel sklepu internetowego ma pełną kontrolę nad sprzed
|
||||
|
||||
### Out of Scope
|
||||
|
||||
- Multitenancy (wiele sklepów w jednej instancji) — nie planowane
|
||||
- Multitenancy (wiele sklepów w jednej instancji) - nie planowane
|
||||
|
||||
## Target Users
|
||||
|
||||
**Primary:** Właściciel/administrator sklepu internetowego
|
||||
- ZarzÄ…dza produktami, zamĂłwieniami, klientami przez panel admina
|
||||
- Potrzebuje niezawodnego, szybkiego narzędzia bez zbędnych zależności
|
||||
**Primary:** Właściciel/administrator sklepu internetowego
|
||||
- Zarządza produktami, zamówieniami, klientami przez panel admina
|
||||
- Potrzebuje niezawodnego, szybkiego narzędzia bez zbędnych zależności
|
||||
|
||||
**Secondary:** Klient końcowy sklepu
|
||||
- Przegląda produkty, dodaje do koszyka, składa zamówienia
|
||||
**Secondary:** Klient końcowy sklepu
|
||||
- Przegląda produkty, dodaje do koszyka, składa zamówienia
|
||||
|
||||
## Context
|
||||
|
||||
@@ -58,26 +58,26 @@ Właściciel sklepu internetowego ma pełną kontrolę nad sprzed
|
||||
- PHP 7.4+ (produkcja: PHP < 8.0)
|
||||
- Medoo ORM (`$mdb`), Redis caching
|
||||
- Domain-Driven Design z Dependency Injection
|
||||
- PHPUnit 9.6, 810+ testĂłw
|
||||
- PHPUnit 9.6, 810+ testów
|
||||
- Namespace: `\Domain\`, `\admin\`, `\front\`, `\api\`, `\Shared\`
|
||||
|
||||
## Constraints
|
||||
|
||||
### Technical Constraints
|
||||
- PHP < 8.0 na produkcji (brak `match`, named arguments, union types)
|
||||
- Medoo ORM — prepared statements bez wyjątków
|
||||
- Medoo ORM - prepared statements bez wyjątków
|
||||
- Redis wymagany dla cache
|
||||
|
||||
### Business Constraints
|
||||
- System wdraĹĽany u klientĂłw jako update package (ZIP)
|
||||
- System wdrażany u klientów jako update package (ZIP)
|
||||
|
||||
## Key Decisions
|
||||
|
||||
| Decision | Rationale | Date | Status |
|
||||
|----------|-----------|------|--------|
|
||||
| DDD + DI zamiast legacy architektury | Testowalność, separacja odpowiedzialności | 2025 | Active |
|
||||
| PHP < 8.0 kompatybilność | Klienci na starszych serwerach | 2025 | Active |
|
||||
| Własny silnik zamiast frameworka | Pełna kontrola, brak narzutów | - | Active |
|
||||
| DDD + DI zamiast legacy architektury | Testowalność, separacja odpowiedzialności | 2025 | Active |
|
||||
| PHP < 8.0 kompatybilność | Klienci na starszych serwerach | 2025 | Active |
|
||||
| Własny silnik zamiast frameworka | Pełna kontrola, brak narzutów | - | Active |
|
||||
| `id` w tabbed FormEdit przez `hiddenFields` | Zapobiega insert zamiast update przy edycji encji | 2026-04-18 | Active |
|
||||
|
||||
## Success Metrics
|
||||
@@ -94,7 +94,7 @@ Właściciel sklepu internetowego ma pełną kontrolę nad sprzed
|
||||
| Backend | PHP 7.4+ | < 8.0 na produkcji |
|
||||
| ORM | Medoo | `$mdb` global |
|
||||
| Cache | Redis | CacheHandler singleton |
|
||||
| Frontend | HTML/CSS/JS | Własny silnik szablonów (Tpl) |
|
||||
| Frontend | HTML/CSS/JS | Własny silnik szablonów (Tpl) |
|
||||
| Auth | Sesje PHP | CSRF, XSS protection |
|
||||
| Testy | PHPUnit 9.6 | phpunit.phar |
|
||||
|
||||
@@ -103,15 +103,14 @@ Właściciel sklepu internetowego ma pełną kontrolę nad sprzed
|
||||
See: .paul/SPECIAL-FLOWS.md
|
||||
|
||||
Quick Reference:
|
||||
- /feature-dev → Nowe funkcje, większe zmiany (required)
|
||||
- /koniec-pracy → Release, update package (required)
|
||||
- /frontend-design → Komponenty UI, szablony widoków
|
||||
- /code-review → Przegląd kodu przed release
|
||||
- /simplify → Upraszczanie po implementacji
|
||||
- /claude-md-improver → Utrzymanie CLAUDE.md
|
||||
- /zapisz + /wznow → Zapis i wznowienie sesji
|
||||
- /feature-dev -> Nowe funkcje, większe zmiany (required)
|
||||
- /koniec-pracy -> Release, update package (required)
|
||||
- /frontend-design -> Komponenty UI, szablony widoków
|
||||
- /code-review -> Przegląd kodu przed release
|
||||
- /simplify -> Upraszczanie po implementacji
|
||||
- /claude-md-improver -> Utrzymanie CLAUDE.md
|
||||
- /zapisz + /wznow -> Zapis i wznowienie sesji
|
||||
|
||||
---
|
||||
*PROJECT.md — Updated when requirements or context change*
|
||||
*PROJECT.md - Updated when requirements or context change*
|
||||
*Last updated: 2026-04-18 after Phase 15*
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
## Overview
|
||||
|
||||
shopPRO to autorski silnik sklepu internetowego rozwijany iteracyjnie. Projekt jest już na produkcji (v0.333) — roadmap obejmuje planowane funkcje i usprawnienia kolejnych wersji.
|
||||
shopPRO to autorski silnik sklepu internetowego rozwijany iteracyjnie. Projekt jest już na produkcji (v0.333) — roadmap obejmuje planowane funkcje i usprawnienia kolejnych wersji.
|
||||
|
||||
## Current Milestone
|
||||
|
||||
@@ -16,25 +16,25 @@ Phases: 4 of 4 complete
|
||||
|-------|------|-------|--------|-----------|
|
||||
| 1 | Sensitive data logging fix | 1 | Done | 2026-03 |
|
||||
| 2 | Path traversal + XSS escaping | 1 | Done | 2026-03 (v0.335) |
|
||||
| 3 | Error handling w krytycznych ścieżkach | 1 | Done | 2026-03 (v0.336) |
|
||||
| 4 | CSRF protection — admin panel forms | 1 | Applied | 2026-03 (v0.337) |
|
||||
| 5 | Order bugs fix — duplicate + COD status | 1 | Applied | 2026-03 (v0.338) |
|
||||
| 3 | Error handling w krytycznych ścieżkach | 1 | Done | 2026-03 (v0.336) |
|
||||
| 4 | CSRF protection — admin panel forms | 1 | Applied | 2026-03 (v0.337) |
|
||||
| 5 | Order bugs fix — duplicate + COD status | 1 | Applied | 2026-03 (v0.338) |
|
||||
|
||||
## Next Milestone
|
||||
|
||||
**Tech debt — Integrations refactoring**
|
||||
**Tech debt — Integrations refactoring**
|
||||
Status: Planning
|
||||
|
||||
| Phase | Name | Plans | Status | Completed |
|
||||
|-------|------|-------|--------|-----------|
|
||||
| 6 | IntegrationsRepository split → ApiloRepository | 2 | Done | 2026-03 |
|
||||
| 6 | IntegrationsRepository split → ApiloRepository | 2 | Done | 2026-03 |
|
||||
|
||||
## Hotfix
|
||||
|
||||
| Phase | Name | Plans | Status | Completed |
|
||||
|-------|------|-------|--------|-----------|
|
||||
| 7 | Coupon Fatal Error — order placement crash | 1 | Done | 2026-03-15 |
|
||||
| 8 | Apilo orders not sending — diagnoza i naprawa | 1 | Done | 2026-03-16 |
|
||||
| 7 | Coupon Fatal Error — order placement crash | 1 | Done | 2026-03-15 |
|
||||
| 8 | Apilo orders not sending — diagnoza i naprawa | 1 | Done | 2026-03-16 |
|
||||
| 9 | Apilo email notification + infinite retry | 1 | Done | 2026-03-19 |
|
||||
| 15 | Scontainers edit saves as new record | 1 | Done | 2026-04-18 |
|
||||
|
||||
@@ -44,72 +44,72 @@ Status: Planning
|
||||
|-------|------|-------|--------|-----------|
|
||||
| 10 | Edycja personalizacji produktu w koszyku | 1 | Done | 2026-03-19 |
|
||||
| 11 | DataLayer GA4 analytics fix | 1 | Done | 2026-03-25 |
|
||||
| 12 | summaryView redirect fix — double order block | 1 | Done | 2026-03-25 |
|
||||
| 12 | summaryView redirect fix — double order block | 1 | Done | 2026-03-25 |
|
||||
| 13 | Basket logging + TTL token fix | 1 | Done | 2026-03-25 |
|
||||
| 14 | Custom fields delete bug — usunięcie wszystkich pól | 1 | Done | 2026-04-16 |
|
||||
| 14 | Custom fields delete bug — usunięcie wszystkich pól | 1 | Done | 2026-04-16 |
|
||||
|
||||
## Phase Details
|
||||
|
||||
### Phase 4 — CSRF protection
|
||||
### Phase 4 — CSRF protection
|
||||
|
||||
**Problem:** Brak tokenĂłw CSRF na formularzach panelu admina. State-changing POST endpointy (create/update/delete) sÄ… potencjalnie podatne na ataki CSRF.
|
||||
**Problem:** Brak tokenów CSRF na formularzach panelu admina. State-changing POST endpointy (create/update/delete) są potencjalnie podatne na ataki CSRF.
|
||||
|
||||
**Scope:** Dodanie CSRF tokenĂłw do formularzy i walidacji w panelu administracyjnym.
|
||||
**Scope:** Dodanie CSRF tokenów do formularzy i walidacji w panelu administracyjnym.
|
||||
|
||||
**Reference:** `.paul/codebase/concerns.md` — MEDIUM — Missing CSRF tokens
|
||||
**Reference:** `.paul/codebase/concerns.md` — MEDIUM — Missing CSRF tokens
|
||||
|
||||
### Phase 6 — IntegrationsRepository split
|
||||
### Phase 6 — IntegrationsRepository split
|
||||
|
||||
**Problem:** `IntegrationsRepository` ma 875 linii — miesza logikę generyczną (settings, logi, product linking) z logiką specyficzną dla Apilo (~650 linii). Narusza zasadę jednej odpowiedzialności.
|
||||
**Problem:** `IntegrationsRepository` ma 875 linii — miesza logikę generyczną (settings, logi, product linking) z logiką specyficzną dla Apilo (~650 linii). Narusza zasadę jednej odpowiedzialności.
|
||||
|
||||
**Scope:**
|
||||
- Plan 06-01: UtwĂłrz `ApiloRepository` z metodami apilo* (non-breaking)
|
||||
- Plan 06-02: Zmigruj konsumentów (IntegrationsController, ShopProductController, OrderAdminService, cron.php), usuń apilo* z IntegrationsRepository
|
||||
- Plan 06-01: Utwórz `ApiloRepository` z metodami apilo* (non-breaking)
|
||||
- Plan 06-02: Zmigruj konsumentów (IntegrationsController, ShopProductController, OrderAdminService, cron.php), usuń apilo* z IntegrationsRepository
|
||||
|
||||
---
|
||||
|
||||
### Phase 5 — Order bugs fix
|
||||
### Phase 5 — Order bugs fix
|
||||
|
||||
**Problem 1:** Zduplikowane zamówienia — klient widzi błąd i klika złóż zamówienie ponownie. Pierwsze zamówienie trafiło do bazy mimo błędu. Powrót do `/podsumowanie` regeneruje token i pozwala złożyć drugie zamówienie.
|
||||
**Problem 1:** Zduplikowane zamówienia — klient widzi błąd i klika złóż zamówienie ponownie. Pierwsze zamówienie trafiło do bazy mimo błędu. Powrót do `/podsumowanie` regeneruje token i pozwala złożyć drugie zamówienie.
|
||||
|
||||
**Problem 2:** Zamówienia COD (płatność przy odbiorze) dostają status "Zamówienie złożone" zamiast "Przyjęte do realizacji". Kod sprawdza hardkodowane `payment_id == 3`, które jest inne w tej instancji sklepu.
|
||||
**Problem 2:** Zamówienia COD (płatność przy odbiorze) dostają status "Zamówienie złożone" zamiast "Przyjęte do realizacji". Kod sprawdza hardkodowane `payment_id == 3`, które jest inne w tej instancji sklepu.
|
||||
|
||||
**Scope:** Guard w `summaryView()`, try-catch w `basketSave()`, kolumna `is_cod` w `pp_shop_payment_methods`, uĹĽycie flagi zamiast hardkodowanego ID.
|
||||
**Scope:** Guard w `summaryView()`, try-catch w `basketSave()`, kolumna `is_cod` w `pp_shop_payment_methods`, użycie flagi zamiast hardkodowanego ID.
|
||||
|
||||
---
|
||||
*Roadmap created: 2026-03-12*
|
||||
### Phase 11 — DataLayer GA4 analytics fix
|
||||
|
||||
**Problem:** Eventy dataLayer ecommerce (purchase, begin_checkout, view_item, add_to_cart) używają starego formatu UA (id/name zamiast item_id/item_name), brak currency w view_item, price:0 w purchase, brak eventu view_cart. Remarketing dynamiczny i konwersje GA4 nie działają poprawnie.
|
||||
### Phase 11 — DataLayer GA4 analytics fix
|
||||
|
||||
**Scope:** Poprawka 4 istniejÄ…cych eventĂłw do formatu GA4 + dodanie nowego eventu view_cart na stronie koszyka.
|
||||
**Problem:** Eventy dataLayer ecommerce (purchase, begin_checkout, view_item, add_to_cart) używają starego formatu UA (id/name zamiast item_id/item_name), brak currency w view_item, price:0 w purchase, brak eventu view_cart. Remarketing dynamiczny i konwersje GA4 nie działają poprawnie.
|
||||
|
||||
**Reference:** `poprawki_datalayer_projectpro.md` — audyt analityki z pomysloweprezenty.pl
|
||||
**Scope:** Poprawka 4 istniejących eventów do formatu GA4 + dodanie nowego eventu view_cart na stronie koszyka.
|
||||
|
||||
### Phase 12 — summaryView redirect fix
|
||||
**Reference:** `poprawki_datalayer_projectpro.md` — audyt analityki z pomysloweprezenty.pl
|
||||
|
||||
**Problem:** Po złożeniu pierwszego zamówienia, guard w `summaryView()` sprawdzał sesyjny `order-submit-last-order-id` i redirectował na stronę starego zamówienia. Blokował dostęp do `/koszyk-podsumowanie` dla kolejnych zamówień. Poprawka z instancji klienta (change.md) do wdrożenia globalnie.
|
||||
### Phase 12 — summaryView redirect fix
|
||||
|
||||
**Scope:** Usunięcie bloku redirect z `summaryView()` w `ShopBasketController.php`. Double-submit protection w `basketSave()` pozostaje bez zmian.
|
||||
**Problem:** Po złożeniu pierwszego zamówienia, guard w `summaryView()` sprawdzał sesyjny `order-submit-last-order-id` i redirectował na stronę starego zamówienia. Blokował dostęp do `/koszyk-podsumowanie` dla kolejnych zamówień. Poprawka z instancji klienta (change.md) do wdrożenia globalnie.
|
||||
|
||||
### Phase 13 — Basket logging + TTL token fix
|
||||
**Scope:** Usunięcie bloku redirect z `summaryView()` w `ShopBasketController.php`. Double-submit protection w `basketSave()` pozostaje bez zmian.
|
||||
|
||||
**Problem:** Brak logowania w basketSave() uniemożliwia diagnozę błędów zamówień. Token zamówienia jednorazowy — nadpisywany przy każdym wejściu na podsumowanie, co powoduje że druga karta, "wstecz" lub odświeżenie unieważnia formularz.
|
||||
### Phase 13 — Basket logging + TTL token fix
|
||||
|
||||
**Scope:** Dodanie metody logOrder() z 4 punktami logowania, zmiana tokena z jednorazowego na TTL 30 min, redirect przy błędzie tokena na /koszyk-podsumowanie zamiast /koszyk, nowy double-submit guard.
|
||||
**Problem:** Brak logowania w basketSave() uniemożliwia diagnozę błędów zamówień. Token zamówienia jednorazowy — nadpisywany przy każdym wejściu na podsumowanie, co powoduje że druga karta, "wstecz" lub odświeżenie unieważnia formularz.
|
||||
|
||||
### Phase 14 — Custom fields delete bug
|
||||
**Scope:** Dodanie metody logOrder() z 4 punktami logowania, zmiana tokena z jednorazowego na TTL 30 min, redirect przy błędzie tokena na /koszyk-podsumowanie zamiast /koszyk, nowy double-submit guard.
|
||||
|
||||
**Problem:** Usunięcie WSZYSTKICH dodatkowych pól z produktu nie działa. jQuery `.serialize()` nie wysyła klucza `custom_field_name[]` gdy nie ma żadnych pól → `array_key_exists('custom_field_name', $d)` w ProductRepository zwraca false → `saveCustomFields()` nigdy nie jest wywoływany → pola pozostają w bazie.
|
||||
### Phase 14 — Custom fields delete bug
|
||||
|
||||
**Problem:** Usunięcie WSZYSTKICH dodatkowych pól z produktu nie działa. jQuery `.serialize()` nie wysyła klucza `custom_field_name[]` gdy nie ma żadnych pól → `array_key_exists('custom_field_name', $d)` w ProductRepository zwraca false → `saveCustomFields()` nigdy nie jest wywoływany → pola pozostają w bazie.
|
||||
|
||||
**Scope:** Dodanie hidden markera `custom_field_name_present` w szablonie JS + zmiana warunku w ProductRepository na sprawdzanie tego markera. Test jednostkowy.
|
||||
|
||||
### Phase 15 - Scontainers edit saves as new record
|
||||
|
||||
**Problem:** Edycja kontenera statycznego (`/admin/scontainers/edit/id={id}`) zapisuje rekord jako nowy wpis zamiast aktualizacji. W praktyce podczas zapisu gubi sie `id` i repository wykonuje insert.
|
||||
**Problem:** Edycja kontenera statycznego (`/admin/scontainers/edit/id={id}`) zapisuje rekord jako nowy wpis zamiast aktualizacji. W praktyce podczas zapisu gubi się `id` i repository wykonuje insert.
|
||||
|
||||
**Scope:** Poprawic przekazywanie `id` w nowym flow formularza ScontainersController + dodac test regresyjny dla edycji, bez zmian globalnych w innych kontrolerach.
|
||||
**Scope:** Poprawić przekazywanie `id` w nowym flow formularza ScontainersController + dodać test regresyjny dla edycji, bez zmian globalnych w innych kontrolerach.
|
||||
|
||||
---
|
||||
*Last updated: 2026-04-18*
|
||||
|
||||
|
||||
@@ -1,37 +1,37 @@
|
||||
# Specialized Flows: shopPRO
|
||||
# Specialized Flows: shopPRO
|
||||
|
||||
## Project-Level Dependencies
|
||||
|
||||
| Work Type | Skill/Command | Priority | Kiedy używać |
|
||||
| Work Type | Skill/Command | Priority | Kiedy uzywac |
|
||||
|-----------|---------------|----------|--------------|
|
||||
| Komponenty UI, szablony widoków | /frontend-design | optional | Przy tworzeniu HTML/CSS |
|
||||
| Nowe funkcje, większe zmiany | /feature-dev | required | Przed implementacją fazy |
|
||||
| Przegląd kodu | /code-review | optional | Przed release / KONIEC PRACY |
|
||||
| Upraszczanie po zmianach | /simplify | optional | Po zakończeniu implementacji |
|
||||
| Utrzymanie CLAUDE.md | /claude-md-improver | optional | Co kilka faz / po dużych zmianach |
|
||||
| Release, budowanie update package | /koniec-pracy | required | Na koniec każdej sesji roboczej |
|
||||
| Zapis i wznowienie sesji | /zapisz + /wznow | optional | Na przerwę / powrót do pracy |
|
||||
| Komponenty UI, szablony widokow | /frontend-design | optional | Przy tworzeniu HTML/CSS |
|
||||
| Nowe funkcje, wieksze zmiany | /feature-dev | required | Przed implementacja fazy |
|
||||
| Przeglad kodu | /code-review | optional | Przed release / KONIEC PRACY |
|
||||
| Upraszczanie po zmianach | /simplify | optional | Po zakonczeniu implementacji |
|
||||
| Utrzymanie CLAUDE.md | /claude-md-improver | optional | Co kilka faz / po duzych zmianach |
|
||||
| Release, budowanie update package | /koniec-pracy | required | Na koniec kazdej sesji roboczej |
|
||||
| Zapis i wznowienie sesji | /zapisz + /wznow | optional | Na przerwe / powrot do pracy |
|
||||
|
||||
## Phase Overrides
|
||||
|
||||
Brak — domyślna konfiguracja obowiązuje dla wszystkich faz.
|
||||
Brak - domyslna konfiguracja obowiazuje dla wszystkich faz.
|
||||
|
||||
## Templates & Assets
|
||||
|
||||
| Asset Type | Location | When Used |
|
||||
|------------|----------|-----------|
|
||||
| CLAUDE.md | CLAUDE.md | Konwencje kodu, architektura, stack techniczny |
|
||||
| Struktura bazy | docs/DATABASE_STRUCTURE.md | Przy zmianach schematu DB |
|
||||
| Dokumentacja API | api-docs/api-reference.json | Przy zmianach API |
|
||||
| TODO | docs/TODO.md | Planowanie nowych funkcji |
|
||||
| Struktura bazy | .paul/docs/DB_SCHEMA.md | Przy zmianach schematu DB |
|
||||
| Dokumentacja API | .paul/docs/API.md | Przy zmianach API |
|
||||
| TODO | .paul/docs/TODO.md | Planowanie nowych funkcji |
|
||||
|
||||
## Verification (UNIFY)
|
||||
|
||||
Podczas UNIFY sprawdź:
|
||||
- `/feature-dev` — czy był użyty przed implementacją fazy?
|
||||
- `/koniec-pracy` — czy release został wykonany?
|
||||
Podczas UNIFY sprawdz:
|
||||
- `/feature-dev` - czy byl uzyty przed implementacja fazy?
|
||||
- `/koniec-pracy` - czy release zostal wykonany?
|
||||
|
||||
Braki dokumentuj w STATE.md (Deferred Issues), nie blokują UNIFY.
|
||||
Braki dokumentuj w STATE.md (Deferred Issues), nie blokuja UNIFY.
|
||||
|
||||
---
|
||||
*SPECIAL-FLOWS.md — Created: 2026-03-12*
|
||||
*SPECIAL-FLOWS.md - Created: 2026-03-12*
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
See: .paul/PROJECT.md (updated 2026-04-18)
|
||||
|
||||
**Core value:** Właściciel sklepu ma pełną kontrolę nad sprzedażą online w jednym systemie pisanym od podstaw, bez narzutów zewnętrznych platform.
|
||||
**Core value:** Właściciel sklepu ma pełną kontrolę nad sprzedażą online w jednym systemie pisanym od podstaw, bez narzutów zewnętrznych platform.
|
||||
**Current focus:** Phase 15 complete - loop closed (scontainers edit save fix)
|
||||
|
||||
## Current Position
|
||||
@@ -51,22 +51,22 @@ Phase 15: PLAN --> APPLY --> UNIFY ✓ ✓ ✓ [COMPLETE - 2026-04-18]
|
||||
- 2026-04-18: /koniec-pracy requirement mapped to .claude/commands/koniec-pracy.md guidance for end-of-session release flow
|
||||
- 2026-04-18: Scontainers edit fix - ID from tabbed form can be omitted when hidden field is defined as FormField and not as hiddenFields
|
||||
- Use existing `CouponRepository::markAsUsed()` instead of adding methods to stdClass
|
||||
- 2026-03-16: Przyczyna braku wysyłki = brakujące $apiloRepository w use() closures cron.php (regresja z fazy 6)
|
||||
- 2026-03-16: Przyczyna braku wysyłki = brakujące $apiloRepository w use() closures cron.php (regresja z fazy 6)
|
||||
- 2026-03-16: Retry -1 orders co 1h zamiast permanent failure
|
||||
- 2026-03-16: Email notification o trwale failed Apilo jobach
|
||||
- 2026-03-19: Order-related Apilo joby — infinite retry co 30 min (nigdy permanent failure)
|
||||
- 2026-03-19: Email z danymi zamĂłwienia + rozróżnienie PONAWIANY vs TRWAĹY BĹÄ„D
|
||||
- 2026-03-19: Cleanup stuck sync_payment/sync_status jobów po udanym wysłaniu
|
||||
- 2026-03-19: Edycja custom fields w koszyku — product_code przeliczany po zmianie, merge duplikatów przy identycznym hashu
|
||||
- 2026-03-19: Order-related Apilo joby — infinite retry co 30 min (nigdy permanent failure)
|
||||
- 2026-03-19: Email z danymi zamówienia + rozróżnienie PONAWIANY vs TRWAŁY BŁĄD
|
||||
- 2026-03-19: Cleanup stuck sync_payment/sync_status jobów po udanym wysłaniu
|
||||
- 2026-03-19: Edycja custom fields w koszyku — product_code przeliczany po zmianie, merge duplikatów przy identycznym hashu
|
||||
- 2026-03-19: JS handlery koszyka w basket.php (nie basket-details.php) bo basket-details jest AJAX-replaceable
|
||||
- 2026-03-25: view_cart event w basket.php (nie basket-details.php) — ten sam powód
|
||||
- 2026-03-25: view_cart event w basket.php (nie basket-details.php) — ten sam powód
|
||||
- 2026-03-25: GA4 item format standard: item_id (string), item_name, price (number), quantity (int), google_business_vertical: "retail"
|
||||
- 2026-03-25: Brak user_data w purchase — wymaga analizy RODO
|
||||
- 2026-03-25: summaryView() redirect guard usunięty — blokował kolejne zamówienia po pierwszym (z change.md instancji klienta)
|
||||
- 2026-03-25: Token zamówienia z jednorazowego na TTL 30 min — backward compat z plain string
|
||||
- 2026-03-25: logOrder() — logowanie błędów zamówień do logs/logs-order-YYYY-MM-DD.log
|
||||
- 2026-03-25: Redirect przy złym tokenie: /koszyk-podsumowanie zamiast /koszyk
|
||||
- 2026-04-16: Custom fields delete fix — hidden marker `custom_field_name_present` zamiast `array_key_exists('custom_field_name')`
|
||||
- 2026-03-25: Brak user_data w purchase — wymaga analizy RODO
|
||||
- 2026-03-25: summaryView() redirect guard usunięty — blokował kolejne zamówienia po pierwszym (z change.md instancji klienta)
|
||||
- 2026-03-25: Token zamówienia z jednorazowego na TTL 30 min — backward compat z plain string
|
||||
- 2026-03-25: logOrder() — logowanie błędów zamówień do logs/logs-order-YYYY-MM-DD.log
|
||||
- 2026-03-25: Redirect przy złym tokenie: /koszyk-podsumowanie zamiast /koszyk
|
||||
- 2026-04-16: Custom fields delete fix — hidden marker `custom_field_name_present` zamiast `array_key_exists('custom_field_name')`
|
||||
|
||||
### Deferred Issues
|
||||
None.
|
||||
@@ -87,5 +87,4 @@ Stopped at: Phase 15 complete, loop closed
|
||||
Next action: Start next work with $paul-plan (or run /koniec-pracy for release flow)
|
||||
Resume file: .paul/phases/15-scontainers-edit-save-fix/15-01-SUMMARY.md
|
||||
---
|
||||
*STATE.md — Updated after every significant action*
|
||||
|
||||
*STATE.md — Updated after every significant action*
|
||||
|
||||
@@ -1,3 +1,255 @@
|
||||
# API
|
||||
|
||||
> Endpointy, kontrakty request/response, autentykacja.
|
||||
## Scope
|
||||
|
||||
Dokument opisuje aktualne REST API dostepne przez `api.php` (ordersPRO + slowniki + produkty + kategorie).
|
||||
|
||||
## Base URL
|
||||
|
||||
- Endpoint techniczny: `/api.php`
|
||||
- Routing odbywa sie przez query params:
|
||||
- `endpoint` (np. `orders`, `products`)
|
||||
- `action` (np. `list`, `get`)
|
||||
|
||||
Przyklad:
|
||||
|
||||
```text
|
||||
GET /api.php?endpoint=orders&action=list
|
||||
```
|
||||
|
||||
## Authentication
|
||||
|
||||
- Wymagany naglowek: `X-Api-Key: <api_key>`
|
||||
- Klucz jest porownywany z wartoscia `pp_settings.api_key`
|
||||
- Brak lub zly klucz:
|
||||
- HTTP `401`
|
||||
- payload:
|
||||
```json
|
||||
{
|
||||
"status": "error",
|
||||
"code": "UNAUTHORIZED",
|
||||
"message": "Invalid or missing API key"
|
||||
}
|
||||
```
|
||||
|
||||
## Response format
|
||||
|
||||
Sukces:
|
||||
|
||||
```json
|
||||
{
|
||||
"status": "ok",
|
||||
"data": {}
|
||||
}
|
||||
```
|
||||
|
||||
Blad:
|
||||
|
||||
```json
|
||||
{
|
||||
"status": "error",
|
||||
"code": "BAD_REQUEST",
|
||||
"message": "..."
|
||||
}
|
||||
```
|
||||
|
||||
## Common HTTP/logic errors
|
||||
|
||||
- `400 BAD_REQUEST` - brak wymaganych parametrow/body
|
||||
- `401 UNAUTHORIZED` - brak/zly API key
|
||||
- `404 NOT_FOUND` - nieznany endpoint/action lub brak rekordu
|
||||
- `405 METHOD_NOT_ALLOWED` - zla metoda HTTP
|
||||
- `500 INTERNAL_ERROR` - blad serwera
|
||||
|
||||
## Endpoints
|
||||
|
||||
### Orders (`endpoint=orders`)
|
||||
|
||||
- `GET action=list`
|
||||
- filtry (opcjonalne): `status`, `paid`, `date_from`, `date_to`, `updated_since`, `number`, `client`
|
||||
- paginacja: `page` (default 1), `per_page` (default 50, max 100)
|
||||
- `GET action=get&id={id}`
|
||||
- `PUT action=change_status&id={id}`
|
||||
- body JSON:
|
||||
- `status_id` (required, int)
|
||||
- `send_email` (optional, bool)
|
||||
- `PUT action=set_paid&id={id}`
|
||||
- body JSON optional: `send_email` (bool)
|
||||
- `PUT action=set_unpaid&id={id}`
|
||||
|
||||
### Products (`endpoint=products`)
|
||||
|
||||
- `GET action=list`
|
||||
- filtry:
|
||||
- `search`, `status`, `promoted`
|
||||
- `attribute_{attributeId}={valueId}` (np. `attribute_12=37`)
|
||||
- sortowanie: `sort` (default `id`), `sort_dir` (default `DESC`)
|
||||
- paginacja: `page`, `per_page` (max 100)
|
||||
- `GET action=get&id={id}`
|
||||
- `POST action=create`
|
||||
- wymagane minimum:
|
||||
- `languages` (array, co najmniej jeden jezyk z `name`)
|
||||
- `price_brutto` (number >= 0)
|
||||
- `PUT action=update&id={id}`
|
||||
- partial update przez JSON body
|
||||
- `GET action=variants&id={parentProductId}`
|
||||
- dla produktu glownego (nie wariantu)
|
||||
- `POST action=create_variant&id={parentProductId}`
|
||||
- body:
|
||||
- `attributes` (required array)
|
||||
- opcjonalnie pola wariantu (np. `price_brutto`, `quantity`, `sku`, ...)
|
||||
- `PUT action=update_variant&id={variantId}`
|
||||
- partial update wariantu
|
||||
- `DELETE action=delete_variant&id={variantId}`
|
||||
- `POST action=upload_image`
|
||||
- body:
|
||||
- `id` (product id, required)
|
||||
- `file_name` (required)
|
||||
- `content_base64` (required)
|
||||
- `alt` (optional)
|
||||
- `o` (optional position)
|
||||
|
||||
### Dictionaries (`endpoint=dictionaries`)
|
||||
|
||||
- `GET action=statuses`
|
||||
- `GET action=transports`
|
||||
- `GET action=payment_methods`
|
||||
- `GET action=attributes`
|
||||
- `POST action=ensure_attribute`
|
||||
- body: `name` (required), `type` (optional int), `lang` (optional, default `pl`)
|
||||
- `POST action=ensure_attribute_value`
|
||||
- body: `attribute_id` (required), `name` (required), `lang` (optional, default `pl`)
|
||||
- `POST action=ensure_producer`
|
||||
- body: `name` (required)
|
||||
|
||||
### Categories (`endpoint=categories`)
|
||||
|
||||
- `GET action=list`
|
||||
- zwraca aktywne kategorie w formie flat list:
|
||||
- `id`
|
||||
- `parent_id`
|
||||
- `title`
|
||||
- tytuly pobierane najpierw w jezyku domyslnym (`pp_langs.start=1`), potem fallback.
|
||||
|
||||
## Source of truth (mapa API w kodzie)
|
||||
|
||||
### 1) Wejscie i dispatch
|
||||
|
||||
- `api.php`
|
||||
- wykrywa request API przez `$_GET['endpoint']`
|
||||
- ustawia JSON content-type
|
||||
- tworzy `medoo` + `SettingsRepository`
|
||||
- przekazuje sterowanie do `\api\ApiRouter::handle()`
|
||||
- `autoload/api/ApiRouter.php`
|
||||
- autentykacja (`X-Api-Key` vs `pp_settings.api_key`)
|
||||
- walidacja `endpoint` i `action`
|
||||
- mapowanie endpoint -> kontroler (`getControllerFactories()`)
|
||||
- helpery odpowiedzi: `sendSuccess()`, `sendError()`
|
||||
- helpery requestu: `getJsonBody()`, `requireMethod()`
|
||||
|
||||
### 2) Endpointy i kontrolery (runtime source)
|
||||
|
||||
#### `endpoint=orders`
|
||||
|
||||
- plik: `autoload/api/Controllers/OrdersApiController.php`
|
||||
- akcje:
|
||||
- `list` (GET)
|
||||
- `get` (GET)
|
||||
- `change_status` (PUT)
|
||||
- `set_paid` (PUT)
|
||||
- `set_unpaid` (PUT)
|
||||
|
||||
#### `endpoint=products`
|
||||
|
||||
- plik: `autoload/api/Controllers/ProductsApiController.php`
|
||||
- akcje:
|
||||
- `list` (GET)
|
||||
- `get` (GET)
|
||||
- `create` (POST)
|
||||
- `update` (PUT)
|
||||
- `variants` (GET)
|
||||
- `create_variant` (POST)
|
||||
- `update_variant` (PUT)
|
||||
- `delete_variant` (DELETE)
|
||||
- `upload_image` (POST)
|
||||
|
||||
#### `endpoint=dictionaries`
|
||||
|
||||
- plik: `autoload/api/Controllers/DictionariesApiController.php`
|
||||
- akcje:
|
||||
- `statuses` (GET)
|
||||
- `transports` (GET)
|
||||
- `payment_methods` (GET)
|
||||
- `attributes` (GET)
|
||||
- `ensure_attribute` (POST)
|
||||
- `ensure_attribute_value` (POST)
|
||||
- `ensure_producer` (POST)
|
||||
|
||||
#### `endpoint=categories`
|
||||
|
||||
- plik: `autoload/api/Controllers/CategoriesApiController.php`
|
||||
- akcje:
|
||||
- `list` (GET)
|
||||
|
||||
### 3) Warstwa domenowa pod API (shape danych)
|
||||
|
||||
- Orders:
|
||||
- `Domain\Order\OrderRepository::listForApi()`
|
||||
- `Domain\Order\OrderRepository::findForApi()`
|
||||
- `Domain\Order\OrderAdminService` (zmiana statusu/platnosci)
|
||||
- Products:
|
||||
- `Domain\Product\ProductRepository::listForApi()`
|
||||
- `Domain\Product\ProductRepository::findForApi()`
|
||||
- `Domain\Product\ProductRepository::saveProduct()`
|
||||
- metody wariantow `*VariantForApi()`
|
||||
- Dictionaries:
|
||||
- `Domain\ShopStatus\ShopStatusRepository`
|
||||
- `Domain\Transport\TransportRepository`
|
||||
- `Domain\PaymentMethod\PaymentMethodRepository`
|
||||
- `Domain\Attribute\AttributeRepository`
|
||||
- `Domain\Producer\ProducerRepository`
|
||||
- Categories:
|
||||
- bezposrednio query przez `$GLOBALS['mdb']` w `CategoriesApiController`
|
||||
|
||||
### 4) Testy API (behavior source)
|
||||
|
||||
- `tests/Unit/api/ApiRouterTest.php`
|
||||
- `tests/Unit/api/Controllers/OrdersApiControllerTest.php`
|
||||
- `tests/Unit/api/Controllers/ProductsApiControllerTest.php`
|
||||
- `tests/Unit/api/Controllers/DictionariesApiControllerTest.php`
|
||||
|
||||
### 5) Dokumentacja kontraktu (human source)
|
||||
|
||||
- `api-docs/api-reference.json`
|
||||
- `api-docs/index.html`
|
||||
|
||||
Uwaga: dokumentacja z `api-docs/*` moze byc starsza od runtime.
|
||||
Zrodlem prawdy dla dzialania endpointow jest zawsze:
|
||||
`api.php` + `autoload/api/ApiRouter.php` + aktualne kontrolery `autoload/api/Controllers/*`.
|
||||
|
||||
## Curl examples
|
||||
|
||||
Pobranie listy zamowien:
|
||||
|
||||
```bash
|
||||
curl -X GET "https://example.com/api.php?endpoint=orders&action=list&page=1&per_page=20" \
|
||||
-H "X-Api-Key: YOUR_API_KEY"
|
||||
```
|
||||
|
||||
Zmiana statusu zamowienia:
|
||||
|
||||
```bash
|
||||
curl -X PUT "https://example.com/api.php?endpoint=orders&action=change_status&id=123" \
|
||||
-H "X-Api-Key: YOUR_API_KEY" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "{\"status_id\": 6, \"send_email\": true}"
|
||||
```
|
||||
|
||||
Dodanie wariantu produktu:
|
||||
|
||||
```bash
|
||||
curl -X POST "https://example.com/api.php?endpoint=products&action=create_variant&id=50" \
|
||||
-H "X-Api-Key: YOUR_API_KEY" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "{\"attributes\":[{\"attribute_id\":12,\"attribute_value_id\":37}],\"price_brutto\":99.99,\"quantity\":10}"
|
||||
```
|
||||
|
||||
@@ -1,3 +1,192 @@
|
||||
# ARCHITECTURE
|
||||
|
||||
> Struktura klas, modulow, przeplywow i zaleznosci w projekcie.
|
||||
## Scope
|
||||
|
||||
Dokument opisuje aktualna architekture runtime projektu `shopPRO`:
|
||||
- warstwy aplikacji i ich odpowiedzialnosci,
|
||||
- przeplywy requestow (admin/front/api),
|
||||
- Dependency Injection i miejsca wiringu,
|
||||
- konwencje autoloadera i namespace,
|
||||
- granice miedzy nowa architektura a pozostalosciami legacy.
|
||||
|
||||
## High-level layout
|
||||
|
||||
Kod aplikacji jest podzielony na 4 glowne warstwy:
|
||||
|
||||
1. `autoload/Domain/` - logika biznesowa i dostep do danych (28 modulow)
|
||||
2. `autoload/admin/` - panel administracyjny (router + kontrolery + form system)
|
||||
3. `autoload/front/` - frontend sklepu (router + layout engine + kontrolery/widoki)
|
||||
4. `autoload/api/` - REST API (`api.php`, `ApiRouter`, kontrolery endpointow)
|
||||
|
||||
Komponenty wspoldzielone sa trzymane w `autoload/Shared/`.
|
||||
|
||||
## Directory map (runtime)
|
||||
|
||||
```text
|
||||
autoload/
|
||||
Domain/ # 28 modulow domenowych
|
||||
Shared/ # Cache, Helpers, Tpl, Html, Email, Image
|
||||
admin/
|
||||
App.php # routing + DI factories
|
||||
Controllers/ # 28 kontrolerow admin
|
||||
Support/ # formularze/tabele
|
||||
Validation/ # walidacja formularzy
|
||||
ViewModels/ # modele widokow
|
||||
front/
|
||||
App.php # routing + DI factories + fallback legacy
|
||||
LayoutEngine.php # silnik layoutu frontend
|
||||
Controllers/ # 8 kontrolerow frontend
|
||||
Views/ # 11 statycznych klas widokow
|
||||
api/
|
||||
ApiRouter.php # auth + endpoint dispatch
|
||||
Controllers/ # 4 kontrolery API
|
||||
```
|
||||
|
||||
## Entry points i przeplyw
|
||||
|
||||
### Admin (`admin/index.php`)
|
||||
|
||||
1. Bootstrap (sesja, DB, autoload)
|
||||
2. `admin\App::update()` - uruchamia pending migracje
|
||||
3. `admin\App::special_actions()` - logowanie/wylogowanie/2FA
|
||||
4. `admin\App::render()`:
|
||||
- auth gate (lub formularz logowania),
|
||||
- `route()` -> kontroler + akcja,
|
||||
- render przez `Shared\Tpl\Tpl`.
|
||||
|
||||
### Front (`index.php`)
|
||||
|
||||
1. Bootstrap (sesja, DB, autoload, jezyk)
|
||||
2. Mapowanie URL (redirecty + routes)
|
||||
3. `front\App::checkUrlParams()`
|
||||
4. `front\App::route()`:
|
||||
- artykul/produkt/kategoria,
|
||||
- nowe kontrolery DI,
|
||||
- fallback do `front\controls\*` (legacy, jesli istnieje)
|
||||
5. `front\LayoutEngine::show()` sklada finalny HTML.
|
||||
|
||||
### API (`api.php`)
|
||||
|
||||
1. Bootstrap (bez sesji biznesowej)
|
||||
2. Tworzenie `\api\ApiRouter`
|
||||
3. `ApiRouter::handle()`:
|
||||
- auth przez `X-Api-Key`,
|
||||
- walidacja `endpoint` i `action`,
|
||||
- dispatch do kontrolera API,
|
||||
- JSON response przez `sendSuccess()` / `sendError()`.
|
||||
|
||||
Szczegolowa specyfikacja endpointow: `.paul/docs/API.md`.
|
||||
|
||||
## Dependency Injection (manual factories)
|
||||
|
||||
DI jest realizowane recznie w mapach factory:
|
||||
|
||||
- admin: `autoload/admin/App.php` -> `getControllerFactories()`
|
||||
- front: `autoload/front/App.php` -> `getControllerFactories()`
|
||||
- api: `autoload/api/ApiRouter.php` -> `getControllerFactories()`
|
||||
|
||||
Wzorzec:
|
||||
- router tworzy repozytoria domenowe,
|
||||
- repozytoria sa wstrzykiwane do kontrolerow przez konstruktor,
|
||||
- kontroler wywoluje metody domenowe i zwraca HTML/JSON.
|
||||
|
||||
## Autoloader i namespace rules
|
||||
|
||||
Kazdy entry point korzysta z custom autoloadera:
|
||||
|
||||
1. `autoload/{namespace}/class.{ClassName}.php` (legacy)
|
||||
2. `autoload/{namespace}/{ClassName}.php` (nowy format)
|
||||
|
||||
Mapowanie namespace -> katalog (case-sensitive na Linux):
|
||||
|
||||
- `\Domain\` -> `autoload/Domain/`
|
||||
- `\Shared\` -> `autoload/Shared/`
|
||||
- `\admin\` -> `autoload/admin/` (male `a`)
|
||||
- `\front\` -> `autoload/front/`
|
||||
- `\api\` -> `autoload/api/`
|
||||
|
||||
Nie uzywac `\Admin\` (duze `A`), bo katalog runtime to `admin/`.
|
||||
|
||||
## Domain layer (28 modulow)
|
||||
|
||||
Aktualne moduly:
|
||||
|
||||
`Article`, `Attribute`, `Banner`, `Basket`, `Cache`, `Category`, `Client`, `Coupon`, `CronJob`, `Dashboard`, `Dictionaries`, `Integrations`, `Languages`, `Layouts`, `Newsletter`, `Order`, `Pages`, `PaymentMethod`, `Producer`, `Product`, `ProductSet`, `Promotion`, `Scontainers`, `Settings`, `ShopStatus`, `Transport`, `Update`, `User`.
|
||||
|
||||
Zasada: logika biznesowa i dostep do danych sa w Domain, bez duplikowania osobnych warstw "admin service" i "front service" dla tych samych przypadkow (wyjatki tylko tam, gdzie historycznie juz istnieja, np. `OrderAdminService`).
|
||||
|
||||
## Admin architecture
|
||||
|
||||
- Router: `admin\App`
|
||||
- Kontrolery: `autoload/admin/Controllers/*.php` (28 klas)
|
||||
- Form system:
|
||||
- `admin\ViewModels\Forms\*`
|
||||
- `admin\Support\Forms\FormRequestHandler`
|
||||
- `admin\Support\Forms\FormFieldRenderer`
|
||||
- `admin\Validation\FormValidator`
|
||||
- template: `admin/templates/components/form-edit.php`
|
||||
|
||||
Admin ma pelne DI i nie korzysta z fallbacku na legacy kontrolery.
|
||||
|
||||
## Front architecture
|
||||
|
||||
- Router: `front\App`
|
||||
- Layout engine: `front\LayoutEngine`
|
||||
- Kontrolery DI: `autoload/front/Controllers/*.php` (8 klas)
|
||||
- Widoki statyczne: `autoload/front/Views/*.php` (11 klas)
|
||||
|
||||
Wazne: frontend nadal ma fallback do `\front\controls\*`, wiec architektura jest hybrydowa (new DI + remaining legacy paths).
|
||||
|
||||
## API architecture
|
||||
|
||||
- Router: `api\ApiRouter`
|
||||
- Endpointy: `orders`, `products`, `dictionaries`, `categories`
|
||||
- Kontrolery: `autoload/api/Controllers/*ApiController.php` (4 klasy)
|
||||
- API jest stateless, autoryzowane naglowkiem `X-Api-Key`
|
||||
|
||||
Source-of-truth API to runtime:
|
||||
- `api.php`
|
||||
- `autoload/api/ApiRouter.php`
|
||||
- `autoload/api/Controllers/*`
|
||||
|
||||
## Shared components
|
||||
|
||||
Najwazniejsze klasy wspoldzielone:
|
||||
|
||||
- `Shared\Cache\CacheHandler`, `Shared\Cache\RedisConnection`
|
||||
- `Shared\Helpers\Helpers`
|
||||
- `Shared\Tpl\Tpl`
|
||||
- `Shared\Html\Html`
|
||||
- `Shared\Email\Email`
|
||||
- `Shared\Image\ImageManipulator`
|
||||
|
||||
## Data and cache conventions
|
||||
|
||||
- ORM: Medoo (`$mdb`)
|
||||
- Prefix tabel: `pp_`
|
||||
- Cache: Redis, domyslnie TTL `86400`
|
||||
- Dane cache czesto serializowane (`serialize`/`unserialize`)
|
||||
- Czyszczenie cache produktu: pattern `shop\\product:{id}:*`
|
||||
|
||||
## Security boundaries
|
||||
|
||||
- Admin:
|
||||
- sesja uzytkownika admina,
|
||||
- CSRF token w akcjach POST,
|
||||
- 2FA email flow (pending session + verify).
|
||||
- API:
|
||||
- `X-Api-Key` porownywany przez `hash_equals()`,
|
||||
- brak logiki sesyjnej.
|
||||
- Front:
|
||||
- sesja klienta + walidacja przeplywow frontendowych.
|
||||
|
||||
## Source files
|
||||
|
||||
Najwazniejsze pliki do szybkiej orientacji:
|
||||
|
||||
- `autoload/admin/App.php`
|
||||
- `autoload/front/App.php`
|
||||
- `autoload/front/LayoutEngine.php`
|
||||
- `autoload/api/ApiRouter.php`
|
||||
- `.paul/docs/API.md`
|
||||
- `docs/PROJECT_STRUCTURE.md`
|
||||
|
||||
@@ -1,3 +1,228 @@
|
||||
# DB_SCHEMA
|
||||
# DB_SCHEMA
|
||||
|
||||
> Schemat bazy danych — tabele, kolumny, FK, indeksy.
|
||||
## Scope
|
||||
|
||||
Dokument opisuje praktyczny schema map dla `shopPRO`:
|
||||
- najwazniejsze tabele i relacje,
|
||||
- grupowanie po domenach biznesowych,
|
||||
- kluczowe kolumny i indeksy, ktore maja znaczenie runtime,
|
||||
- mapowanie tabela -> warstwa Domain.
|
||||
|
||||
Pelna lista tabel i historyczne notki migracyjne:
|
||||
`docs/DATABASE_STRUCTURE.md` (source of truth dla detali kolumnowych).
|
||||
|
||||
## Konwencje globalne
|
||||
|
||||
- ORM: Medoo (`$mdb`)
|
||||
- Prefix tabel: `pp_`
|
||||
- Primary key: najczesciej `id` (INT AUTO_INCREMENT)
|
||||
- Jezyki/translations: zwykle tabele `*_langs` z kluczem `lang_id`
|
||||
- Wiele-do-wielu: tabele lacznikowe `*_products`, `*_payment_methods`, itp.
|
||||
|
||||
## Core commerce
|
||||
|
||||
### Produkty
|
||||
|
||||
- `pp_shop_products`
|
||||
- core produktu i wariantu (`parent_id` dla kombinacji)
|
||||
- ceny (`price_brutto`, `price_brutto_promo`), stany (`quantity`)
|
||||
- flagi (`status`, `archive`, `promoted`)
|
||||
- `pp_shop_products_langs`
|
||||
- nazwy/opisy per jezyk
|
||||
- `pp_shop_products_images`
|
||||
- obrazy produktu
|
||||
- `pp_shop_products_categories`
|
||||
- przypisania produkt-kategoria
|
||||
- `pp_shop_products_attributes`
|
||||
- przypisania wariantu do wartosci cech
|
||||
- `pp_shop_products_custom_fields`
|
||||
- dodatkowe pola produktu
|
||||
|
||||
Warstwa: `Domain\Product\ProductRepository`, `Domain\Attribute\AttributeRepository`.
|
||||
|
||||
### Kategorie
|
||||
|
||||
- `pp_shop_categories`
|
||||
- drzewo kategorii (`parent_id`), status, kolejnosc
|
||||
- `pp_shop_categories_langs`
|
||||
- tresci SEO i opisy kategorii
|
||||
|
||||
Warstwa: `Domain\Category\CategoryRepository`.
|
||||
|
||||
### Zamowienia
|
||||
|
||||
- `pp_shop_orders`
|
||||
- dane klienta "w momencie zakupu", summary, status/platnosc, daty
|
||||
- kluczowe pole integracyjne: `updated_at` (polling API)
|
||||
|
||||
Warstwa: `Domain\Order\OrderRepository`, `Domain\Order\OrderAdminService`.
|
||||
|
||||
### Klienci
|
||||
|
||||
- `pp_shop_clients`
|
||||
- konto klienta i dane adresowe/logowania (uzywane przez ClientRepository)
|
||||
|
||||
Warstwa: `Domain\Client\ClientRepository`.
|
||||
|
||||
## Slowniki i checkout
|
||||
|
||||
### Platnosci
|
||||
|
||||
- `pp_shop_payment_methods`
|
||||
- status, opis, mapowanie Apilo
|
||||
- limity kwotowe: `min_order_amount`, `max_order_amount`
|
||||
- COD flag: `is_cod`
|
||||
|
||||
Warstwa: `Domain\PaymentMethod\PaymentMethodRepository`.
|
||||
|
||||
### Transport
|
||||
|
||||
- `pp_shop_transports`
|
||||
- koszt, status, limity, mapowanie Apilo
|
||||
- `pp_shop_transport_payment_methods`
|
||||
- relacja transport <-> platnosc (N:M)
|
||||
|
||||
Warstwa: `Domain\Transport\TransportRepository`.
|
||||
|
||||
### Statusy zamowien
|
||||
|
||||
- `pp_shop_statuses`
|
||||
- statusy predefiniowane, kolor, mapowanie Apilo
|
||||
|
||||
Warstwa: `Domain\ShopStatus\ShopStatusRepository`.
|
||||
|
||||
## Marketing i merch
|
||||
|
||||
### Promocje i kupony
|
||||
|
||||
- `pp_shop_promotion`
|
||||
- reguly promocji, daty aktywnosci, warunki i zakresy (JSON categories)
|
||||
- `pp_shop_coupon`
|
||||
- kupony, licznik uzyc, ograniczenia
|
||||
|
||||
Warstwa: `Domain\Promotion\PromotionRepository`, `Domain\Coupon\CouponRepository`.
|
||||
|
||||
### Producenci
|
||||
|
||||
- `pp_shop_producer`
|
||||
- `pp_shop_producer_lang`
|
||||
|
||||
Warstwa: `Domain\Producer\ProducerRepository`.
|
||||
|
||||
### Zestawy produktow
|
||||
|
||||
- `pp_shop_product_sets`
|
||||
- `pp_shop_product_sets_products`
|
||||
|
||||
Warstwa: `Domain\ProductSet\ProductSetRepository`.
|
||||
|
||||
### Cechy i wartosci
|
||||
|
||||
- `pp_shop_attributes`
|
||||
- `pp_shop_attributes_langs`
|
||||
- `pp_shop_attributes_values`
|
||||
- `pp_shop_attributes_values_langs`
|
||||
|
||||
Warstwa: `Domain\Attribute\AttributeRepository`.
|
||||
|
||||
## CMS i frontend content
|
||||
|
||||
### Artykuly
|
||||
|
||||
- `pp_articles`
|
||||
- `pp_articles_langs`
|
||||
- `pp_articles_pages`
|
||||
- `pp_articles_images`
|
||||
- `pp_articles_files`
|
||||
|
||||
Warstwa: `Domain\Article\ArticleRepository`.
|
||||
|
||||
### Strony i layouty
|
||||
|
||||
- `pp_pages`
|
||||
- `pp_layouts`
|
||||
- `pp_layouts_pages`
|
||||
- `pp_layouts_categories`
|
||||
|
||||
Warstwa: `Domain\Pages\PagesRepository`, `Domain\Layouts\LayoutsRepository`.
|
||||
|
||||
### Banery i kontenery statyczne
|
||||
|
||||
- `pp_banners`
|
||||
- `pp_banners_langs`
|
||||
- `pp_scontainers`
|
||||
- `pp_scontainers_langs`
|
||||
|
||||
Warstwa: `Domain\Banner\BannerRepository`, `Domain\Scontainers\ScontainersRepository`.
|
||||
|
||||
## Ustawienia i system
|
||||
|
||||
### Ustawienia aplikacji
|
||||
|
||||
- `pp_settings`
|
||||
- klucze globalne (w tym `api_key` dla REST API)
|
||||
- `pp_shop_apilo_settings`
|
||||
- `pp_shop_shoppro_settings`
|
||||
|
||||
Warstwa: `Domain\Settings\SettingsRepository`, `Domain\Integrations\IntegrationsRepository`.
|
||||
|
||||
### Jezyki i tlumaczenia
|
||||
|
||||
- `pp_langs`
|
||||
- `pp_langs_translations`
|
||||
|
||||
Warstwa: `Domain\Languages\LanguagesRepository`.
|
||||
|
||||
### Uzytkownicy admina
|
||||
|
||||
- `pp_users`
|
||||
- login, hash hasla, status
|
||||
- pola 2FA (`twofa_*`)
|
||||
|
||||
Warstwa: `Domain\User\UserRepository`.
|
||||
|
||||
## Routing i URL mapping
|
||||
|
||||
- `pp_routes`
|
||||
- regex `pattern` -> `destination` query string
|
||||
- obsluguje trasy encji oraz trasy systemowe
|
||||
- cache Redis: `pp_routes:all`
|
||||
|
||||
Runtime wykorzystanie:
|
||||
- `index.php`
|
||||
- `Shared\Helpers\Helpers::htacces()`
|
||||
- repozytoria encji generujace/odswiezajace trasy.
|
||||
|
||||
## Kolejka cron
|
||||
|
||||
- `pp_cron_jobs`
|
||||
- status processing pipeline (`pending`, `processing`, `completed`, `failed`, `cancelled`)
|
||||
- retry/backoff: `attempts`, `max_attempts`, `scheduled_at`
|
||||
- indeksy:
|
||||
- `(status, priority, scheduled_at)`
|
||||
- `(job_type)`
|
||||
- `(status)`
|
||||
- `pp_cron_schedules`
|
||||
- harmonogramy okresowe (`interval_seconds`, `next_run_at`)
|
||||
- indeks `(enabled, next_run_at)`
|
||||
|
||||
Warstwa: `Domain\CronJob\CronJobRepository`, `Domain\CronJob\CronJobProcessor`.
|
||||
|
||||
## Najwazniejsze relacje (FK logiczne)
|
||||
|
||||
- Produkt glowny -> wariant: `pp_shop_products.parent_id -> pp_shop_products.id`
|
||||
- Produkt -> tlumaczenia: `pp_shop_products_langs.product_id -> pp_shop_products.id`
|
||||
- Produkt -> kategoria: `pp_shop_products_categories.product_id -> pp_shop_products.id`
|
||||
- Kategoria -> tlumaczenia: `pp_shop_categories_langs.category_id -> pp_shop_categories.id`
|
||||
- Zamowienie -> klient: `pp_shop_orders.client_id -> pp_shop_clients.id` (opcjonalne)
|
||||
- Transport <-> platnosc: `pp_shop_transport_payment_methods`
|
||||
- Cecha -> wartosci -> warianty: `attributes -> values -> shop_products_attributes`
|
||||
- Producent -> tlumaczenia: `pp_shop_producer_lang.producer_id -> pp_shop_producer.id`
|
||||
- Kontener -> tlumaczenia: `pp_scontainers_langs.container_id -> pp_scontainers.id`
|
||||
|
||||
## Uwaga operacyjna
|
||||
|
||||
Ten dokument jest skrotem architektonicznym.
|
||||
Przy zmianach SQL/migracji zawsze aktualizuj rownolegle:
|
||||
1. `docs/DATABASE_STRUCTURE.md` (detal techniczny)
|
||||
2. `.paul/docs/DB_SCHEMA.md` (mapa domenowa i impact runtime)
|
||||
|
||||
3611
.paul/docs/TODO.md
3611
.paul/docs/TODO.md
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
@@ -2,5 +2,5 @@ projectKey=shopPRO
|
||||
serverUrl=https://sonar.project-pro.pl
|
||||
serverVersion=26.3.0.120487
|
||||
dashboardUrl=https://sonar.project-pro.pl/dashboard?id=shopPRO
|
||||
ceTaskId=77fcbbea-9d8f-45d6-86d7-b262e33f979e
|
||||
ceTaskUrl=https://sonar.project-pro.pl/api/ce/task?id=77fcbbea-9d8f-45d6-86d7-b262e33f979e
|
||||
ceTaskId=3051d3c7-50d0-4263-bdc3-12917447a707
|
||||
ceTaskUrl=https://sonar.project-pro.pl/api/ce/task?id=3051d3c7-50d0-4263-bdc3-12917447a707
|
||||
|
||||
165
AGENTS.md
165
AGENTS.md
@@ -1,4 +1,4 @@
|
||||
# CLAUDE.md
|
||||
# CLAUDE.md
|
||||
|
||||
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
||||
|
||||
@@ -7,19 +7,19 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
|
||||
shopPRO is a PHP e-commerce platform with an admin panel and customer-facing storefront. It uses Medoo ORM (`$mdb`), Redis caching, and a Domain-Driven Design architecture with Dependency Injection (migration from legacy architecture complete).
|
||||
|
||||
## Zasady pisania kodu
|
||||
- Kod ma być czytelny „dla obcego”: jasne nazwy, mało magii
|
||||
- Brak „skrótów na szybko” typu logika w widokach, copy-paste, losowe helpery bez spójności
|
||||
- Każda funkcja/klasa ma mieć jedną odpowiedzialność, zwykle do 30–50 linii (jeśli dłuższe – dzielić)
|
||||
- max 3 poziomy zagnieżdżeń (if/foreach), reszta do osobnych metod
|
||||
- Kod ma być czytelny „dla obcego”: jasne nazwy, mało magii
|
||||
- Brak „skrótów na szybko” typu logika w widokach, copy-paste, losowe helpery bez spójności
|
||||
- Każda funkcja/klasa ma mieć jedną odpowiedzialność, zwykle do 30–50 linii (jeśli dłuższe – dzielić)
|
||||
- max 3 poziomy zagnieżdżeń (if/foreach), reszta do osobnych metod
|
||||
- Nazewnictwo:
|
||||
- klasy: PascalCase
|
||||
- metody/zmienne: camelCase
|
||||
- stałe: UPPER_SNAKE_CASE
|
||||
- Zero „skrótologii” w nazwach (np. $d, $tmp, $x1) poza pętlami 2–3 linijki
|
||||
- medoo + prepared statements bez wyjątków (żadnego sklejania SQL stringiem)
|
||||
- stałe: UPPER_SNAKE_CASE
|
||||
- Zero „skrótologii” w nazwach (np. $d, $tmp, $x1) poza pętlami 2–3 linijki
|
||||
- medoo + prepared statements bez wyjÄ…tkĂłw (ĹĽadnego sklejania SQL stringiem)
|
||||
- XSS: escape w widokach (np. helper e())
|
||||
- CSRF dla formularzy, sensowna obsługa sesji
|
||||
- Kod ma mieć komentarze tylko tam, gdzie wyjaśniają „dlaczego”, nie „co”
|
||||
- CSRF dla formularzy, sensowna obsługa sesji
|
||||
- Kod ma mieć komentarze tylko tam, gdzie wyjaśniają „dlaczego”, nie „co”
|
||||
|
||||
## PHP Version Constraint
|
||||
|
||||
@@ -36,7 +36,7 @@ shopPRO is a PHP e-commerce platform with an admin panel and customer-facing sto
|
||||
|
||||
### Running Tests
|
||||
```bash
|
||||
# Full suite (recommended — PowerShell, auto-finds php)
|
||||
# Full suite (recommended — PowerShell, auto-finds php)
|
||||
./test.ps1
|
||||
|
||||
# Specific file
|
||||
@@ -61,50 +61,50 @@ See `docs/UPDATE_INSTRUCTIONS.md` for the full procedure. Updates are ZIP packag
|
||||
### Directory Structure
|
||||
```
|
||||
shopPRO/
|
||||
├── autoload/ # Autoloaded classes (core codebase)
|
||||
│ ├── Domain/ # Business logic repositories (\Domain\)
|
||||
│ ├── Shared/ # Shared utilities (\Shared\)
|
||||
│ │ ├── Cache/ # CacheHandler, RedisConnection
|
||||
│ │ ├── Email/ # Email (PHPMailer wrapper)
|
||||
│ │ ├── Helpers/ # Helpers (formerly class.S.php)
|
||||
│ │ ├── Html/ # Html utility
|
||||
│ │ ├── Image/ # ImageManipulator
|
||||
│ │ └── Tpl/ # Template engine
|
||||
│ ├── api/ # REST API layer (\api\)
|
||||
│ │ ├── ApiRouter.php # API router (\api\ApiRouter)
|
||||
│ │ └── Controllers/ # API controllers (\api\Controllers\)
|
||||
│ ├── admin/ # Admin panel layer
|
||||
│ │ ├── App.php # Admin router (\admin\App)
|
||||
│ │ ├── Controllers/ # DI controllers (\admin\Controllers\) — 28 controllers
|
||||
│ │ ├── Support/ # TableListRequestFactory, Forms/FormRequestHandler, Forms/FormFieldRenderer
|
||||
│ │ ├── Validation/ # FormValidator
|
||||
│ │ └── ViewModels/ # Forms/ (FormEditViewModel, FormField, FormTab, FormAction, FormFieldType), Common/ (PaginatedTableViewModel)
|
||||
│ └── front/ # Frontend layer
|
||||
│ ├── App.php # Frontend router (\front\App)
|
||||
│ ├── LayoutEngine.php # Layout engine (\front\LayoutEngine)
|
||||
│ ├── Controllers/ # DI controllers (\front\Controllers\) — 8 controllers
|
||||
│ └── Views/ # Static views (\front\Views\) — 11 view classes
|
||||
├── admin/ # Admin panel
|
||||
│ ├── templates/ # Admin view templates
|
||||
│ └── layout/ # Admin CSS/JS/icons
|
||||
├── templates/ # Frontend view templates
|
||||
├── libraries/ # Third-party libraries (Medoo, RedBeanPHP, PHPMailer)
|
||||
├── tests/ # PHPUnit tests
|
||||
│ ├── bootstrap.php
|
||||
│ ├── stubs/ # Test stubs (CacheHandler, Helpers, ShopProduct)
|
||||
│ └── Unit/
|
||||
│ ├── Domain/ # Repository tests
|
||||
│ ├── admin/Controllers/ # Controller tests
|
||||
│ └── api/ # API tests
|
||||
├── updates/ # Update packages for clients
|
||||
├── docs/ # Technical documentation
|
||||
├── config.php # Database/Redis config (not in repo)
|
||||
├── index.php # Frontend entry point
|
||||
├── ajax.php # Frontend AJAX handler
|
||||
├── admin/index.php # Admin entry point
|
||||
├── admin/ajax.php # Admin AJAX handler
|
||||
├── cron.php # CRON jobs (Apilo sync)
|
||||
└── api.php # REST API (ordersPRO + Ekomi)
|
||||
├── autoload/ # Autoloaded classes (core codebase)
|
||||
│ ├── Domain/ # Business logic repositories (\Domain\)
|
||||
│ ├── Shared/ # Shared utilities (\Shared\)
|
||||
│ │ ├── Cache/ # CacheHandler, RedisConnection
|
||||
│ │ ├── Email/ # Email (PHPMailer wrapper)
|
||||
│ │ ├── Helpers/ # Helpers (formerly class.S.php)
|
||||
│ │ ├── Html/ # Html utility
|
||||
│ │ ├── Image/ # ImageManipulator
|
||||
│ │ └── Tpl/ # Template engine
|
||||
│ ├── api/ # REST API layer (\api\)
|
||||
│ │ ├── ApiRouter.php # API router (\api\ApiRouter)
|
||||
│ │ └── Controllers/ # API controllers (\api\Controllers\)
|
||||
│ ├── admin/ # Admin panel layer
|
||||
│ │ ├── App.php # Admin router (\admin\App)
|
||||
│ │ ├── Controllers/ # DI controllers (\admin\Controllers\) — 28 controllers
|
||||
│ │ ├── Support/ # TableListRequestFactory, Forms/FormRequestHandler, Forms/FormFieldRenderer
|
||||
│ │ ├── Validation/ # FormValidator
|
||||
│ │ └── ViewModels/ # Forms/ (FormEditViewModel, FormField, FormTab, FormAction, FormFieldType), Common/ (PaginatedTableViewModel)
|
||||
│ └── front/ # Frontend layer
|
||||
│ ├── App.php # Frontend router (\front\App)
|
||||
│ ├── LayoutEngine.php # Layout engine (\front\LayoutEngine)
|
||||
│ ├── Controllers/ # DI controllers (\front\Controllers\) — 8 controllers
|
||||
│ └── Views/ # Static views (\front\Views\) — 11 view classes
|
||||
├── admin/ # Admin panel
|
||||
│ ├── templates/ # Admin view templates
|
||||
│ └── layout/ # Admin CSS/JS/icons
|
||||
├── templates/ # Frontend view templates
|
||||
├── libraries/ # Third-party libraries (Medoo, RedBeanPHP, PHPMailer)
|
||||
├── tests/ # PHPUnit tests
|
||||
│ ├── bootstrap.php
|
||||
│ ├── stubs/ # Test stubs (CacheHandler, Helpers, ShopProduct)
|
||||
│ └── Unit/
|
||||
│ ├── Domain/ # Repository tests
|
||||
│ ├── admin/Controllers/ # Controller tests
|
||||
│ └── api/ # API tests
|
||||
├── updates/ # Update packages for clients
|
||||
├── docs/ # Technical documentation
|
||||
├── config.php # Database/Redis config (not in repo)
|
||||
├── index.php # Frontend entry point
|
||||
├── ajax.php # Frontend AJAX handler
|
||||
├── admin/index.php # Admin entry point
|
||||
├── admin/ajax.php # Admin AJAX handler
|
||||
├── cron.php # CRON jobs (Apilo sync)
|
||||
└── api.php # REST API (ordersPRO + Ekomi)
|
||||
```
|
||||
|
||||
### Autoloader
|
||||
@@ -114,19 +114,19 @@ Custom autoloader in each entry point (not Composer autoload at runtime). Tries
|
||||
2. `autoload/{namespace}/{ClassName}.php` (PSR-4 style, fallback)
|
||||
|
||||
### Namespace Conventions (case-sensitive on Linux!)
|
||||
- `\Domain\` → `autoload/Domain/` (uppercase D)
|
||||
- `\admin\Controllers\` → `autoload/admin/Controllers/` (lowercase a)
|
||||
- `\Shared\` → `autoload/Shared/`
|
||||
- `\api\` → `autoload/api/`
|
||||
- Do NOT use `\Admin\` (uppercase A) — the server directory is `admin/` (lowercase)
|
||||
- `\shop\` namespace is **deleted** — all 12 legacy classes migrated to `\Domain\`, `autoload/shop/` directory removed
|
||||
- `\Domain\` → `autoload/Domain/` (uppercase D)
|
||||
- `\admin\Controllers\` → `autoload/admin/Controllers/` (lowercase a)
|
||||
- `\Shared\` → `autoload/Shared/`
|
||||
- `\api\` → `autoload/api/`
|
||||
- Do NOT use `\Admin\` (uppercase A) — the server directory is `admin/` (lowercase)
|
||||
- `\shop\` namespace is **deleted** — all 12 legacy classes migrated to `\Domain\`, `autoload/shop/` directory removed
|
||||
|
||||
### Domain-Driven Architecture (migration complete)
|
||||
|
||||
All legacy directories (`admin/controls/`, `admin/factory/`, `admin/view/`, `front/controls/`, `front/view/`, `front/factory/`, `shop/`) have been deleted. All modules now use this pattern:
|
||||
|
||||
**Domain Layer** (`autoload/Domain/{Module}/`):
|
||||
- `{Module}Repository.php` — data access, business logic, Redis caching
|
||||
- `{Module}Repository.php` — data access, business logic, Redis caching
|
||||
- Constructor DI with `$db` (Medoo instance)
|
||||
- Methods serve both admin and frontend (shared Domain, no separate services)
|
||||
|
||||
@@ -141,7 +141,7 @@ All legacy directories (`admin/controls/`, `admin/factory/`, `admin/view/`, `fro
|
||||
- Wired in `front\App::getControllerFactories()`
|
||||
|
||||
**Frontend Views** (`autoload/front/Views/`):
|
||||
- Static classes, no state, no DI — pure rendering
|
||||
- Static classes, no state, no DI — pure rendering
|
||||
|
||||
**API Controllers** (`autoload/api/Controllers/`):
|
||||
- DI via constructor, stateless (no session)
|
||||
@@ -151,13 +151,13 @@ All legacy directories (`admin/controls/`, `admin/factory/`, `admin/view/`, `fro
|
||||
### Key Classes
|
||||
| Class | Purpose |
|
||||
|-------|---------|
|
||||
| `\admin\App` | Admin router — maps URL segments to controllers |
|
||||
| `\front\App` | Frontend router — `route()`, `checkUrlParams()` |
|
||||
| `\front\LayoutEngine` | Frontend layout engine — `show()`, tag replacement |
|
||||
| `\admin\App` | Admin router — maps URL segments to controllers |
|
||||
| `\front\App` | Frontend router — `route()`, `checkUrlParams()` |
|
||||
| `\front\LayoutEngine` | Frontend layout engine — `show()`, tag replacement |
|
||||
| `\Shared\Helpers\Helpers` | Utility methods (SEO, email, cache clearing) |
|
||||
| `\Shared\Tpl\Tpl` | Template engine — `render()`, `set()` |
|
||||
| `\Shared\Cache\CacheHandler` | Redis cache — `get()`, `set()`, `delete()`, `deletePattern()` |
|
||||
| `\api\ApiRouter` | REST API router — auth, routing, response helpers |
|
||||
| `\Shared\Tpl\Tpl` | Template engine — `render()`, `set()` |
|
||||
| `\Shared\Cache\CacheHandler` | Redis cache — `get()`, `set()`, `delete()`, `deletePattern()` |
|
||||
| `\api\ApiRouter` | REST API router — auth, routing, response helpers |
|
||||
|
||||
### Database
|
||||
- ORM: Medoo (`$mdb` global variable, injected via DI in new code)
|
||||
@@ -179,7 +179,7 @@ Universal form system for admin edit views. Docs: `docs/FORM_EDIT_SYSTEM.md`.
|
||||
- Clear product cache: `\Shared\Helpers\Helpers::clear_product_cache($id)`
|
||||
- Pattern delete: `CacheHandler::deletePattern("shop\\product:{$id}:*")`
|
||||
- Default TTL: 86400 (24h)
|
||||
- Data is serialized — requires `unserialize()` after `get()`
|
||||
- Data is serialized — requires `unserialize()` after `get()`
|
||||
- Config: `config.php` (`$config['redis']`)
|
||||
|
||||
## Code Patterns
|
||||
@@ -203,7 +203,7 @@ $controller = new \admin\Controllers\ExampleController($repo);
|
||||
```
|
||||
|
||||
### Medoo ORM pitfalls
|
||||
- `$mdb->delete($table, $where)` takes **2 arguments**, NOT 3 — has caused bugs
|
||||
- `$mdb->delete($table, $where)` takes **2 arguments**, NOT 3 — has caused bugs
|
||||
- `$mdb->get()` returns `null` when no record, NOT `false`
|
||||
- After `$mdb->insert()`, check `$mdb->id()` to confirm success
|
||||
|
||||
@@ -222,18 +222,19 @@ $controller = new \admin\Controllers\ExampleController($repo);
|
||||
When user says **"KONIEC PRACY"**, run `/koniec-pracy` (see `.claude/commands/koniec-pracy.md`).
|
||||
|
||||
Before starting implementation, review current state of docs.
|
||||
For documentation updates, modify only .paul/docs/* unless the user explicitly asks to update root docs/*.
|
||||
|
||||
## Key Documentation
|
||||
- `docs/MEMORY.md` — project memory: known issues, confirmed patterns, ORM pitfalls, caching conventions
|
||||
- `docs/PROJECT_STRUCTURE.md` — current architecture, layers, cache, entry points, integrations
|
||||
- `docs/DATABASE_STRUCTURE.md` — full database schema
|
||||
- `docs/TESTING.md` — test suite guide and structure
|
||||
- `docs/FORM_EDIT_SYSTEM.md` — form system architecture
|
||||
- `docs/CHANGELOG.md` — version history
|
||||
- `api-docs/api-reference.json` — REST API documentation (ordersPRO)
|
||||
- `api-docs/index.html` — REST API documentation (ordersPRO)
|
||||
- `docs/UPDATE_INSTRUCTIONS.md` — how to build client update packages
|
||||
- `docs/MEMORY.md` — project memory: known issues, confirmed patterns, ORM pitfalls, caching conventions
|
||||
- `docs/PROJECT_STRUCTURE.md` — current architecture, layers, cache, entry points, integrations
|
||||
- `docs/DATABASE_STRUCTURE.md` — full database schema
|
||||
- `docs/TESTING.md` — test suite guide and structure
|
||||
- `docs/FORM_EDIT_SYSTEM.md` — form system architecture
|
||||
- `docs/CHANGELOG.md` — version history
|
||||
- `api-docs/api-reference.json` — REST API documentation (ordersPRO)
|
||||
- `api-docs/index.html` — REST API documentation (ordersPRO)
|
||||
- `docs/UPDATE_INSTRUCTIONS.md` — how to build client update packages
|
||||
|
||||
## Za każdym razem jak próbujesz sprawdzić jakiś plik z logami spróbuj go najpierw pobrać z serwera FTP
|
||||
## Za każdym razem jak próbujesz sprawdzić jakiś plik z logami spróbuj go najpierw pobrać z serwera FTP
|
||||
|
||||
## Wszystkie pliki które tworzysz jako pomocnicze, np build_0330.ps1 czy build-update.ps1 twórz w folderze temp
|
||||
## Wszystkie pliki ktĂłre tworzysz jako pomocnicze, np build_0330.ps1 czy build-update.ps1 twĂłrz w folderze temp
|
||||
|
||||
174
CLAUDE.md
174
CLAUDE.md
@@ -1,4 +1,4 @@
|
||||
# CLAUDE.md
|
||||
# CLAUDE.md
|
||||
|
||||
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
||||
|
||||
@@ -7,19 +7,19 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
|
||||
shopPRO is a PHP e-commerce platform with an admin panel and customer-facing storefront. It uses Medoo ORM (`$mdb`), Redis caching, and a Domain-Driven Design architecture with Dependency Injection (migration from legacy architecture complete).
|
||||
|
||||
## Zasady pisania kodu
|
||||
- Kod ma być czytelny „dla obcego”: jasne nazwy, mało magii
|
||||
- Brak „skrótów na szybko” typu logika w widokach, copy-paste, losowe helpery bez spójności
|
||||
- Każda funkcja/klasa ma mieć jedną odpowiedzialność, zwykle do 30–50 linii (jeśli dłuższe – dzielić)
|
||||
- max 3 poziomy zagnieżdżeń (if/foreach), reszta do osobnych metod
|
||||
- Kod ma być czytelny „dla obcego”: jasne nazwy, mało magii
|
||||
- Brak „skrótów na szybko” typu logika w widokach, copy-paste, losowe helpery bez spójności
|
||||
- Każda funkcja/klasa ma mieć jedną odpowiedzialność, zwykle do 30–50 linii (jeśli dłuższe – dzielić)
|
||||
- max 3 poziomy zagnieżdżeń (if/foreach), reszta do osobnych metod
|
||||
- Nazewnictwo:
|
||||
- klasy: PascalCase
|
||||
- metody/zmienne: camelCase
|
||||
- stałe: UPPER_SNAKE_CASE
|
||||
- Zero „skrótologii” w nazwach (np. $d, $tmp, $x1) poza pętlami 2–3 linijki
|
||||
- medoo + prepared statements bez wyjątków (żadnego sklejania SQL stringiem)
|
||||
- stałe: UPPER_SNAKE_CASE
|
||||
- Zero „skrótologii” w nazwach (np. $d, $tmp, $x1) poza pętlami 2–3 linijki
|
||||
- medoo + prepared statements bez wyjÄ…tkĂłw (ĹĽadnego sklejania SQL stringiem)
|
||||
- XSS: escape w widokach (np. helper e())
|
||||
- CSRF dla formularzy, sensowna obsługa sesji
|
||||
- Kod ma mieć komentarze tylko tam, gdzie wyjaśniają „dlaczego”, nie „co”
|
||||
- CSRF dla formularzy, sensowna obsługa sesji
|
||||
- Kod ma mieć komentarze tylko tam, gdzie wyjaśniają „dlaczego”, nie „co”
|
||||
|
||||
## PHP Version Constraint
|
||||
|
||||
@@ -36,7 +36,7 @@ shopPRO is a PHP e-commerce platform with an admin panel and customer-facing sto
|
||||
|
||||
### Running Tests
|
||||
```bash
|
||||
# Full suite (recommended — PowerShell, auto-finds php)
|
||||
# Full suite (recommended — PowerShell, auto-finds php)
|
||||
./test.ps1
|
||||
|
||||
# Specific file
|
||||
@@ -58,57 +58,57 @@ PHPUnit 9.6 via `phpunit.phar`. Bootstrap: `tests/bootstrap.php`. Config: `phpun
|
||||
Current suite: **823 tests, 2284 assertions**.
|
||||
|
||||
### Creating Updates
|
||||
See `docs/UPDATE_INSTRUCTIONS.md` for the full procedure. Updates are ZIP packages in `updates/0.XX/`. Never include `*.md` files, `updates/changelog.php`, or root `.htaccess` in update ZIPs. ZIP structure must start directly from project directories — no version subfolder inside the archive.
|
||||
See `docs/UPDATE_INSTRUCTIONS.md` for the full procedure. Updates are ZIP packages in `updates/0.XX/`. Never include `*.md` files, `updates/changelog.php`, or root `.htaccess` in update ZIPs. ZIP structure must start directly from project directories — no version subfolder inside the archive.
|
||||
|
||||
## Architecture
|
||||
|
||||
### Directory Structure
|
||||
```
|
||||
shopPRO/
|
||||
├── autoload/ # Autoloaded classes (core codebase)
|
||||
│ ├── Domain/ # Business logic repositories (\Domain\)
|
||||
│ ├── Shared/ # Shared utilities (\Shared\)
|
||||
│ │ ├── Cache/ # CacheHandler, RedisConnection
|
||||
│ │ ├── Email/ # Email (PHPMailer wrapper)
|
||||
│ │ ├── Helpers/ # Helpers (formerly class.S.php)
|
||||
│ │ ├── Html/ # Html utility
|
||||
│ │ ├── Image/ # ImageManipulator
|
||||
│ │ └── Tpl/ # Template engine
|
||||
│ ├── api/ # REST API layer (\api\)
|
||||
│ │ ├── ApiRouter.php # API router (\api\ApiRouter)
|
||||
│ │ └── Controllers/ # API controllers (\api\Controllers\)
|
||||
│ ├── admin/ # Admin panel layer
|
||||
│ │ ├── App.php # Admin router (\admin\App)
|
||||
│ │ ├── Controllers/ # DI controllers (\admin\Controllers\) — 28 controllers
|
||||
│ │ ├── Support/ # TableListRequestFactory, Forms/FormRequestHandler, Forms/FormFieldRenderer
|
||||
│ │ ├── Validation/ # FormValidator
|
||||
│ │ └── ViewModels/ # Forms/ (FormEditViewModel, FormField, FormTab, FormAction, FormFieldType), Common/ (PaginatedTableViewModel)
|
||||
│ └── front/ # Frontend layer
|
||||
│ ├── App.php # Frontend router (\front\App)
|
||||
│ ├── LayoutEngine.php # Layout engine (\front\LayoutEngine)
|
||||
│ ├── Controllers/ # DI controllers (\front\Controllers\) — 8 controllers
|
||||
│ └── Views/ # Static views (\front\Views\) — 11 view classes
|
||||
├── admin/ # Admin panel
|
||||
│ ├── templates/ # Admin view templates
|
||||
│ └── layout/ # Admin CSS/JS/icons
|
||||
├── templates/ # Frontend view templates
|
||||
├── libraries/ # Third-party libraries (Medoo, RedBeanPHP, PHPMailer)
|
||||
├── tests/ # PHPUnit tests
|
||||
│ ├── bootstrap.php
|
||||
│ ├── stubs/ # Test stubs (CacheHandler, Helpers, ShopProduct)
|
||||
│ └── Unit/
|
||||
│ ├── Domain/ # Repository tests
|
||||
│ ├── admin/Controllers/ # Controller tests
|
||||
│ └── api/ # API tests
|
||||
├── updates/ # Update packages for clients
|
||||
├── docs/ # Technical documentation
|
||||
├── config.php # Database/Redis config (not in repo)
|
||||
├── index.php # Frontend entry point
|
||||
├── ajax.php # Frontend AJAX handler
|
||||
├── admin/index.php # Admin entry point
|
||||
├── admin/ajax.php # Admin AJAX handler
|
||||
├── cron.php # CRON jobs (Apilo sync)
|
||||
└── api.php # REST API (ordersPRO + Ekomi)
|
||||
├── autoload/ # Autoloaded classes (core codebase)
|
||||
│ ├── Domain/ # Business logic repositories (\Domain\)
|
||||
│ ├── Shared/ # Shared utilities (\Shared\)
|
||||
│ │ ├── Cache/ # CacheHandler, RedisConnection
|
||||
│ │ ├── Email/ # Email (PHPMailer wrapper)
|
||||
│ │ ├── Helpers/ # Helpers (formerly class.S.php)
|
||||
│ │ ├── Html/ # Html utility
|
||||
│ │ ├── Image/ # ImageManipulator
|
||||
│ │ └── Tpl/ # Template engine
|
||||
│ ├── api/ # REST API layer (\api\)
|
||||
│ │ ├── ApiRouter.php # API router (\api\ApiRouter)
|
||||
│ │ └── Controllers/ # API controllers (\api\Controllers\)
|
||||
│ ├── admin/ # Admin panel layer
|
||||
│ │ ├── App.php # Admin router (\admin\App)
|
||||
│ │ ├── Controllers/ # DI controllers (\admin\Controllers\) — 28 controllers
|
||||
│ │ ├── Support/ # TableListRequestFactory, Forms/FormRequestHandler, Forms/FormFieldRenderer
|
||||
│ │ ├── Validation/ # FormValidator
|
||||
│ │ └── ViewModels/ # Forms/ (FormEditViewModel, FormField, FormTab, FormAction, FormFieldType), Common/ (PaginatedTableViewModel)
|
||||
│ └── front/ # Frontend layer
|
||||
│ ├── App.php # Frontend router (\front\App)
|
||||
│ ├── LayoutEngine.php # Layout engine (\front\LayoutEngine)
|
||||
│ ├── Controllers/ # DI controllers (\front\Controllers\) — 8 controllers
|
||||
│ └── Views/ # Static views (\front\Views\) — 11 view classes
|
||||
├── admin/ # Admin panel
|
||||
│ ├── templates/ # Admin view templates
|
||||
│ └── layout/ # Admin CSS/JS/icons
|
||||
├── templates/ # Frontend view templates
|
||||
├── libraries/ # Third-party libraries (Medoo, RedBeanPHP, PHPMailer)
|
||||
├── tests/ # PHPUnit tests
|
||||
│ ├── bootstrap.php
|
||||
│ ├── stubs/ # Test stubs (CacheHandler, Helpers, ShopProduct)
|
||||
│ └── Unit/
|
||||
│ ├── Domain/ # Repository tests
|
||||
│ ├── admin/Controllers/ # Controller tests
|
||||
│ └── api/ # API tests
|
||||
├── updates/ # Update packages for clients
|
||||
├── docs/ # Technical documentation
|
||||
├── config.php # Database/Redis config (not in repo)
|
||||
├── index.php # Frontend entry point
|
||||
├── ajax.php # Frontend AJAX handler
|
||||
├── admin/index.php # Admin entry point
|
||||
├── admin/ajax.php # Admin AJAX handler
|
||||
├── cron.php # CRON jobs (Apilo sync)
|
||||
└── api.php # REST API (ordersPRO + Ekomi)
|
||||
```
|
||||
|
||||
### Autoloader
|
||||
@@ -118,19 +118,19 @@ Custom autoloader in each entry point (not Composer autoload at runtime). Tries
|
||||
2. `autoload/{namespace}/{ClassName}.php` (PSR-4 style, fallback)
|
||||
|
||||
### Namespace Conventions (case-sensitive on Linux!)
|
||||
- `\Domain\` → `autoload/Domain/` (uppercase D)
|
||||
- `\admin\Controllers\` → `autoload/admin/Controllers/` (lowercase a)
|
||||
- `\Shared\` → `autoload/Shared/`
|
||||
- `\api\` → `autoload/api/`
|
||||
- Do NOT use `\Admin\` (uppercase A) — the server directory is `admin/` (lowercase)
|
||||
- `\shop\` namespace is **deleted** — all 12 legacy classes migrated to `\Domain\`, `autoload/shop/` directory removed
|
||||
- `\Domain\` → `autoload/Domain/` (uppercase D)
|
||||
- `\admin\Controllers\` → `autoload/admin/Controllers/` (lowercase a)
|
||||
- `\Shared\` → `autoload/Shared/`
|
||||
- `\api\` → `autoload/api/`
|
||||
- Do NOT use `\Admin\` (uppercase A) — the server directory is `admin/` (lowercase)
|
||||
- `\shop\` namespace is **deleted** — all 12 legacy classes migrated to `\Domain\`, `autoload/shop/` directory removed
|
||||
|
||||
### Domain-Driven Architecture (migration complete)
|
||||
|
||||
All legacy directories (`admin/controls/`, `admin/factory/`, `admin/view/`, `front/controls/`, `front/view/`, `front/factory/`, `shop/`) have been deleted. All modules now use this pattern:
|
||||
|
||||
**Domain Layer** (`autoload/Domain/{Module}/`):
|
||||
- `{Module}Repository.php` — data access, business logic, Redis caching
|
||||
- `{Module}Repository.php` — data access, business logic, Redis caching
|
||||
- Constructor DI with `$db` (Medoo instance)
|
||||
- Methods serve both admin and frontend (shared Domain, no separate services)
|
||||
|
||||
@@ -145,7 +145,7 @@ All legacy directories (`admin/controls/`, `admin/factory/`, `admin/view/`, `fro
|
||||
- Wired in `front\App::getControllerFactories()`
|
||||
|
||||
**Frontend Views** (`autoload/front/Views/`):
|
||||
- Static classes, no state, no DI — pure rendering
|
||||
- Static classes, no state, no DI — pure rendering
|
||||
|
||||
**API Controllers** (`autoload/api/Controllers/`):
|
||||
- DI via constructor, stateless (no session)
|
||||
@@ -155,13 +155,13 @@ All legacy directories (`admin/controls/`, `admin/factory/`, `admin/view/`, `fro
|
||||
### Key Classes
|
||||
| Class | Purpose |
|
||||
|-------|---------|
|
||||
| `\admin\App` | Admin router — maps URL segments to controllers |
|
||||
| `\front\App` | Frontend router — `route()`, `checkUrlParams()` |
|
||||
| `\front\LayoutEngine` | Frontend layout engine — `show()`, tag replacement |
|
||||
| `\admin\App` | Admin router — maps URL segments to controllers |
|
||||
| `\front\App` | Frontend router — `route()`, `checkUrlParams()` |
|
||||
| `\front\LayoutEngine` | Frontend layout engine — `show()`, tag replacement |
|
||||
| `\Shared\Helpers\Helpers` | Utility methods (SEO, email, cache clearing) |
|
||||
| `\Shared\Tpl\Tpl` | Template engine — `render()`, `set()` |
|
||||
| `\Shared\Cache\CacheHandler` | Redis cache — `get()`, `set()`, `delete()`, `deletePattern()` |
|
||||
| `\api\ApiRouter` | REST API router — auth, routing, response helpers |
|
||||
| `\Shared\Tpl\Tpl` | Template engine — `render()`, `set()` |
|
||||
| `\Shared\Cache\CacheHandler` | Redis cache — `get()`, `set()`, `delete()`, `deletePattern()` |
|
||||
| `\api\ApiRouter` | REST API router — auth, routing, response helpers |
|
||||
|
||||
### Database
|
||||
- ORM: Medoo (`$mdb` global variable, injected via DI in new code)
|
||||
@@ -183,7 +183,7 @@ Universal form system for admin edit views. Docs: `docs/FORM_EDIT_SYSTEM.md`.
|
||||
- Clear product cache: `\Shared\Helpers\Helpers::clear_product_cache($id)`
|
||||
- Pattern delete: `CacheHandler::deletePattern("shop\\product:{$id}:*")`
|
||||
- Default TTL: 86400 (24h)
|
||||
- Data is serialized — requires `unserialize()` after `get()`
|
||||
- Data is serialized — requires `unserialize()` after `get()`
|
||||
- Config: `config.php` (`$config['redis']`)
|
||||
|
||||
## Code Patterns
|
||||
@@ -207,7 +207,7 @@ $controller = new \admin\Controllers\ExampleController($repo);
|
||||
```
|
||||
|
||||
### Medoo ORM pitfalls
|
||||
- `$mdb->delete($table, $where)` takes **2 arguments**, NOT 3 — has caused bugs
|
||||
- `$mdb->delete($table, $where)` takes **2 arguments**, NOT 3 — has caused bugs
|
||||
- `$mdb->get()` returns `null` when no record, NOT `false`
|
||||
- After `$mdb->insert()`, check `$mdb->id()` to confirm success
|
||||
|
||||
@@ -226,21 +226,23 @@ $controller = new \admin\Controllers\ExampleController($repo);
|
||||
When user says **"KONIEC PRACY"**, run `/koniec-pracy` (see `.claude/commands/koniec-pracy.md`).
|
||||
|
||||
Before starting implementation, review current state of docs.
|
||||
For documentation updates, modify only .paul/docs/* unless the user explicitly asks to update root docs/*.
|
||||
|
||||
## Key Documentation
|
||||
- `docs/MEMORY.md` — project memory: known issues, confirmed patterns, ORM pitfalls, caching conventions
|
||||
- `docs/PROJECT_STRUCTURE.md` — current architecture, layers, cache, entry points, integrations
|
||||
- `docs/DATABASE_STRUCTURE.md` — full database schema
|
||||
- `docs/TESTING.md` — test suite guide and structure
|
||||
- `docs/FORM_EDIT_SYSTEM.md` — form system architecture
|
||||
- `docs/CLASS_CATALOG.md` — full catalog of all classes with descriptions
|
||||
- `docs/TODO.md` — outstanding tasks and planned features
|
||||
- `docs/CRON_QUEUE_PLAN.md` — planned cron/queue architecture
|
||||
- `docs/CHANGELOG.md` — version history
|
||||
- `api-docs/api-reference.json` — REST API documentation (ordersPRO)
|
||||
- `api-docs/index.html` — REST API documentation (ordersPRO)
|
||||
- `docs/UPDATE_INSTRUCTIONS.md` — how to build client update packages
|
||||
- `docs/MEMORY.md` — project memory: known issues, confirmed patterns, ORM pitfalls, caching conventions
|
||||
- `docs/PROJECT_STRUCTURE.md` — current architecture, layers, cache, entry points, integrations
|
||||
- `docs/DATABASE_STRUCTURE.md` — full database schema
|
||||
- `docs/TESTING.md` — test suite guide and structure
|
||||
- `docs/FORM_EDIT_SYSTEM.md` — form system architecture
|
||||
- `docs/CLASS_CATALOG.md` — full catalog of all classes with descriptions
|
||||
- `docs/TODO.md` — outstanding tasks and planned features
|
||||
- `docs/CRON_QUEUE_PLAN.md` — planned cron/queue architecture
|
||||
- `docs/CHANGELOG.md` — version history
|
||||
- `api-docs/api-reference.json` — REST API documentation (ordersPRO)
|
||||
- `api-docs/index.html` — REST API documentation (ordersPRO)
|
||||
- `docs/UPDATE_INSTRUCTIONS.md` — how to build client update packages
|
||||
|
||||
## Za każdym razem jak próbujesz sprawdzić jakiś plik z logami spróbuj go najpierw pobrać z serwera FTP
|
||||
## Za każdym razem jak próbujesz sprawdzić jakiś plik z logami spróbuj go najpierw pobrać z serwera FTP
|
||||
|
||||
## Wszystkie pliki ktĂłre tworzysz jako pomocnicze, np build_0330.ps1 czy build-update.ps1 twĂłrz w folderze temp
|
||||
|
||||
## Wszystkie pliki które tworzysz jako pomocnicze, np build_0330.ps1 czy build-update.ps1 twórz w folderze temp
|
||||
|
||||
@@ -1,79 +0,0 @@
|
||||
# Refaktoryzacja autoloadera — centralizacja
|
||||
|
||||
## Problem
|
||||
|
||||
Funkcja `__autoload_my_classes()` jest zduplikowana w każdym entry poincie:
|
||||
- `index.php`
|
||||
- `admin/index.php`
|
||||
- `admin/ajax.php`
|
||||
- `ajax.php`
|
||||
- `api.php`
|
||||
- `cron.php`
|
||||
- `download.php`
|
||||
- `cron-turstmate.php`
|
||||
- `cron/cron-xml.php`
|
||||
|
||||
Każdy plik zawiera identyczną (lub prawie identyczną) kopię autoloadera. Warianty różnią się ścieżką bazową (`autoload/` vs `../autoload/`) i drobnymi detalami (np. Savant3 special case tylko w `admin/ajax.php`).
|
||||
|
||||
## Rozwiązanie
|
||||
|
||||
Utworzyć centralny plik `autoload/autoloader.php` z jedną definicją autoloadera, używając `__DIR__` zamiast ścieżek relatywnych.
|
||||
|
||||
### Plik: `autoload/autoloader.php`
|
||||
|
||||
```php
|
||||
<?php
|
||||
function __autoload_my_classes( $classname )
|
||||
{
|
||||
$base = __DIR__ . '/';
|
||||
|
||||
$q = explode( '\\', $classname );
|
||||
$c = array_pop( $q );
|
||||
|
||||
// Savant3 — special case
|
||||
if ( $c == 'Savant3' )
|
||||
{
|
||||
$f = $base . 'Savant3.php';
|
||||
if ( file_exists( $f ) ) { require_once( $f ); return; }
|
||||
}
|
||||
|
||||
$path = implode( '/', $q );
|
||||
|
||||
// 1. Legacy: class.ClassName.php
|
||||
$f = $base . $path . '/class.' . $c . '.php';
|
||||
if ( file_exists( $f ) ) { require_once( $f ); return; }
|
||||
|
||||
// 2. PSR-4: ClassName.php
|
||||
$f = $base . $path . '/' . $c . '.php';
|
||||
if ( file_exists( $f ) ) require_once( $f );
|
||||
}
|
||||
|
||||
spl_autoload_register( '__autoload_my_classes' );
|
||||
```
|
||||
|
||||
### Zmiana w entry pointach
|
||||
|
||||
Zamienić definicję funkcji + `spl_autoload_register` na jedną linię:
|
||||
|
||||
```php
|
||||
// Root entry points (index.php, ajax.php, api.php, cron.php, download.php):
|
||||
require_once __DIR__ . '/autoload/autoloader.php';
|
||||
|
||||
// Admin entry points (admin/index.php, admin/ajax.php):
|
||||
require_once __DIR__ . '/../autoload/autoloader.php';
|
||||
|
||||
// Cron subdirectory (cron/cron-xml.php):
|
||||
require_once __DIR__ . '/../autoload/autoloader.php';
|
||||
```
|
||||
|
||||
## Korzyści
|
||||
|
||||
- **DRY** — jedna definicja zamiast 9+ kopii
|
||||
- **Bezpieczeństwo** — poprawka w jednym miejscu działa wszędzie
|
||||
- **`__DIR__`** — ścieżki absolutne, niezależne od cwd
|
||||
- **Savant3** — obsłużony centralnie, nie tylko w admin/ajax.php
|
||||
|
||||
## Uwagi
|
||||
|
||||
- Reszta kodu w entry pointach (config.php, medoo, session) pozostaje bez zmian
|
||||
- Rozwiązanie wdrożone i przetestowane w cmsPRO
|
||||
@@ -1,147 +0,0 @@
|
||||
# CARL — Dynamiczne reguły dla Claude Code
|
||||
|
||||
[CARL](https://github.com/ChristopherKahler/carl) (Context Augmentation & Reinforcement Layer) to system wstrzykiwania reguł do Claude Code — reguły ładują się **tylko gdy są potrzebne**, zamiast zapychać kontekst sesji regułami, których akurat nie używasz.
|
||||
|
||||
Zainstalowany globalnie w `~/.carl/`.
|
||||
|
||||
---
|
||||
|
||||
## Jak to działa
|
||||
|
||||
Claude Code ma ograniczony kontekst sesji. CARL rozwiązuje problem "zbyt wielu reguł na raz":
|
||||
|
||||
- **Domeny** — zestawy reguł uruchamiane automatycznie przez słowa kluczowe w prompcie
|
||||
- **Star-commands** (`*dev`, `*review`, itp.) — tryby uruchamiane ręcznie przez wpisanie `*nazwatrybe`
|
||||
- **Global** — reguły zawsze aktywne (minimalne, uniwersalne)
|
||||
|
||||
Efekt: zamiast 50 reguł na każdą sesję, Claude dostaje 5–10 tych, które są relevantne teraz.
|
||||
|
||||
---
|
||||
|
||||
## Struktura plików
|
||||
|
||||
```
|
||||
~/.carl/ # Globalna konfiguracja (wszystkie projekty)
|
||||
├── manifest # Rejestr domen (stany + słowa kluczowe)
|
||||
├── global # Reguły zawsze aktywne
|
||||
├── commands # Definicje star-commands
|
||||
├── context # Reguły kontekstowe (rozmiar okna kontekstu)
|
||||
└── {nazwa-domeny} # Twoja domena (bez rozszerzenia!)
|
||||
|
||||
.carl/ # Konfiguracja lokalna (tylko ten projekt)
|
||||
└── {nazwa-domeny} # Reguły specyficzne dla shopPRO
|
||||
```
|
||||
|
||||
**Ważne:** Nazwy plików domen — **małe litery, bez rozszerzenia** (`phpdev`, nie `phpdev.carl`).
|
||||
|
||||
---
|
||||
|
||||
## Dostępne star-commands
|
||||
|
||||
Wpisz `*nazwa` na początku wiadomości lub w środku promptu, żeby przełączyć tryb:
|
||||
|
||||
| Komenda | Tryb | Kiedy używać |
|
||||
|---------|------|-------------|
|
||||
| `*dev` | Development | Implementacja, szybkie zmiany bez tłumaczeń |
|
||||
| `*review` | Code review | Przegląd kodu, bezpieczeństwo, edge cases |
|
||||
| `*brief` | Zwięzłe odpowiedzi | Tylko bullet points, bez elaboracji |
|
||||
| `*plan` | Planowanie | Eksploracja przed implementacją, opcje + tradeoffs |
|
||||
| `*discuss` | Dyskusja | Burza mózgów, wiele podejść, bez skakania do kodu |
|
||||
| `*debug` | Debugowanie | Systematyczna diagnoza, root cause analysis |
|
||||
| `*explain` | Wyjaśnianie | Nauka, koncepty, stopniowe budowanie wiedzy |
|
||||
| `*carl` | Pomoc CARL | Zarządzanie domenami, konfiguracja, pytania o CARL |
|
||||
|
||||
### Przykłady
|
||||
|
||||
```
|
||||
*dev Napraw błąd w summaryView() gdzie duplikuje zamówienia
|
||||
|
||||
*review Przejrzyj OrderRepository::createFromBasket pod kątem bezpieczeństwa
|
||||
|
||||
*brief Co robi CacheHandler::deletePattern()
|
||||
|
||||
*plan Chcę dodać system rabatów do koszyka
|
||||
|
||||
*discuss Czy lepiej rozdzielić ApiloRepository na sync i admin?
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Tworzenie własnej domeny (projekt)
|
||||
|
||||
Kiedy masz zestaw reguł specyficznych dla shopPRO, utwórz lokalną domenę w `.carl/`.
|
||||
|
||||
### Krok 1 — Plik domeny
|
||||
|
||||
Utwórz `.carl/shoppro` (bez rozszerzenia):
|
||||
|
||||
```
|
||||
# shopPRO Domain Rules
|
||||
SHOPPRO_RULE_0=PHP < 8.0 — nie używaj match, named args, union types, str_contains
|
||||
SHOPPRO_RULE_1=ORM: Medoo ($mdb), zawsze prepared statements, nigdy string concatenation
|
||||
SHOPPRO_RULE_2=Namespace \Domain\ mapuje do autoload/Domain/ (D uppercase, a lowercase)
|
||||
SHOPPRO_RULE_3=Testy: PHPUnit 9.6, pattern AAA, mock Medoo przez createMock(\medoo::class)
|
||||
SHOPPRO_RULE_4=Cache: CacheHandler::deletePattern() do kasowania, TTL 86400, dane serialized
|
||||
```
|
||||
|
||||
### Krok 2 — Wpis w manifeście
|
||||
|
||||
Dodaj do `.carl/manifest` (lub `~/.carl/manifest` jeśli globalna):
|
||||
|
||||
```
|
||||
SHOPPRO_STATE=active
|
||||
SHOPPRO_RECALL=shopPRO, medoo, zamówienie, koszyk, OrderRepository, Domain
|
||||
SHOPPRO_EXCLUDE=
|
||||
SHOPPRO_ALWAYS_ON=false
|
||||
```
|
||||
|
||||
- `RECALL` — słowa kluczowe które triggerują domenę (przecinek = OR)
|
||||
- `ALWAYS_ON=true` — ładuj przy każdym prompcie (tylko dla naprawdę universalnych reguł)
|
||||
- `EXCLUDE` — słowa które blokują domenę mimo dopasowania RECALL
|
||||
|
||||
### Krok 3 — Weryfikacja
|
||||
|
||||
Wpisz `*carl` w czacie i zapytaj: _"Pokaż mi aktywne domeny"_.
|
||||
|
||||
---
|
||||
|
||||
## Zarządzanie przez Claude
|
||||
|
||||
Zamiast ręcznie edytować pliki, możesz zarządzać CARL przez Claude:
|
||||
|
||||
```
|
||||
*carl Dodaj domenę dla testów PHPUnit w shopPRO
|
||||
|
||||
*carl Pokaż moją aktualną konfigurację
|
||||
|
||||
*carl Wyłącz domenę SHOPPRO tymczasowo
|
||||
|
||||
*carl Dodaj regułę do domeny dev: zawsze uruchamiaj ./test.ps1 po zmianach
|
||||
```
|
||||
|
||||
Claude użyje skills `carl:manager` / `carl:tasks:*` do operacji na plikach.
|
||||
|
||||
---
|
||||
|
||||
## Integracja z PAUL
|
||||
|
||||
CARL i [PAUL](./PAUL_WORKFLOW.md) działają uzupełniająco:
|
||||
|
||||
- **PAUL** strukturyzuje *proces* (plan → apply → unify)
|
||||
- **CARL** dostarcza *reguły domenowe* wtedy gdy są potrzebne
|
||||
|
||||
Praktycznie: podczas `/paul:apply` możesz prefixować `*dev` żeby Claude skupił się na kodzie bez elaboracji. Podczas `/paul:discuss` — `*discuss` żeby dostać pełną analizę opcji.
|
||||
|
||||
---
|
||||
|
||||
## Dobre praktyki
|
||||
|
||||
- **RECALL słowa** — używaj konkretnych, rzadkich słów żeby unikać false triggers. `medoo` lepsze niż `php`.
|
||||
- **Mało reguł per domena** — 5–8 reguł to optymalnie. Więcej = wolniejsze matching, więcej tokenów.
|
||||
- **ALWAYS_ON=false** domyślnie — ALWAYS_ON=true tylko dla reguł naprawdę universalnych (jak GLOBAL).
|
||||
- **Star-commands przy dużych taskach** — na początku sesji wpisz `*dev` lub `*plan` żeby ustawić tryb.
|
||||
- **Nie duplikuj CLAUDE.md** — CARL nie zastępuje CLAUDE.md. CLAUDE.md to architektura projektu. CARL to reguły zachowania Claude.
|
||||
|
||||
---
|
||||
|
||||
*Docs: 2026-03-12*
|
||||
1424
docs/CHANGELOG.md
1424
docs/CHANGELOG.md
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,156 +0,0 @@
|
||||
# Plan: System kolejki zadań cron oparty o bazę danych
|
||||
|
||||
## Kontekst
|
||||
|
||||
Obecny system cron ma dwa problemy:
|
||||
1. **Kolejka plikowa (JSON)** — sync płatności/statusów Apilo trzymany w `/temp/apilo-sync-queue.json` — kruchy, brak transakcji, ryzyko utraty danych
|
||||
2. **Monolityczny cron.php** (~550 linii) — brak priorytetów, brak retry z backoff, brak centralnego zarządzania
|
||||
|
||||
Cel: Zastąpienie całego systemu cron tabelą `pp_cron_jobs` z priorytetami, retry/backoff i harmonogramem `pp_cron_schedules`.
|
||||
|
||||
## Nowe pliki
|
||||
|
||||
| Plik | Opis |
|
||||
|------|------|
|
||||
| `autoload/Domain/CronJob/CronJobType.php` | Stałe typów zadań i priorytetów |
|
||||
| `autoload/Domain/CronJob/CronJobRepository.php` | CRUD na `pp_cron_jobs` + `pp_cron_schedules` |
|
||||
| `autoload/Domain/CronJob/CronJobProcessor.php` | Orkiestracja: pobierz zadanie → wywołaj handler → obsłuż wynik |
|
||||
| `tests/Unit/Domain/CronJob/CronJobTypeTest.php` | Testy stałych |
|
||||
| `tests/Unit/Domain/CronJob/CronJobRepositoryTest.php` | Testy repozytorium |
|
||||
| `tests/Unit/Domain/CronJob/CronJobProcessorTest.php` | Testy procesora |
|
||||
| `migrations/0.315.sql` | CREATE TABLE + INSERT harmonogramów |
|
||||
|
||||
## Modyfikowane pliki
|
||||
|
||||
| Plik | Zmiana |
|
||||
|------|--------|
|
||||
| `cron.php` | Zastąpienie ~550 linii orchestratorem (~100 linii) z rejestracją handlerów |
|
||||
| `cron/cron-xml.php` | Usunięcie — logika przeniesiona do handlera `google_xml_feed` |
|
||||
| `cron-turstmate.php` | Usunięcie — logika przeniesiona do handlera `trustmate_invitation` |
|
||||
| `autoload/Domain/Order/OrderAdminService.php` | `queueApiloSync()` → enqueue do DB; usunięcie metod plikowych; `syncApiloPayment()`/`syncApiloStatus()` → public |
|
||||
| `tests/Unit/Domain/Order/OrderAdminServiceTest.php` | Refaktor testów kolejki: mock `CronJobRepository` zamiast pliku JSON |
|
||||
| `docs/DATABASE_STRUCTURE.md` | Dodanie tabel `pp_cron_jobs`, `pp_cron_schedules` |
|
||||
| `docs/CHANGELOG.md` | Wpis o nowym systemie |
|
||||
|
||||
## Schemat DB (`migrations/0.315.sql`)
|
||||
|
||||
### `pp_cron_jobs`
|
||||
```sql
|
||||
CREATE TABLE pp_cron_jobs (
|
||||
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
|
||||
job_type VARCHAR(50) NOT NULL,
|
||||
status ENUM('pending','processing','completed','failed','cancelled') NOT NULL DEFAULT 'pending',
|
||||
priority TINYINT UNSIGNED NOT NULL DEFAULT 100, -- niższy = ważniejszy
|
||||
payload TEXT NULL, -- JSON z danymi zadania
|
||||
result TEXT NULL, -- JSON z wynikiem
|
||||
attempts SMALLINT UNSIGNED NOT NULL DEFAULT 0,
|
||||
max_attempts SMALLINT UNSIGNED NOT NULL DEFAULT 10,
|
||||
last_error VARCHAR(500) NULL,
|
||||
scheduled_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
started_at DATETIME NULL,
|
||||
completed_at DATETIME NULL,
|
||||
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
INDEX idx_status_priority_scheduled (status, priority, scheduled_at),
|
||||
INDEX idx_job_type (job_type),
|
||||
INDEX idx_status (status)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
```
|
||||
|
||||
### `pp_cron_schedules`
|
||||
```sql
|
||||
CREATE TABLE pp_cron_schedules (
|
||||
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
|
||||
job_type VARCHAR(50) NOT NULL UNIQUE,
|
||||
interval_seconds INT UNSIGNED NOT NULL,
|
||||
priority TINYINT UNSIGNED NOT NULL DEFAULT 100,
|
||||
max_attempts SMALLINT UNSIGNED NOT NULL DEFAULT 3,
|
||||
payload TEXT NULL,
|
||||
enabled TINYINT(1) NOT NULL DEFAULT 1,
|
||||
last_run_at DATETIME NULL,
|
||||
next_run_at DATETIME NULL,
|
||||
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
INDEX idx_enabled_next_run (enabled, next_run_at)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
```
|
||||
|
||||
## Typy zadań i priorytety
|
||||
|
||||
| Typ | Priorytet | Harmonogram |
|
||||
|-----|-----------|-------------|
|
||||
| `apilo_token_keepalive` | 10 (krytyczny) | co 4 min |
|
||||
| `apilo_send_order` | 50 (wysoki) | co 1 min |
|
||||
| `apilo_sync_payment` | 50 (wysoki) | event-driven (enqueue przy zmianie) |
|
||||
| `apilo_sync_status` | 50 (wysoki) | event-driven |
|
||||
| `apilo_product_sync` | 100 (normalny) | co 10 min |
|
||||
| `apilo_pricelist_sync` | 100 (normalny) | co 1h |
|
||||
| `apilo_status_poll` | 100 (normalny) | co 10 min |
|
||||
| `price_history` | 100 (normalny) | co 24h |
|
||||
| `order_analysis` | 100 (normalny) | co 10 min |
|
||||
| `trustmate_invitation` | 200 (niski) | co 10 min |
|
||||
| `google_xml_feed` | 200 (niski) | co 1h |
|
||||
|
||||
## Architektura klas
|
||||
|
||||
### CronJobRepository — metody kluczowe
|
||||
- `enqueue($jobType, $payload, $priority, $maxAttempts, $scheduledAt)` — dodaj do kolejki
|
||||
- `fetchNext($limit)` — atomowe pobranie pending jobs (UPDATE WHERE status='pending')
|
||||
- `markCompleted($jobId, $result)` / `markFailed($jobId, $error, $backoffSeconds)`
|
||||
- `hasPendingJob($jobType, $payloadMatch)` — zapobiega duplikatom
|
||||
- `cleanup($olderThanDays)` — GC starych wpisów
|
||||
- `recoverStuck($olderThanMinutes)` — reset stuck "processing" jobs
|
||||
- `getDueSchedules()` / `touchSchedule($id)` — harmonogram
|
||||
|
||||
### CronJobProcessor — orkiestracja
|
||||
- `registerHandler($jobType, callable)` — rejestracja handlera
|
||||
- `createScheduledJobs()` — tworzy jobs z harmonogramów których `next_run_at <= NOW`
|
||||
- `processQueue($limit)` — pobierz + wywołaj handler + markCompleted/markFailed
|
||||
- `run($limit)` — główna metoda: schedules + process
|
||||
|
||||
### Exponential backoff
|
||||
```
|
||||
Próba 1: 60s, Próba 2: 120s, Próba 3: 240s, ... max 3600s (1h)
|
||||
```
|
||||
|
||||
### Zależność "order not yet in Apilo"
|
||||
Handler `apilo_sync_payment`/`apilo_sync_status` sprawdza `apilo_order_id`. Jeśli brak → zwraca false → `markFailed()` z backoffem → zadanie wraca do kolejki. Max 50 prób.
|
||||
|
||||
## Nowy cron.php (schemat)
|
||||
|
||||
```php
|
||||
$cronRepo = new \Domain\CronJob\CronJobRepository($mdb);
|
||||
$processor = new \Domain\CronJob\CronJobProcessor($mdb, $cronRepo);
|
||||
|
||||
// Rejestracja handlerów (każdy to callable)
|
||||
$processor->registerHandler('apilo_token_keepalive', function($payload) use ($integrationsRepo) { ... });
|
||||
$processor->registerHandler('apilo_send_order', function($payload) use ($orderService, ...) { ... });
|
||||
// ... inne handlery
|
||||
|
||||
$result = $processor->run(20);
|
||||
```
|
||||
|
||||
## Zmiany w OrderAdminService
|
||||
|
||||
1. `queueApiloSync()` → `CronJobRepository::enqueue()` zamiast zapisu do pliku JSON
|
||||
2. Usunięcie: `loadApiloSyncQueue()`, `saveApiloSyncQueue()`, `apiloSyncQueuePath()`, stała `APILO_SYNC_QUEUE_FILE`
|
||||
3. `syncApiloPayment()`, `syncApiloStatus()` → zmiana z `private` na `public`
|
||||
4. Jednorazowa migracja: odczyt JSON → insert do DB → usunięcie pliku
|
||||
|
||||
## Kolejność implementacji
|
||||
|
||||
1. Migracja SQL
|
||||
2. `CronJobType.php`
|
||||
3. `CronJobRepository.php` + testy
|
||||
4. `CronJobProcessor.php` + testy
|
||||
5. Modyfikacja `OrderAdminService` (queue → DB, public methods)
|
||||
6. Jednorazowa migracja pliku JSON → DB
|
||||
7. Nowy `cron.php` z handlerami (ekstrakcja logiki z bloków proceduralnych)
|
||||
8. Aktualizacja testów OrderAdminService
|
||||
9. Dokumentacja (DATABASE_STRUCTURE.md, CHANGELOG.md)
|
||||
|
||||
## Weryfikacja
|
||||
|
||||
1. Uruchomienie pełnego zestawu testów: `./test.ps1`
|
||||
2. Sprawdzenie czy nowe testy CronJob* przechodzą
|
||||
3. Sprawdzenie czy istniejące testy OrderAdminService przechodzą po refaktorze
|
||||
4. Weryfikacja migracji SQL na pustej bazie
|
||||
@@ -1,730 +0,0 @@
|
||||
# Struktura bazy danych shopPRO
|
||||
|
||||
Plik aktualizowany na bieżąco przy zmianach w kodzie.
|
||||
ORM: Medoo (`$mdb`), prefix tabel: `pp_`
|
||||
|
||||
## pp_shop_products
|
||||
Główna tabela produktów.
|
||||
|
||||
| Kolumna | Opis |
|
||||
|---------|------|
|
||||
| id | PK |
|
||||
| parent_id | FK do produktu nadrzędnego (kombinacje) - NULL dla produktów głównych |
|
||||
| price_brutto | Cena brutto |
|
||||
| price_brutto_promo | Cena promocyjna brutto |
|
||||
| quantity | Stan magazynowy |
|
||||
| status | Status: 1 = aktywny, 0 = nieaktywny |
|
||||
| archive | Archiwum: 1 = zarchiwizowany, 0 = aktywny |
|
||||
| promoted | Czy promowany |
|
||||
| vat | Stawka VAT |
|
||||
| ean | Kod EAN |
|
||||
| sku | Kod SKU |
|
||||
| apilo_product_id | ID produktu w Apilo |
|
||||
| apilo_product_name | Nazwa produktu w Apilo |
|
||||
|
||||
**Używane w:** `Domain\Product\ProductRepository`, `admin\factory\ShopProduct`, `admin\Controllers\ShopProductController`
|
||||
|
||||
## pp_shop_products_langs
|
||||
Tłumaczenia produktów (per język).
|
||||
|
||||
| Kolumna | Opis |
|
||||
|---------|------|
|
||||
| id | PK |
|
||||
| product_id | FK do pp_shop_products |
|
||||
| lang_id | ID języka (np. 'pl') |
|
||||
| name | Nazwa produktu |
|
||||
|
||||
**Używane w:** `Domain\Product\ProductRepository::getName()`
|
||||
|
||||
## pp_shop_products_images
|
||||
Zdjęcia produktów.
|
||||
|
||||
| Kolumna | Opis |
|
||||
|---------|------|
|
||||
| id | PK |
|
||||
| product_id | FK do pp_shop_products |
|
||||
| src | Ścieżka do pliku |
|
||||
| alt | Tekst alternatywny |
|
||||
|
||||
## pp_shop_products_custom_fields
|
||||
Dodatkowe pola produktów (custom fields).
|
||||
|
||||
| Kolumna | Opis |
|
||||
|---------|------|
|
||||
| id_additional_field | PK |
|
||||
| id_product | FK do pp_shop_products |
|
||||
| name | Nazwa pola |
|
||||
| type | Typ pola (VARCHAR 30) |
|
||||
| is_required | Czy wymagane (0/1) |
|
||||
|
||||
## pp_shop_products_categories
|
||||
Przypisanie produktów do kategorii.
|
||||
|
||||
| Kolumna | Opis |
|
||||
|---------|------|
|
||||
| product_id | FK do pp_shop_products |
|
||||
|
||||
**Używane w:** `admin\factory\ShopProduct::product_delete()`, `Domain\Product\ProductRepository::getProductsByCategory()`
|
||||
|
||||
**Aktualizacja 2026-02-15 (ver. 0.274):** akcje `/admin/shop_product/mass_edit/*` korzystają z `Domain\Product\ProductRepository` przez `admin\Controllers\ShopProductController`.
|
||||
|
||||
## pp_shop_categories
|
||||
Kategorie sklepu.
|
||||
|
||||
| Kolumna | Opis |
|
||||
|---------|------|
|
||||
| id | PK |
|
||||
| parent_id | FK do kategorii nadrzednej (NULL dla root) |
|
||||
| status | 1 = aktywna, 0 = nieaktywna |
|
||||
| o | Kolejnosc wyswietlania |
|
||||
| sort_type | Typ sortowania produktow w kategorii |
|
||||
| view_subcategories | Czy wyswietlac podkategorie |
|
||||
|
||||
**Uzywane w:** `Domain\Category\CategoryRepository`, `admin\Controllers\ShopCategoryController`.
|
||||
|
||||
## pp_shop_categories_langs
|
||||
Tlumaczenia kategorii (per jezyk).
|
||||
|
||||
| Kolumna | Opis |
|
||||
|---------|------|
|
||||
| category_id | FK do pp_shop_categories |
|
||||
| lang_id | ID jezyka (np. pl, en) |
|
||||
| title | Nazwa kategorii |
|
||||
| text | Opis kategorii |
|
||||
| text_hidden | Rozwiniecie opisu kategorii |
|
||||
| seo_link | Link SEO kategorii |
|
||||
| meta_title | Meta title |
|
||||
| meta_description | Meta description |
|
||||
| meta_keywords | Meta keywords |
|
||||
| noindex | Flaga noindex |
|
||||
| category_title | Naglowek H1 kategorii |
|
||||
| additional_text | Dodatkowy tekst nad lista produktow |
|
||||
|
||||
**Uzywane w:** `Domain\Category\CategoryRepository`, `admin\Controllers\ShopCategoryController`.
|
||||
|
||||
**Aktualizacja 2026-02-15 (ver. 0.275):** modul `/admin/shop_category/*` korzysta z `Domain\Category\CategoryRepository` przez `admin\Controllers\ShopCategoryController`; usunieto legacy `admin\controls/factory/view\ShopCategory`.
|
||||
|
||||
## pp_shop_orders
|
||||
Zamówienia sklepu (źródło danych dla list i szczegółów klientów w panelu admin).
|
||||
|
||||
| Kolumna | Opis |
|
||||
|---------|------|
|
||||
| id | PK |
|
||||
| client_id | FK do `pp_shop_clients` (NULL dla gościa) |
|
||||
| client_name | Imię klienta z zamówienia |
|
||||
| client_surname | Nazwisko klienta z zamówienia |
|
||||
| client_email | E-mail klienta z zamówienia |
|
||||
| client_phone | Telefon klienta |
|
||||
| client_city | Miasto klienta |
|
||||
| summary | Wartość zamówienia |
|
||||
| date_order | Data złożenia zamówienia |
|
||||
| payment_method | Nazwa metody płatności |
|
||||
| transport | Nazwa transportu |
|
||||
| message | Wiadomość klienta |
|
||||
| updated_at | Data ostatniej modyfikacji (polling API) |
|
||||
|
||||
**Używane w:** `Domain\Client\ClientRepository::listForAdmin()`, `Domain\Client\ClientRepository::ordersForClient()`, `Domain\Client\ClientRepository::totalsForClient()`, `Domain\Order\OrderRepository::listForApi()`, `Domain\Order\OrderRepository::findForApi()`.
|
||||
|
||||
**Aktualizacja 2026-02-15 (ver. 0.274):** moduł `/admin/shop_clients/*` korzysta z `Domain\Client\ClientRepository` przez `admin\Controllers\ShopClientsController`.
|
||||
|
||||
**Aktualizacja 2026-02-15 (ver. 0.276):** moduł `/admin/shop_order/*` korzysta z `Domain\Order\OrderRepository` przez `admin\Controllers\ShopOrderController`; usunięto legacy `admin\controls\ShopOrder` i `admin\factory\ShopOrder`.
|
||||
|
||||
**Aktualizacja 2026-02-17 (ver. 0.290):** frontend `/shop_order/*` korzysta z `Domain\Order\OrderRepository` przez `front\Controllers\ShopOrderController`; usunięto legacy `front\controls\ShopOrder`, `front\factory\ShopOrder`, `front\view\ShopOrder`. Callery (ShopBasketController, ClientRepository, shop\Order, cron-turstmate) przepięte na OrderRepository.
|
||||
|
||||
## pp_banners
|
||||
Banery.
|
||||
|
||||
| Kolumna | Opis |
|
||||
|---------|------|
|
||||
| id | PK |
|
||||
| name | Nazwa banera |
|
||||
| status | 0/1 |
|
||||
| date_start | Data rozpoczęcia |
|
||||
| date_end | Data zakończenia |
|
||||
| home_page | Czy na stronie głównej 0/1 |
|
||||
|
||||
**Używane w:** `Domain\Banner\BannerRepository`, `front\Views\Banners`
|
||||
|
||||
**Aktualizacja 2026-02-16 (ver. 0.281):** metody frontendowe `banners()`, `mainBanner()` dodane do `Domain\Banner\BannerRepository`. Fasady `front\factory\Banners` i `front\view\Banners` deleguja do repo/Views.
|
||||
|
||||
## pp_banners_langs
|
||||
Tłumaczenia banerów.
|
||||
|
||||
| Kolumna | Opis |
|
||||
|---------|------|
|
||||
| id | PK |
|
||||
| id_banner | FK do pp_banners |
|
||||
| id_lang | ID języka |
|
||||
| src | Ścieżka do grafiki |
|
||||
| url | URL docelowy |
|
||||
| html | Kod HTML |
|
||||
| text | Tekst |
|
||||
|
||||
**Używane w:** `Domain\Banner\BannerRepository`, `front\Views\Banners`
|
||||
|
||||
## pp_articles
|
||||
Artykuły.
|
||||
|
||||
| Kolumna | Opis |
|
||||
|---------|------|
|
||||
| id | PK |
|
||||
| status | -1 = archiwum, 0 = nieaktywny, 1 = aktywny |
|
||||
|
||||
**Używane w:** `admin\Controllers\ArticlesArchiveController`, `Domain\Article\ArticleRepository::find()`, `Domain\Article\ArticleRepository::listArchivedForAdmin()`
|
||||
|
||||
## pp_articles_pages
|
||||
Strony artykułów.
|
||||
|
||||
| Kolumna | Opis |
|
||||
|---------|------|
|
||||
| article_id | FK do pp_articles |
|
||||
| page_id | FK do strony (pp_pages) |
|
||||
| o | Kolejność |
|
||||
|
||||
**Używane w:** `Domain\Article\ArticleRepository::find()`, `Domain\Article\ArticleRepository::deleteNonassignedImages()`
|
||||
|
||||
## pp_articles_langs
|
||||
Tłumaczenia artykułów.
|
||||
|
||||
| Kolumna | Opis |
|
||||
|---------|------|
|
||||
| article_id | FK do pp_articles |
|
||||
| lang_id | ID języka (np. 'pl') |
|
||||
| title | Tytuł artykułu |
|
||||
| seo_link | Link SEO artykułu |
|
||||
|
||||
**Używane w:** `Domain\Article\ArticleRepository::find()`, `Domain\Article\ArticleRepository::deleteNonassignedFiles()`
|
||||
|
||||
## pp_articles_images
|
||||
Zdjęcia artykułów.
|
||||
|
||||
| Kolumna | Opis |
|
||||
|---------|------|
|
||||
| article_id | FK do pp_articles |
|
||||
| src | Ścieżka do pliku |
|
||||
| o | Kolejność |
|
||||
| id | PK (używane też do sortowania DESC) |
|
||||
|
||||
**Używane w:** `Domain\Article\ArticleRepository::find()`
|
||||
|
||||
## pp_articles_files
|
||||
Pliki artykułów.
|
||||
|
||||
| Kolumna | Opis |
|
||||
|---------|------|
|
||||
| id | PK |
|
||||
| article_id | FK do pp_articles |
|
||||
| src | Ścieżka do pliku |
|
||||
| name | Nazwa wyświetlana załącznika (opcjonalna) |
|
||||
| to_delete | Flaga miękkiego usuwania (0/1) |
|
||||
| o | Kolejność załączników (używana przez sortowanie drag&drop w adminie) |
|
||||
|
||||
**Używane w:** `Domain\Article\ArticleRepository::find()`, `Domain\Article\ArticleRepository::saveFilesOrder()`
|
||||
|
||||
## pp_units
|
||||
Jednostki/slowniki (np. jednostki produktu).
|
||||
|
||||
| Kolumna | Opis |
|
||||
|---------|------|
|
||||
| id | PK |
|
||||
|
||||
**Używane w:** `Domain\Dictionaries\DictionariesRepository`, `admin\controls\ShopProduct`
|
||||
|
||||
## pp_units_langs
|
||||
Tlumaczenia jednostek (per jezyk).
|
||||
|
||||
| Kolumna | Opis |
|
||||
|---------|------|
|
||||
| id | PK |
|
||||
| unit_id | FK do pp_units |
|
||||
| lang_id | ID jezyka (np. 'pl') |
|
||||
| text | Nazwa jednostki |
|
||||
|
||||
**Używane w:** `Domain\Dictionaries\DictionariesRepository`
|
||||
|
||||
## pp_users
|
||||
Uzytkownicy panelu administratora.
|
||||
|
||||
| Kolumna | Opis |
|
||||
|---------|------|
|
||||
| id | PK |
|
||||
| login | Login / e-mail uzytkownika |
|
||||
| password | Hash hasla (legacy: md5) |
|
||||
| status | Status konta: 1 = aktywny, 0 = zablokowany |
|
||||
| admin | Flaga dostepu do panelu admin |
|
||||
| error_logged_count | Licznik nieudanych logowan |
|
||||
| last_logged | Data ostatniego poprawnego logowania |
|
||||
| last_error_logged | Data ostatniej nieudanej proby logowania |
|
||||
| twofa_enabled | Czy wlaczone 2FA (0/1) |
|
||||
| twofa_email | E-mail do wysylki kodu 2FA |
|
||||
| twofa_code_hash | Hash aktualnego kodu 2FA |
|
||||
| twofa_expires_at | Data waznosci kodu 2FA |
|
||||
| twofa_sent_at | Data ostatniej wysylki kodu 2FA |
|
||||
| twofa_failed_attempts | Liczba nieudanych prob 2FA |
|
||||
|
||||
**Uzywane w:** `Domain\User\UserRepository`, `admin\Controllers\UsersController`, `admin\factory\Users`
|
||||
|
||||
**Aktualizacja 2026-02-12:** uzycia `pp_users` sa prowadzone przez `Domain\\User\\UserRepository` (legacy `admin\\factory\\Users` usunieto).
|
||||
|
||||
## pp_langs
|
||||
Jezyki panelu i frontendu.
|
||||
|
||||
| Kolumna | Opis |
|
||||
|---------|------|
|
||||
| id | PK (2-literowe ID jezyka, np. pl, en) |
|
||||
| name | Nazwa jezyka |
|
||||
| status | 1 = aktywny, 0 = nieaktywny |
|
||||
| start | 1 = domyslny jezyk |
|
||||
| o | Kolejnosc |
|
||||
|
||||
**Uzywane w:** `Domain\\Languages\\LanguagesRepository`, `admin\\Controllers\\LanguagesController`, `front\\factory\\Languages`
|
||||
|
||||
## pp_langs_translations
|
||||
Slownik tlumaczen panelu/frontendu.
|
||||
|
||||
| Kolumna | Opis |
|
||||
|---------|------|
|
||||
| id | PK |
|
||||
| text | Klucz/tekst bazowy |
|
||||
| <lang_id> | Kolumny dynamiczne per jezyk (np. pl, en) |
|
||||
|
||||
**Uzywane w:** `Domain\\Languages\\LanguagesRepository`, `admin\\Controllers\\LanguagesController`, `front\\factory\\Languages`
|
||||
|
||||
**Aktualizacja 2026-02-12:** modul jezykow i tlumaczen (`pp_langs`, `pp_langs_translations`) obslugiwany przez `Domain\\Languages\\LanguagesRepository`.
|
||||
|
||||
## pp_layouts
|
||||
Szablony layoutow (HTML/CSS/JS + flagi domyslne).
|
||||
|
||||
| Kolumna | Opis |
|
||||
|---------|------|
|
||||
| id | PK |
|
||||
| name | Nazwa szablonu |
|
||||
| html | Kod HTML |
|
||||
| css | Kod CSS |
|
||||
| js | Kod JS |
|
||||
| m_html | Kod HTML mobilny |
|
||||
| m_css | Kod CSS mobilny |
|
||||
| m_js | Kod JS mobilny |
|
||||
| status | Domyslny layout stron (0/1) |
|
||||
| categories_default | Domyslny layout kategorii (0/1) |
|
||||
|
||||
**Uzywane w:** `Domain\\Layouts\\LayoutsRepository`, `admin\\Controllers\\LayoutsController`, `front\\factory\\Layouts`
|
||||
|
||||
## pp_layouts_pages
|
||||
Przypisanie layoutow do stron CMS.
|
||||
|
||||
| Kolumna | Opis |
|
||||
|---------|------|
|
||||
| layout_id | FK do pp_layouts |
|
||||
| page_id | FK do pp_pages |
|
||||
|
||||
**Uzywane w:** `Domain\\Layouts\\LayoutsRepository`, `front\\factory\\Layouts`
|
||||
|
||||
## pp_layouts_categories
|
||||
Przypisanie layoutow do kategorii sklepu.
|
||||
|
||||
| Kolumna | Opis |
|
||||
|---------|------|
|
||||
| layout_id | FK do pp_layouts |
|
||||
| category_id | FK do pp_shop_categories |
|
||||
|
||||
**Uzywane w:** `Domain\\Layouts\\LayoutsRepository`, `front\\factory\\Layouts`
|
||||
|
||||
**Aktualizacja 2026-02-12 (ver. 0.256):** modul `/admin/layouts` korzysta z `Domain\\Layouts\\LayoutsRepository` (DI kontroler + fasada legacy).
|
||||
|
||||
## pp_newsletter
|
||||
Adresy e-mail zapisane do newslettera.
|
||||
|
||||
| Kolumna | Opis |
|
||||
|---------|------|
|
||||
| id | PK |
|
||||
| email | Adres e-mail subskrybenta |
|
||||
| hash | Hash potwierdzenia/wypisu |
|
||||
| status | 1 = potwierdzony, 0 = oczekujacy |
|
||||
|
||||
**Uzywane w:** `Domain\\Newsletter\\NewsletterRepository`, `front\\Controllers\\NewsletterController`
|
||||
|
||||
## pp_newsletter_send
|
||||
Kolejka wysylki newslettera.
|
||||
|
||||
| Kolumna | Opis |
|
||||
|---------|------|
|
||||
| id | PK |
|
||||
| email | Adres docelowy |
|
||||
| dates | Zakres dat artykulow (tekst) |
|
||||
| id_template | FK do `pp_newsletter_templates` (NULL gdy brak szablonu) |
|
||||
|
||||
**Uzywane w:** `Domain\\Newsletter\\NewsletterRepository`
|
||||
|
||||
## pp_newsletter_templates
|
||||
Szablony tresci e-maili (uzytkownik + administracyjne/systemowe).
|
||||
|
||||
| Kolumna | Opis |
|
||||
|---------|------|
|
||||
| id | PK |
|
||||
| name | Nazwa/klucz szablonu |
|
||||
| text | Tresc HTML szablonu |
|
||||
| is_admin | 1 = szablon administracyjny/systemowy, 0 = szablon uzytkownika |
|
||||
|
||||
**Uzywane w:** `Domain\\Newsletter\\NewsletterRepository`, `admin\\Controllers\\NewsletterController`
|
||||
|
||||
**Aktualizacja 2026-02-12 (ver. 0.257):** modul `/admin/newsletter` korzysta z `Domain\\Newsletter\\NewsletterRepository` (DI kontroler + fasada legacy).
|
||||
|
||||
**Aktualizacja 2026-02-16 (ver. 0.279):** `front\\factory\\Newsletter` usunięta — logika przeniesiona do `NewsletterRepository`. Frontend korzysta z `front\\Controllers\\NewsletterController` (DI).
|
||||
|
||||
## pp_scontainers
|
||||
Kontenery statyczne (modul /admin/scontainers).
|
||||
|
||||
| Kolumna | Opis |
|
||||
|---------|------|
|
||||
| id | PK |
|
||||
| status | 1 = aktywny, 0 = nieaktywny |
|
||||
| show_title | 1 = pokaz tytul, 0 = ukryj tytul |
|
||||
|
||||
**Uzywane w:** `Domain\Scontainers\ScontainersRepository`, `admin\Controllers\ScontainersController`, `front\factory\Scontainers`
|
||||
|
||||
## pp_scontainers_langs
|
||||
Tlumaczenia kontenerow statycznych (per jezyk).
|
||||
|
||||
| Kolumna | Opis |
|
||||
|---------|------|
|
||||
| id | PK |
|
||||
| container_id | FK do pp_scontainers |
|
||||
| lang_id | ID jezyka (np. pl, en) |
|
||||
| title | Tytul kontenera |
|
||||
| text | Tresc HTML kontenera |
|
||||
|
||||
**Uzywane w:** `Domain\Scontainers\ScontainersRepository`, `front\factory\Scontainers`
|
||||
|
||||
**Aktualizacja 2026-02-12 (ver. 0.259):** modul `/admin/scontainers` korzysta z `Domain\Scontainers\ScontainersRepository` (DI kontroler + fasada legacy).
|
||||
|
||||
**Aktualizacja 2026-02-12 (ver. 0.260):** modul `/admin/articles_archive` korzysta z `Domain\Article\ArticleRepository` (`listArchivedForAdmin`, `restore`, `deletePermanently`) przez `admin\Controllers\ArticlesArchiveController`.
|
||||
|
||||
## pp_shop_attributes
|
||||
Cechy produktu (modul `/admin/shop_attribute`).
|
||||
|
||||
| Kolumna | Opis |
|
||||
|---------|------|
|
||||
| id | PK |
|
||||
| status | Status: 1 = aktywny, 0 = nieaktywny |
|
||||
| type | Typ cechy: 0 = tekst, 1 = kolor, 2 = wzor |
|
||||
| o | Kolejnosc wyswietlania |
|
||||
|
||||
**Uzywane w:** `Domain\Attribute\AttributeRepository`, `admin\Controllers\ShopAttributeController`, `admin\controls\ShopProduct`, `admin\factory\ShopProduct`
|
||||
|
||||
## pp_shop_attributes_langs
|
||||
Tlumaczenia cech produktu (per jezyk).
|
||||
|
||||
| Kolumna | Opis |
|
||||
|---------|------|
|
||||
| id | PK |
|
||||
| attribute_id | FK do pp_shop_attributes |
|
||||
| lang_id | ID jezyka (np. pl, en) |
|
||||
| name | Nazwa cechy |
|
||||
|
||||
**Uzywane w:** `Domain\Attribute\AttributeRepository`, `shop\ProductAttribute`
|
||||
|
||||
## pp_shop_attributes_values
|
||||
Wartosci cech produktu.
|
||||
|
||||
| Kolumna | Opis |
|
||||
|---------|------|
|
||||
| id | PK |
|
||||
| attribute_id | FK do pp_shop_attributes |
|
||||
| is_default | Czy wartosc domyslna dla cechy (0/1) |
|
||||
| impact_on_the_price | Wplyw na cene wariantu (NULL = brak) |
|
||||
|
||||
**Uzywane w:** `Domain\Attribute\AttributeRepository`, `admin\Controllers\ShopAttributeController`, `admin\factory\ShopProduct`
|
||||
|
||||
## pp_shop_attributes_values_langs
|
||||
Tlumaczenia wartosci cech (per jezyk).
|
||||
|
||||
| Kolumna | Opis |
|
||||
|---------|------|
|
||||
| id | PK |
|
||||
| value_id | FK do pp_shop_attributes_values |
|
||||
| lang_id | ID jezyka (np. pl, en) |
|
||||
| name | Nazwa wyswietlana |
|
||||
| value | Wewnetrzna wartosc techniczna (opcjonalna) |
|
||||
|
||||
**Uzywane w:** `Domain\Attribute\AttributeRepository`, `shop\ProductAttribute`
|
||||
|
||||
## pp_shop_products_attributes
|
||||
Powiazanie kombinacji produktow z wartosciami cech.
|
||||
|
||||
| Kolumna | Opis |
|
||||
|---------|------|
|
||||
| product_id | FK do pp_shop_products (kombinacja) |
|
||||
| value_id | FK do pp_shop_attributes_values |
|
||||
|
||||
**Uzywane w:** `Domain\Attribute\AttributeRepository::refreshCombinationPricesForValue()`, `admin\controls\ShopProduct`, `admin\factory\ShopProduct`
|
||||
|
||||
**Aktualizacja 2026-02-14 (ver. 0.271):** modul `/admin/shop_attribute` korzysta z `Domain\Attribute\AttributeRepository` przez `admin\Controllers\ShopAttributeController`. Usunieto legacy klasy `admin\controls\ShopAttribute`, `admin\factory\ShopAttribute`, `admin\view\ShopAttribute`.
|
||||
|
||||
## pp_shop_coupon
|
||||
Kody rabatowe sklepu (modul `/admin/shop_coupon`).
|
||||
|
||||
| Kolumna | Opis |
|
||||
|---------|------|
|
||||
| id | PK |
|
||||
| name | Kod kuponu (UNIQUE) |
|
||||
| status | Status: 1 = aktywny, 0 = nieaktywny |
|
||||
| send | Czy kupon zostal wyslany (0/1) |
|
||||
| used | Czy kupon zostal wykorzystany (0/1) |
|
||||
| date_used | Data wykorzystania kuponu (NULL gdy brak) |
|
||||
| used_count | Licznik uzyc kuponu |
|
||||
| type | Typ kuponu (obecnie: 1 = rabat procentowy na koszyk) |
|
||||
| amount | Wartosc kuponu (np. procent) |
|
||||
| one_time | Czy kupon jednorazowy (0/1) |
|
||||
| include_discounted_product | Czy obejmuje rowniez produkty przecenione (0/1) |
|
||||
| categories | JSON z ID kategorii objetych kuponem (NULL = bez ograniczenia) |
|
||||
|
||||
**Uzywane w:** `Domain\Coupon\CouponRepository`, `admin\Controllers\ShopCouponController`, `front\Controllers\ShopCouponController`, `shop\Coupon`, `Domain\Order\OrderRepository`
|
||||
|
||||
**Aktualizacja 2026-02-13 (ver. 0.266):** modul `/admin/shop_coupon` korzysta z `Domain\Coupon\CouponRepository` przez `admin\Controllers\ShopCouponController`. Usunieto legacy klasy `admin\controls\ShopCoupon` i `admin\factory\ShopCoupon`.
|
||||
|
||||
## pp_shop_promotion
|
||||
Promocje sklepu (modul `/admin/shop_promotion`).
|
||||
|
||||
| Kolumna | Opis |
|
||||
|---------|------|
|
||||
| id | PK |
|
||||
| name | Nazwa promocji |
|
||||
| status | Status: 1 = aktywna, 0 = nieaktywna |
|
||||
| condition_type | Typ warunku promocji (slownik w `shop\Promotion::$condition_type`) |
|
||||
| discount_type | Typ rabatu (slownik w `shop\Promotion::$discount_type`) |
|
||||
| amount | Wartosc rabatu (np. procent) |
|
||||
| date_from | Data startu promocji (NULL = aktywna od razu) |
|
||||
| date_to | Data konca promocji (NULL = bez daty konca) |
|
||||
| categories | JSON z ID kategorii grupy I |
|
||||
| condition_categories | JSON z ID kategorii grupy II |
|
||||
| include_coupon | Czy laczyc z kuponami rabatowymi (0/1) |
|
||||
| include_product_promo | Czy uwzgledniac produkty przecenione (0/1) |
|
||||
| min_product_count | Minimalna liczba produktow (dla wybranych warunkow) |
|
||||
| price_cheapest_product | Cena najtanszego produktu (dla wybranych warunkow) |
|
||||
|
||||
**Uzywane w:** `Domain\Promotion\PromotionRepository`, `admin\Controllers\ShopPromotionController`, `shop\Promotion`, `front\factory\ShopPromotion`
|
||||
|
||||
**Aktualizacja 2026-02-13:** modul `/admin/shop_promotion` korzysta z `Domain\Promotion\PromotionRepository` przez `admin\Controllers\ShopPromotionController`. Usunieto legacy klasy `admin\controls\ShopPromotion` i `admin\factory\ShopPromotion`.
|
||||
|
||||
**Aktualizacja 2026-02-13 (ver. 0.265):** dodano obsluge `date_from` (repozytorium, formularz admin, lista admin, filtr aktywnych promocji na froncie) oraz poprawke zapisu edycji promocji po `id`.
|
||||
|
||||
## pp_shop_payment_methods
|
||||
Metody platnosci sklepu (modul `/admin/shop_payment_method`).
|
||||
|
||||
| Kolumna | Opis |
|
||||
|---------|------|
|
||||
| id | PK |
|
||||
| name | Nazwa metody platnosci |
|
||||
| description | Opis metody platnosci (wyswietlany m.in. w checkout) |
|
||||
| status | Status: 1 = aktywna, 0 = nieaktywna |
|
||||
| apilo_payment_type_id | ID typu platnosci Apilo (NULL gdy brak mapowania) |
|
||||
| min_order_amount | Minimalna kwota zamowienia (DECIMAL(10,2), NULL = brak limitu) |
|
||||
| max_order_amount | Maksymalna kwota zamowienia (DECIMAL(10,2), NULL = brak limitu) |
|
||||
| is_cod | Platnosc przy odbiorze: 1 = tak, 0 = nie (TINYINT DEFAULT 0) |
|
||||
| sellasist_payment_type_id | DEPRECATED (integracja Sellasist usunieta w ver. 0.263) |
|
||||
|
||||
**Uzywane w:** `Domain\PaymentMethod\PaymentMethodRepository`, `admin\Controllers\ShopPaymentMethodController`, `front\factory\ShopPaymentMethod`, `shop\PaymentMethod`, `admin\controls\ShopTransport`, `cron.php`
|
||||
|
||||
**Aktualizacja 2026-03-12 (ver. 0.338):** dodano kolumne `is_cod` — flaga platnosci przy odbiorze, zastepuje hardkodowane `payment_id == 3` w `OrderRepository::createFromBasket()`.
|
||||
|
||||
**Aktualizacja 2026-02-14 (ver. 0.268):** modul `/admin/shop_payment_method` korzysta z `Domain\PaymentMethod\PaymentMethodRepository` przez `admin\Controllers\ShopPaymentMethodController`. Usunieto legacy klasy `admin\controls\ShopPaymentMethod`, `admin\factory\ShopPaymentMethod`, `admin\view\ShopPaymentMethod` oraz widok `admin/templates/shop-payment-method/view-list.php`.
|
||||
|
||||
**Aktualizacja 2026-02-22 (ver. 0.304):** dodano kolumny `min_order_amount` i `max_order_amount` — konfigurowalne limity kwotowe metod platnosci. Zastapiono hardcoded warunek PayPo (id=6, 40-1000 PLN) generycznym filtrowaniem na froncie.
|
||||
|
||||
## pp_shop_transports
|
||||
Rodzaje transportu sklepu (modul `/admin/shop_transport`).
|
||||
|
||||
| Kolumna | Opis |
|
||||
|---------|------|
|
||||
| id | PK |
|
||||
| name | Nazwa (systemowa, readonly) |
|
||||
| name_visible | Nazwa widoczna dla klienta |
|
||||
| description | Opis metody transportu |
|
||||
| status | Status: 1 = aktywny, 0 = nieaktywny |
|
||||
| cost | Koszt dostawy (PLN) |
|
||||
| max_wp | Maksymalna waga paczki (NULL = bez limitu) |
|
||||
| default | Domyslna forma dostawy (0/1) |
|
||||
| delivery_free | Czy obsluguje darmowa dostawe (0/1) |
|
||||
| apilo_carrier_account_id | ID konta przewoznika w Apilo (NULL gdy brak mapowania) |
|
||||
| o | Kolejnosc wyswietlania |
|
||||
|
||||
**Uzywane w:** `Domain\Transport\TransportRepository`, `admin\Controllers\ShopTransportController`, `front\factory\ShopTransport`
|
||||
|
||||
## pp_shop_transport_payment_methods
|
||||
Powiazanie metod transportu z metodami platnosci (tabela lacznikowa).
|
||||
|
||||
| Kolumna | Opis |
|
||||
|---------|------|
|
||||
| id_transport | FK do pp_shop_transports |
|
||||
| id_payment_method | FK do pp_shop_payment_methods |
|
||||
|
||||
**Uzywane w:** `Domain\Transport\TransportRepository`, `Domain\PaymentMethod\PaymentMethodRepository::forTransport()`
|
||||
|
||||
**Aktualizacja 2026-02-14 (ver. 0.269):** modul `/admin/shop_transport` korzysta z `Domain\Transport\TransportRepository` przez `admin\Controllers\ShopTransportController`. Usunieto legacy klasy `admin\controls\ShopTransport`, `admin\view\ShopTransport` oraz widok `admin/templates/shop-transport/view-list.php`.
|
||||
|
||||
## pp_shop_apilo_settings
|
||||
Ustawienia integracji Apilo (key-value).
|
||||
|
||||
| Kolumna | Opis |
|
||||
|---------|------|
|
||||
| id | PK |
|
||||
| name | Klucz ustawienia (np. client-id, access-token) |
|
||||
| value | Wartosc ustawienia |
|
||||
|
||||
**Uzywane w:** `Domain\Integrations\IntegrationsRepository`, `admin\Controllers\IntegrationsController`, `admin\factory\Integrations`
|
||||
|
||||
## pp_shop_shoppro_settings
|
||||
Ustawienia integracji ShopPRO (key-value).
|
||||
|
||||
| Kolumna | Opis |
|
||||
|---------|------|
|
||||
| id | PK |
|
||||
| name | Klucz ustawienia (np. domain, db_name) |
|
||||
| value | Wartosc ustawienia |
|
||||
|
||||
**Uzywane w:** `Domain\Integrations\IntegrationsRepository`, `admin\Controllers\IntegrationsController`, `admin\factory\Integrations`
|
||||
|
||||
**Aktualizacja 2026-02-13:** modul `/admin/integrations/` korzysta z `Domain\Integrations\IntegrationsRepository` (DI kontroler + fasada legacy). Usunieto integracje Sellasist i Baselinker.
|
||||
|
||||
## pp_shop_statuses
|
||||
Statusy zamowien sklepu (modul `/admin/shop_statuses`). Statusy sa predefiniowane - brak dodawania/usuwania, mozliwa edycja koloru i mapowania Apilo.
|
||||
|
||||
| Kolumna | Opis |
|
||||
|---------|------|
|
||||
| id | PK (zaczyna sie od 0!) |
|
||||
| status | Nazwa statusu (read-only) |
|
||||
| color | Kolor statusu (hex, np. #ff0000) |
|
||||
| o | Kolejnosc wyswietlania |
|
||||
| apilo_status_id | ID statusu w Apilo (NULL gdy brak mapowania) |
|
||||
| baselinker_status_id | DEPRECATED (usuniety w ver. 0.263) |
|
||||
| sellasist_status_id | DEPRECATED (usuniety w ver. 0.263) |
|
||||
|
||||
**Uzywane w:** `Domain\ShopStatus\ShopStatusRepository`, `admin\Controllers\ShopStatusesController`, `front\factory\ShopStatuses`, `shop\Order`, `cron.php`
|
||||
|
||||
**Aktualizacja 2026-02-14 (ver. 0.267):** modul `/admin/shop_statuses` korzysta z `Domain\ShopStatus\ShopStatusRepository` przez `admin\Controllers\ShopStatusesController`. Usunieto legacy klasy `admin\controls\ShopStatuses` i `admin\factory\ShopStatuses`. `front\factory\ShopStatuses` dziala jako fasada do repozytorium.
|
||||
|
||||
## pp_shop_product_sets
|
||||
Komplety produktow (modul `/admin/shop_product_sets`).
|
||||
|
||||
| Kolumna | Opis |
|
||||
|---------|------|
|
||||
| id | PK |
|
||||
| name | Nazwa kompletu |
|
||||
| status | Status: 1 = aktywny, 0 = nieaktywny |
|
||||
|
||||
**Uzywane w:** `Domain\ProductSet\ProductSetRepository`, `admin\Controllers\ShopProductSetsController`, `shop\ProductSet`, `shop\Product`
|
||||
|
||||
## pp_shop_product_sets_products
|
||||
Powiazanie kompletow z produktami (tabela lacznikowa).
|
||||
|
||||
| Kolumna | Opis |
|
||||
|---------|------|
|
||||
| id | PK |
|
||||
| set_id | FK do pp_shop_product_sets |
|
||||
| product_id | FK do pp_shop_products |
|
||||
|
||||
**Uzywane w:** `Domain\ProductSet\ProductSetRepository`, `shop\Product`, `front\factory\ShopProduct`, `admin\factory\ShopProduct`
|
||||
|
||||
**Aktualizacja 2026-02-15 (ver. 0.272):** modul `/admin/shop_product_sets` korzysta z `Domain\ProductSet\ProductSetRepository` przez `admin\Controllers\ShopProductSetsController`. Usunieto legacy klasy `admin\controls\ShopProductSets` i `admin\factory\ShopProductSet`. `shop\ProductSet` dziala jako fasada do repozytorium.
|
||||
|
||||
## pp_shop_producer
|
||||
Producenci produktow (modul `/admin/shop_producer`).
|
||||
|
||||
| Kolumna | Opis |
|
||||
|---------|------|
|
||||
| id | PK |
|
||||
| name | Nazwa producenta |
|
||||
| status | Status: 1 = aktywny, 0 = nieaktywny |
|
||||
| img | Sciezka do logo producenta (NULL gdy brak) |
|
||||
|
||||
**Uzywane w:** `Domain\Producer\ProducerRepository`, `admin\Controllers\ShopProducerController`, `front\Controllers\ShopProducerController`, `shop\Product`
|
||||
|
||||
## pp_shop_producer_lang
|
||||
Tlumaczenia producentow (per jezyk). FK kaskadowe ON DELETE CASCADE.
|
||||
|
||||
| Kolumna | Opis |
|
||||
|---------|------|
|
||||
| id | PK |
|
||||
| producer_id | FK do pp_shop_producer |
|
||||
| lang_id | ID jezyka (np. pl, en) |
|
||||
| description | Opis producenta (TEXT) |
|
||||
| data | Dane producenta (TEXT, HTML) |
|
||||
| meta_title | Meta title SEO (VARCHAR 255) |
|
||||
|
||||
**Uzywane w:** `Domain\Producer\ProducerRepository`, `shop\Product`
|
||||
|
||||
**Aktualizacja 2026-02-15 (ver. 0.273):** modul `/admin/shop_producer` korzysta z `Domain\Producer\ProducerRepository` przez `admin\Controllers\ShopProducerController`. Usunieto legacy `admin\controls\ShopProducer` i `admin\factory\ShopProducer`. `shop\Producer` dziala jako fasada do repozytorium.
|
||||
|
||||
**Aktualizacja 2026-02-17 (ver. 0.291):** frontend `/shop_producer/*` korzysta z `Domain\Producer\ProducerRepository` przez `front\Controllers\ShopProducerController`; usunięto legacy `front\controls\ShopProducer` i `shop\Producer`.
|
||||
|
||||
## pp_cron_jobs
|
||||
Kolejka zadań cron z priorytetami i retry/backoff.
|
||||
|
||||
| Kolumna | Opis |
|
||||
|---------|------|
|
||||
| id | PK auto increment |
|
||||
| job_type | Typ zadania (VARCHAR 50) — np. apilo_send_order, price_history |
|
||||
| status | ENUM: pending, processing, completed, failed, cancelled |
|
||||
| priority | TINYINT — niższy = ważniejszy (10=krytyczny, 50=wysoki, 100=normalny, 200=niski) |
|
||||
| payload | JSON z danymi zadania (TEXT NULL) |
|
||||
| result | JSON z wynikiem (TEXT NULL) |
|
||||
| attempts | Liczba prób (SMALLINT) |
|
||||
| max_attempts | Maksymalna liczba prób (SMALLINT, domyślnie 10) |
|
||||
| last_error | Ostatni błąd (VARCHAR 500) |
|
||||
| scheduled_at | Kiedy zadanie ma być uruchomione (DATETIME) |
|
||||
| started_at | Kiedy rozpoczęto przetwarzanie (DATETIME NULL) |
|
||||
| completed_at | Kiedy zakończono (DATETIME NULL) |
|
||||
| created_at | Data utworzenia (DATETIME) |
|
||||
| updated_at | Data ostatniej modyfikacji (DATETIME, ON UPDATE) |
|
||||
|
||||
**Indeksy:** idx_status_priority_scheduled (status, priority, scheduled_at), idx_job_type, idx_status
|
||||
|
||||
**Używane w:** `Domain\CronJob\CronJobRepository`, `Domain\CronJob\CronJobProcessor`
|
||||
|
||||
## pp_cron_schedules
|
||||
Harmonogram cyklicznych zadań cron.
|
||||
|
||||
| Kolumna | Opis |
|
||||
|---------|------|
|
||||
| id | PK auto increment |
|
||||
| job_type | Typ zadania (VARCHAR 50, UNIQUE) |
|
||||
| interval_seconds | Interwał uruchomienia w sekundach |
|
||||
| priority | Priorytet tworzonych zadań (TINYINT) |
|
||||
| max_attempts | Maks. prób dla tworzonych zadań (SMALLINT) |
|
||||
| payload | Opcjonalny payload JSON (TEXT NULL) |
|
||||
| enabled | Czy harmonogram aktywny (TINYINT 1) |
|
||||
| last_run_at | Ostatnie uruchomienie (DATETIME NULL) |
|
||||
| next_run_at | Następne planowane uruchomienie (DATETIME NULL) |
|
||||
| created_at | Data utworzenia (DATETIME) |
|
||||
|
||||
**Indeksy:** idx_enabled_next_run (enabled, next_run_at)
|
||||
|
||||
**Używane w:** `Domain\CronJob\CronJobRepository`, `Domain\CronJob\CronJobProcessor`
|
||||
|
||||
**Dodano w wersji 0.324.**
|
||||
|
||||
## pp_routes
|
||||
Tabela tras URL — mapowanie wzorców URL (regex) na parametry GET. Zastępuje reguły `RewriteRule` w `.htaccess` dla wszystkich URL-i aplikacji: produktów, kategorii, stron, artykułów oraz systemowych (koszyk, logowanie, newsletter, itp.).
|
||||
|
||||
| Kolumna | Opis |
|
||||
|---------|------|
|
||||
| id | Klucz główny (AUTO_INCREMENT) |
|
||||
| product_id | ID produktu (INT NULL) — wypełnione dla tras produktów |
|
||||
| category_id | ID kategorii (INT NULL) — wypełnione dla tras kategorii |
|
||||
| page_id | ID strony (INT NULL) — wypełnione dla tras stron |
|
||||
| article_id | ID artykułu (INT NULL) — wypełnione dla tras artykułów |
|
||||
| type | Typ trasy: NULL = encja (produkt/kategoria/strona/artykuł), `'system'` = trasa systemowa (koszyk, logowanie, newsletter, AJAX moduły, itp.) |
|
||||
| lang_id | ID języka (0 dla tras systemowych niezwiązanych z językiem) |
|
||||
| pattern | Wyrażenie regularne dopasowywane do REQUEST_URI |
|
||||
| destination | Docelowy query string, np. `index.php?category=5&lang=1` |
|
||||
|
||||
**Mechanizm:** `index.php` ładuje wszystkie trasy (z cache Redis `pp_routes:all`) przed `checkUrlParams()`, dopasowuje `pattern` do ścieżki żądania i ustawia `$_GET` z `destination`. Obsługuje grupy przechwytujące (np. `$1` dla paginacji).
|
||||
|
||||
**Trasy systemowe:** Przy każdym wywołaniu `Helpers::htacces()` wszystkie rekordy z `type='system'` są usuwane i wstawiane na nowo (32 statycznych + dynamiczne trasy językowe i producentów). Zarządzane automatycznie — nie edytować ręcznie.
|
||||
|
||||
**Cache:** Redis klucz `pp_routes:all`, TTL 86400s. Invalidowany automatycznie przy każdym wywołaniu `Helpers::htacces()`.
|
||||
|
||||
**Używane w:** `index.php`, `Shared\Helpers\Helpers::htacces()`, `Domain\Product\ProductRepository`, `Domain\Category\CategoryRepository`, `Domain\Pages\PagesRepository`, `Domain\Article\ArticleRepository`
|
||||
|
||||
**Dodano w wersji 0.329. Kolumna `type` i trasy systemowe dodane w wersji 0.330.**
|
||||
@@ -1,178 +0,0 @@
|
||||
# Form Edit System - Dokumentacja użycia
|
||||
|
||||
## Architektura
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ Controller │
|
||||
│ ┌─────────────────┐ ┌─────────────────┐ │
|
||||
│ │ edit() │ │ save() │ │
|
||||
│ │ - buduje VM │ │ - walidacja │ │
|
||||
│ │ - renderuje │ │ - zapis │ │
|
||||
│ └────────┬────────┘ └─────────────────┘ │
|
||||
└───────────┼─────────────────────────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ FormEditViewModel │
|
||||
│ - title, formId, data, fields, tabs, actions │
|
||||
│ - validationErrors, persist, languages │
|
||||
└───────────┬─────────────────────────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ components/form-edit.php (szablon) │
|
||||
│ ┌─────────────────────────────────────────────────────┐ │
|
||||
│ │ FormFieldRenderer - renderuje każde pole │ │
|
||||
│ │ ├─ input, select, textarea, switch │ │
|
||||
│ │ ├─ date, datetime, editor, image │ │
|
||||
│ │ └─ lang_section (zagnieżdżone pola) │ │
|
||||
│ └─────────────────────────────────────────────────────┘ │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
## Pliki systemu
|
||||
|
||||
| Plik | Opis |
|
||||
|------|------|
|
||||
| `autoload/admin/ViewModels/Forms/FormFieldType.php` | Stale typow pol |
|
||||
| `autoload/admin/ViewModels/Forms/FormField.php` | Factory methods per typ |
|
||||
| `autoload/admin/ViewModels/Forms/FormTab.php` | Zakladki |
|
||||
| `autoload/admin/ViewModels/Forms/FormAction.php` | Akcje (zapisz, anuluj) |
|
||||
| `autoload/admin/ViewModels/Forms/FormEditViewModel.php` | ViewModel formularza |
|
||||
| `autoload/admin/Support/Forms/FormValidator.php` | Walidacja pol |
|
||||
| `autoload/admin/Support/Forms/FormRequestHandler.php` | Obsluga POST + persist |
|
||||
| `autoload/admin/Support/Forms/FormFieldRenderer.php` | Renderowanie HTML |
|
||||
| `admin/templates/components/form-edit.php` | Uniwersalny szablon |
|
||||
|
||||
## Przykład użycia w kontrolerze
|
||||
|
||||
```php
|
||||
use admin\ViewModels\Forms\FormEditViewModel;
|
||||
use admin\ViewModels\Forms\FormField;
|
||||
use admin\ViewModels\Forms\FormTab;
|
||||
use admin\ViewModels\Forms\FormAction;
|
||||
use admin\Support\Forms\FormRequestHandler;
|
||||
|
||||
class BannerController
|
||||
{
|
||||
public function edit(): string
|
||||
{
|
||||
$banner = $this->repository->find($id);
|
||||
$languages = \admin\factory\Languages::languages_list();
|
||||
|
||||
$viewModel = new FormEditViewModel(
|
||||
formId: 'banner-edit',
|
||||
title: 'Edycja banera',
|
||||
data: $banner,
|
||||
tabs: [
|
||||
new FormTab('settings', 'Ustawienia', 'fa-wrench'),
|
||||
new FormTab('content', 'Zawartość', 'fa-file'),
|
||||
],
|
||||
fields: [
|
||||
// Zakładka Ustawienia
|
||||
FormField::text('name', [
|
||||
'label' => 'Nazwa',
|
||||
'tab' => 'settings',
|
||||
'required' => true,
|
||||
]),
|
||||
FormField::switch('status', [
|
||||
'label' => 'Aktywny',
|
||||
'tab' => 'settings',
|
||||
]),
|
||||
FormField::date('date_start', [
|
||||
'label' => 'Data rozpoczęcia',
|
||||
'tab' => 'settings',
|
||||
]),
|
||||
|
||||
// Sekcja językowa w zakładce Zawartość
|
||||
FormField::langSection('translations', 'content', [
|
||||
FormField::image('src', ['label' => 'Obraz']),
|
||||
FormField::text('url', ['label' => 'Url']),
|
||||
FormField::editor('text', ['label' => 'Treść']),
|
||||
]),
|
||||
],
|
||||
actions: [
|
||||
FormAction::save('/admin/banners/save', '/admin/banners'),
|
||||
FormAction::cancel('/admin/banners'),
|
||||
],
|
||||
languages: $languages,
|
||||
persist: true,
|
||||
);
|
||||
|
||||
return \Tpl::view('components/form-edit', ['form' => $viewModel]);
|
||||
}
|
||||
|
||||
public function save(): void
|
||||
{
|
||||
$formHandler = new FormRequestHandler();
|
||||
$viewModel = $this->buildFormViewModel(); // jak w edit()
|
||||
|
||||
$result = $formHandler->handleSubmit($viewModel, $_POST);
|
||||
|
||||
if (!$result['success']) {
|
||||
// Błędy walidacji - zapisane automatycznie do sesji
|
||||
echo json_encode(['success' => false, 'errors' => $result['errors']]);
|
||||
exit;
|
||||
}
|
||||
|
||||
// Sukces - persist wyczyszczony automatycznie
|
||||
$this->repository->save($result['data']);
|
||||
echo json_encode(['success' => true]);
|
||||
exit;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Dostępne typy pól
|
||||
|
||||
| Typ | Metoda | Opcje |
|
||||
|-----|--------|-------|
|
||||
| `text` | `FormField::text(name, ['label' => '...', 'required' => true])` | placeholder, help |
|
||||
| `number` | `FormField::number(name, [...])` | - |
|
||||
| `email` | `FormField::email(name, [...])` | walidacja formatu |
|
||||
| `password` | `FormField::password(name, [...])` | - |
|
||||
| `date` | `FormField::date(name, [...])` | datetimepicker |
|
||||
| `datetime` | `FormField::datetime(name, [...])` | datetimepicker z czasem |
|
||||
| `switch` | `FormField::switch(name, [...])` | checked (bool) |
|
||||
| `select` | `FormField::select(name, ['options' => [...]])` | options: [key => label] |
|
||||
| `textarea` | `FormField::textarea(name, ['rows' => 4])` | rows |
|
||||
| `editor` | `FormField::editor(name, ['toolbar' => 'MyTool'])` | CKEditor |
|
||||
| `image` | `FormField::image(name, ['filemanager' => true])` | filemanager URL |
|
||||
| `file` | `FormField::file(name, [...])` | filemanager |
|
||||
| `hidden` | `FormField::hidden(name, value)` | - |
|
||||
| `color` | `FormField::color(name, ['label' => '...'])` | HTML5 color picker + text input |
|
||||
| `lang_section` | `FormField::langSection(name, 'tab', [fields])` | pola per język |
|
||||
|
||||
## Walidacja
|
||||
|
||||
Walidacja jest automatyczna na podstawie właściwości pól:
|
||||
- `required` - pole wymagane
|
||||
- `type` = `email` - walidacja formatu e-mail
|
||||
- `type` = `number` - walidacja liczby
|
||||
- `type` = `date` - walidacja formatu YYYY-MM-DD
|
||||
|
||||
Dla sekcji językowych walidacja jest powtarzana dla każdego aktywnego języka.
|
||||
|
||||
## Persist (zapamiętywanie danych)
|
||||
|
||||
Gdy `persist = true`:
|
||||
1. Przy błędzie walidacji dane są zapisywane w `$_SESSION['form_persist'][$formId]`
|
||||
2. Formularz automatycznie przywraca dane z sesji przy ponownym wyświetleniu
|
||||
3. Po udanym zapisie sesja jest czyszczona automatycznie przez `FormRequestHandler`
|
||||
|
||||
## Przerabianie istniejących formularzy
|
||||
|
||||
1. **Kontroler** - zamień `view\Xxx::edit()` na `FormEditViewModel`
|
||||
2. **Repository** - dostosuj `save()` do formatu z `FormRequestHandler` (lub dodaj wsparcie dla obu formatów)
|
||||
3. **Szablon** - usuń stary szablon lub zostaw jako fallback
|
||||
4. **Testy** - zaktualizuj testy jeśli zmienił się format danych
|
||||
|
||||
## Aktualizacja 2026-02-15 (ver. 0.275)
|
||||
|
||||
- Modul `ShopCategory` zostal zmigrowany do warstwy Domain + DI, ale formularz kategorii nadal korzysta z legacy `gridEdit`.
|
||||
- W ramach migracji wydzielono skrypty UI do osobnych partiali `*-custom-script.php` (lista, browse, edycja, produkty), co upraszcza dalsze przepiecie formularza na `components/form-edit`.
|
||||
- Po migracji `ShopCategory` kolejnym kandydatem do pelnej migracji formularza na Form Edit System pozostaje modul `Order` (zgodnie z `REFACTORING_PLAN.md`).
|
||||
|
||||
---
|
||||
*Dokument aktualizowany: 2026-02-15*
|
||||
@@ -1,42 +0,0 @@
|
||||
# Pamięć projektu shopPRO
|
||||
|
||||
Notatki i wnioski zebrane podczas pracy z kodem. Aktualizowane na bieżąco.
|
||||
|
||||
---
|
||||
|
||||
## Serwer produkcyjny
|
||||
|
||||
- PHP < 8.0 — unikać `match`, named arguments, union types, `str_contains()` itp.
|
||||
- Zamiast `match` używać operatorów trójargumentowych (ternary) lub `if/else`
|
||||
|
||||
## Znane problemy / TODO
|
||||
|
||||
- `\Shared\Helpers\Helpers::send_email()` i `Shared\Email\Email::send()` — zduplikowana logika PHPMailer. Docelowo zunifikować w `Shared\Email\Email`
|
||||
|
||||
## Wzorce potwierdzone w projekcie
|
||||
|
||||
- Metody frontendowe (z cache Redis) dodawane do istniejących repozytoriów Domain — NIE tworzymy osobnych FrontendService/AdminService
|
||||
- Klasy View (`front\Views\*`) są statyczne i bezstanowe — nie wymagają DI
|
||||
- Kontrolery (`Controllers\*`) są instancyjne z DI przez konstruktor
|
||||
- Autoloader obsługuje dwa formaty plików: `class.X.php` (legacy) i `X.php` (nowy) — oba działają bez zmian w autoloaderze
|
||||
- Nowe katalogi z dużej litery: `Views/`, `Controllers/` (legacy: `view/`, `controls/`, `factory/`)
|
||||
|
||||
## Medoo ORM — pułapki
|
||||
|
||||
- `$mdb->delete()` przyjmuje 2 argumenty (tabela, warunek), NIE 3 — wielokrotnie powodowało bugi (np. `newsletter_unsubscribe`)
|
||||
- `$mdb->get()` zwraca `null` gdy brak rekordu, NIE `false`
|
||||
- Przy `$mdb->insert()` sprawdzać `$mdb->id()` aby potwierdzić sukces
|
||||
|
||||
## Redis cache — konwencje
|
||||
|
||||
- TTL domyślnie 86400 (24h)
|
||||
- Klucze produktów: `shop\product:{id}:{lang}:{permutation_hash}`
|
||||
- Wzorzec czyszczenia: `CacheHandler::deletePattern("shop\\product:{$id}:*")`
|
||||
- Dane w cache są serializowane — wymagają `unserialize()` po `get()`
|
||||
|
||||
## Aktualizacje klienckie
|
||||
|
||||
- Pliki `*.md` NIGDY nie trafiają do ZIP aktualizacji
|
||||
- `updates/changelog.php` to plik serwisowy repozytorium, nie runtime klienta
|
||||
- Główny `.htaccess` wdrażany osobno, poza ZIP aktualizacji
|
||||
- W archiwum ZIP NIE powinno być folderu z nazwą wersji — struktura zaczyna się od katalogów projektu
|
||||
@@ -1,150 +0,0 @@
|
||||
# PAUL — Workflow dla shopPRO
|
||||
|
||||
[PAUL](https://github.com/ChristopherKahler/paul) to zestaw komend dla Claude Code, który strukturyzuje pracę nad projektem: planowanie → implementacja → weryfikacja. Działa przez slash-komendy w czacie z Claude.
|
||||
|
||||
---
|
||||
|
||||
## Jednorazowa inicjalizacja
|
||||
|
||||
```
|
||||
/paul:init
|
||||
```
|
||||
|
||||
Uruchom raz — tworzy plik `.paul/` z konfiguracją projektu (milestones, fazy). Jeśli już zainicjalizowany, pomijasz ten krok.
|
||||
|
||||
---
|
||||
|
||||
## Nowa funkcja (feature)
|
||||
|
||||
### 1. Omów wizję
|
||||
|
||||
```
|
||||
/paul:discuss
|
||||
```
|
||||
|
||||
Claude zadaje pytania, żeby doprecyzować, co dokładnie chcesz zbudować — wynik to jasna definicja przed planowaniem.
|
||||
|
||||
### 2. Zbadaj opcje techniczne (opcjonalnie)
|
||||
|
||||
```
|
||||
/paul:discover
|
||||
```
|
||||
|
||||
Przydatne gdy nie jesteś pewny jak zaimplementować coś technicznie — Claude przegląda kod i proponuje podejścia.
|
||||
|
||||
### 3. Zaplanuj implementację
|
||||
|
||||
```
|
||||
/paul:plan
|
||||
```
|
||||
|
||||
Tworzy szczegółowy plan z krokami, plikami do zmiany, kolejnością. Zatwierdź plan zanim ruszysz dalej.
|
||||
|
||||
### 4. Wykonaj plan
|
||||
|
||||
```
|
||||
/paul:apply
|
||||
```
|
||||
|
||||
Claude implementuje plan krok po kroku, z commitami na każdą fazę.
|
||||
|
||||
### 5. Zweryfikuj (UAT)
|
||||
|
||||
```
|
||||
/paul:verify
|
||||
```
|
||||
|
||||
Claude generuje checklistę testów manualnych — punkt po punkcie sprawdzasz czy funkcja działa.
|
||||
|
||||
---
|
||||
|
||||
## Poprawka błędu (bug fix)
|
||||
|
||||
```
|
||||
/paul:plan-fix
|
||||
```
|
||||
|
||||
Dedykowany flow dla bugów: opisujesz problem → Claude analizuje kod → tworzy plan naprawy → `/paul:apply` wykonuje.
|
||||
|
||||
Krótszy wariant dla prostych bugów — możesz też po prostu opisać błąd i użyć `/paul:plan` → `/paul:apply`.
|
||||
|
||||
---
|
||||
|
||||
## Zarządzanie sesjami
|
||||
|
||||
| Komenda | Kiedy |
|
||||
|---------|-------|
|
||||
| `/paul:progress` | Sprawdź gdzie jesteś, co dalej |
|
||||
| `/paul:pause` | Kończysz sesję — tworzy plik handoff |
|
||||
| `/paul:resume` | Zaczynasz nową sesję — wczytuje kontekst |
|
||||
| `/paul:handoff` | Generuje dokument przekazania pracy |
|
||||
|
||||
---
|
||||
|
||||
## Milestones (większe wdrożenia)
|
||||
|
||||
Gdy planujesz większą wersję (np. nowy moduł, refactor):
|
||||
|
||||
```
|
||||
/paul:milestone # Tworzysz nowy milestone
|
||||
/paul:add-phase # Dodajesz fazę do milestone
|
||||
/paul:complete-milestone # Zamykasz po wdrożeniu
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Czy PAUL może przejrzeć projekt pod kątem błędów?
|
||||
|
||||
Tak, częściowo. Najlepsze podejście:
|
||||
|
||||
### Mapowanie kodu
|
||||
|
||||
```
|
||||
/paul:map-codebase
|
||||
```
|
||||
|
||||
Generuje analizę architektury — wyłapuje niespójności, martwy kod, problematyczne zależności.
|
||||
|
||||
### Research konkretnego obszaru
|
||||
|
||||
```
|
||||
/paul:research
|
||||
```
|
||||
|
||||
Np. _"Sprawdź czy walidacja zamówień w OrderRepository jest kompletna"_ — Claude przegląda kod i raportuje problemy.
|
||||
|
||||
### Ograniczenia
|
||||
|
||||
PAUL nie jest narzędziem do statycznej analizy kodu (jak PHPStan czy psalm). Do systematycznego przeglądu błędów lepiej połączyć:
|
||||
|
||||
- **PAUL** (`/paul:map-codebase` + `/paul:research`) — problemy architektoniczne, logika biznesowa
|
||||
- **Testy** (`./test.ps1`) — regresje, złe zachowanie metod
|
||||
- **PHPStan** (jeśli dodany do projektu) — typy, niezdefiniowane zmienne
|
||||
|
||||
---
|
||||
|
||||
## Typowy dzień pracy w shopPRO
|
||||
|
||||
```
|
||||
# Rano — wznów kontekst
|
||||
/paul:resume
|
||||
|
||||
# W trakcie — sprawdź co dalej
|
||||
/paul:progress
|
||||
|
||||
# Nowa funkcja lub bug
|
||||
/paul:discuss → /paul:plan → /paul:apply → /paul:verify
|
||||
|
||||
# Na koniec dnia
|
||||
/paul:pause
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Dobre praktyki
|
||||
|
||||
- Zawsze zatwierdzaj plan (`/paul:plan`) zanim uruchomisz `/paul:apply` — łatwiej zmienić plan niż cofnąć kod
|
||||
- Po każdym `/paul:apply` odpal testy: `./test.ps1`
|
||||
- Używaj `/paul:verify` przed mergem — generuje checklistę zamiast zgadywać co przetestować
|
||||
- Przy bugach najpierw opisz symptom dokładnie, Claude lepiej planuje naprawę mając konkretny przypadek
|
||||
|
||||
@@ -1,190 +0,0 @@
|
||||
# Struktura Projektu shopPRO
|
||||
|
||||
Aktualna architektura po zakonczonej migracji na Domain-Driven Design + Dependency Injection.
|
||||
|
||||
## Warstwa domenowa (`autoload/Domain/`)
|
||||
|
||||
Kazdy modul zawiera Repository (i opcjonalnie dodatkowe klasy). Konstruktor DI z `$db` (Medoo). Metody sluza zarowno adminowi, jak i frontendowi (wspolna warstwa).
|
||||
|
||||
| Modul | Klasy | Uwagi |
|
||||
|-------|-------|-------|
|
||||
| Article | ArticleRepository | blog, aktualnosci, galerie, pliki |
|
||||
| Attribute | AttributeRepository | cechy produktow + wartosci |
|
||||
| Banner | BannerRepository | banery glowne + boczne, Redis cache |
|
||||
| Basket | BasketCalculator | summary, count, walidacja stanow |
|
||||
| Cache | CacheRepository | czyszczenie cache z poziomu admin |
|
||||
| Category | CategoryRepository | drzewa kategorii, produkty w kategorii, Redis cache |
|
||||
| Client | ClientRepository | CRUD, auth, adresy, zamowienia |
|
||||
| Coupon | CouponRepository | kupony rabatowe, walidacja, uzycie |
|
||||
| CronJob | CronJobType, CronJobRepository, CronJobProcessor | kolejka zadan cron (DB), priorytety, retry/backoff, harmonogram |
|
||||
| Dashboard | DashboardRepository | statystyki admin, Redis cache |
|
||||
| Dictionaries | DictionariesRepository | slowniki admin |
|
||||
| Integrations | IntegrationsRepository | Apilo sync, ustawienia |
|
||||
| Languages | LanguagesRepository | jezyki, tlumaczenia |
|
||||
| Layouts | LayoutsRepository | layouty stron, 3-level fallback |
|
||||
| Newsletter | NewsletterRepository, NewsletterPreviewRenderer | subskrypcje, szablony, kolejka wysylki |
|
||||
| Order | OrderRepository, OrderAdminService | CRUD, Apilo sync, webhooki platnosci, kolejka retry |
|
||||
| Pages | PagesRepository | strony, menu, drzewa stron |
|
||||
| PaymentMethod | PaymentMethodRepository | metody platnosci, mapowanie Apilo |
|
||||
| Producer | ProducerRepository | producenci |
|
||||
| Product | ProductRepository | CRUD, cache, kombinacje, zdjecia, Google Feed XML |
|
||||
| ProductSet | ProductSetRepository | zestawy produktow |
|
||||
| Promotion | PromotionRepository | promocje, 5 typow applyType*, silnik dopasowania |
|
||||
| Scontainers | ScontainersRepository | kontenery sidebaru |
|
||||
| Settings | SettingsRepository | ustawienia sklepu |
|
||||
| ShopStatus | ShopStatusRepository | statusy zamowien, mapowanie Apilo |
|
||||
| Transport | TransportRepository | transport, koszty, powiazanie z platnosci |
|
||||
| Update | UpdateRepository | aktualizacje, migracje SQL |
|
||||
| User | UserRepository | uzytkownicy admin, 2FA, logowanie |
|
||||
|
||||
## Warstwa admin (`autoload/admin/`)
|
||||
|
||||
### Router: `admin\App`
|
||||
- `getControllerFactories()` — mapa kontrolerow z DI wiring
|
||||
- Brak fallbacku na legacy — wszystkie moduly na nowych kontrolerach
|
||||
|
||||
### Kontrolery (`admin\Controllers\`) — 28 kontrolerow
|
||||
ArticlesArchive, Articles, Banner, Dashboard, Dictionaries, Filemanager, Integrations, Languages, Layouts, Newsletter, Pages, ProductArchive, Scontainers, Settings, ShopAttribute, ShopCategory, ShopClients, ShopCoupon, ShopOrder, ShopPaymentMethod, ShopProducer, ShopProduct, ShopProductSets, ShopPromotion, ShopStatuses, ShopTransport, Update, Users
|
||||
|
||||
### Support
|
||||
- `admin\Support\TableListRequestFactory` — paginacja/sortowanie tabel
|
||||
- `admin\Support\Forms\FormRequestHandler` — obsluga formularzy (persist przy bledach)
|
||||
- `admin\Support\Forms\FormFieldRenderer` — renderowanie pol formularzy
|
||||
|
||||
### ViewModels
|
||||
- `admin\ViewModels\Forms\` — FormEditViewModel, FormField, FormTab, FormAction, FormFieldType
|
||||
- `admin\ViewModels\Common\PaginatedTableViewModel`
|
||||
|
||||
### Walidacja
|
||||
- `admin\Validation\FormValidator` — reguly per pole, sekcje jezykowe
|
||||
|
||||
## Warstwa frontend (`autoload/front/`)
|
||||
|
||||
### Router: `front\App`
|
||||
- `route()`, `checkUrlParams()`, `getControllerFactories()`
|
||||
|
||||
### Layout Engine: `front\LayoutEngine`
|
||||
- `show()` — zamiana tagow szablonowych (kategorie, produkty, menu, banery, artykuly, kontenery, meta)
|
||||
- `contact()`, `cookieInformation()`
|
||||
|
||||
### Kontrolery (`front\Controllers\`) — 8 kontrolerow
|
||||
Newsletter, Search, ShopBasket, ShopClient, ShopCoupon, ShopOrder, ShopProducer, ShopProduct
|
||||
|
||||
### Widoki (`front\Views\`) — 11 klas statycznych
|
||||
Articles, Banners, Languages, Menu, Newsletter, Scontainers, ShopCategory, ShopClient, ShopPaymentMethod, ShopProduct, ShopSearch
|
||||
|
||||
## Warstwa API (`autoload/api/`)
|
||||
|
||||
REST API dla ordersPRO. Entry point: `api.php`. Stateless (bez sesji), autentykacja przez `X-Api-Key` header.
|
||||
|
||||
### Router: `api\ApiRouter`
|
||||
- `handle()` — autentykacja → routing → dispatch
|
||||
- Helpery statyczne: `sendSuccess()`, `sendError()`, `getJsonBody()`, `requireMethod()`
|
||||
|
||||
### Kontrolery (`api\Controllers\`)
|
||||
- `OrdersApiController` — lista, szczegoly, zmiana statusu, platnosc (5 akcji)
|
||||
- `ProductsApiController` — lista, szczegoly, tworzenie, aktualizacja produktow (4 akcje)
|
||||
- `DictionariesApiController` — statusy, transporty, metody platnosci (3 akcje)
|
||||
- `CategoriesApiController` — lista aktywnych kategorii (1 akcja)
|
||||
|
||||
Dokumentacja: `docs/API.md`
|
||||
|
||||
## Warstwa wspoldzielona (`autoload/Shared/`)
|
||||
|
||||
| Klasa | Opis |
|
||||
|-------|------|
|
||||
| `Shared\Cache\CacheHandler` | Redis cache: get/set/delete/deletePattern |
|
||||
| `Shared\Cache\RedisConnection` | Singleton polaczenia Redis |
|
||||
| `Shared\Email\Email` | Wrapper PHPMailer |
|
||||
| `Shared\Helpers\Helpers` | SEO, email, cache clearing, shortPrice, utility |
|
||||
| `Shared\Html\Html` | Helpery HTML |
|
||||
| `Shared\Image\ImageManipulator` | Obrobka obrazow GD |
|
||||
| `Shared\Tpl\Tpl` | Silnik szablonow: render(), set() |
|
||||
|
||||
## Cache Redis
|
||||
|
||||
### Klucze
|
||||
```
|
||||
shop\product:{id}:{lang}:{permutation_hash} — dane produktu (TTL 24h)
|
||||
ProductRepository::getProductPermutationQuantityOptions:v2:{id}:{perm} — ilosc + komunikaty
|
||||
ProductRepository::productSetsWhenAddToBasket:{id} — zestawy "kupowane razem"
|
||||
```
|
||||
|
||||
### Konwencje
|
||||
- TTL domyslnie 86400 (24h)
|
||||
- Dane serializowane — `unserialize()` po `get()`
|
||||
- Czyszczenie: `CacheHandler::deletePattern("shop\\product:{$id}:*")`
|
||||
- Czyszczenie z poziomu admin: `Shared\Helpers\Helpers::clear_product_cache($id)`
|
||||
- Przycisk "Wyczysc cache" w admin: `SettingsController::clearCacheAjax()` → `flushAll()` Redis + `temp/` + `thumbs/`
|
||||
|
||||
## Entry pointy
|
||||
|
||||
| Plik | Rola |
|
||||
|------|------|
|
||||
| `index.php` | Frontend — autoload, sesja, DB, routing (`front\App`), layout (`front\LayoutEngine`), DOM post-processing |
|
||||
| `ajax.php` | Frontend AJAX — koszyk, transport, kontakt |
|
||||
| `api.php` | REST API (ordersPRO + Ekomi CSV) — router: `\api\ApiRouter`, kontrolery: `\api\Controllers\` |
|
||||
| `admin/index.php` | Admin — autoload, sesja, DB, routing (`admin\App`) |
|
||||
| `admin/ajax.php` | Admin AJAX |
|
||||
| `cron.php` | CRON: Apilo sync (ceny/stany co 10min, cennik co 1h, retry queue) |
|
||||
| `cron-turstmate.php` | TrustMate integracja |
|
||||
| `cron/cron-xml.php` | Google Feed XML |
|
||||
| `download.php` | Pobieranie plikow |
|
||||
|
||||
### Autoloader
|
||||
Kazdy entry point rejestruje `__autoload_my_classes()`:
|
||||
1. Probuje `autoload/{namespace}/class.{ClassName}.php` (legacy format)
|
||||
2. Probuje `autoload/{namespace}/{ClassName}.php` (PSR-4 format)
|
||||
|
||||
### Routing frontend (index.php)
|
||||
Przed `front\App::route()`:
|
||||
1. Sprawdza tabele `pp_redirects` → 301 redirect
|
||||
2. Sprawdza tabele `pp_routes` → regex pattern → destination
|
||||
|
||||
### Newsletter queue
|
||||
`index.php` wywoluje `$newsletterRepo->sendQueued()` na koncu kazdego requestu frontendowego (limit 1 mail/request).
|
||||
|
||||
## Integracje zewnetrzne
|
||||
|
||||
### Apilo (cron.php)
|
||||
- Synchronizacja cen/stanow produktow (co 10 min)
|
||||
- Synchronizacja cennika (co 1h)
|
||||
- Kolejka retry: `temp/apilo-sync-queue.json` — `OrderAdminService::processApiloSyncQueue()`
|
||||
- Mapowanie statusow i platnosci przez tabele `pp_shop_statuses` i `pp_shop_payment_methods`
|
||||
|
||||
### Webhooki platnosci (front\Controllers\ShopOrderController)
|
||||
- tPay, Przelewy24, Hotpay — ujednolicone: `set_as_paid` + `update_status`
|
||||
|
||||
## Biblioteki (`libraries/`)
|
||||
|
||||
- `medoo/medoo.php` — Medoo ORM (`$mdb`)
|
||||
- `rb.php` — RedBeanPHP ORM (`\R::`, `$pdo`)
|
||||
- `phpmailer/` — PHPMailer
|
||||
|
||||
## Wzorce architektoniczne
|
||||
|
||||
### DI zamiast global
|
||||
```php
|
||||
// Kontroler wiring (w admin\App lub front\App)
|
||||
$repo = new \Domain\Example\ExampleRepository($mdb);
|
||||
$controller = new \admin\Controllers\ExampleController($repo);
|
||||
```
|
||||
|
||||
### Wspolna warstwa Domain
|
||||
Metody frontendowe (z Redis cache) dodawane do istniejacych repozytoriow — NIE tworzymy osobnych FrontendService/AdminService.
|
||||
|
||||
### Klasy View — statyczne, bezstanowe
|
||||
`front\Views\*` — nie wymagaja DI. Czyste funkcje: dane wchodza, HTML wychodzi.
|
||||
|
||||
### Kontrolery — instancyjne z DI
|
||||
`Controllers\*` — repozytoria wstrzykiwane przez konstruktor.
|
||||
|
||||
### Nazewnictwo plikow
|
||||
- Nowe: `ClassName.php`
|
||||
- Legacy (pozostalosci): `class.ClassName.php`
|
||||
- Autoloader obsluguje oba formaty
|
||||
|
||||
### Nazewnictwo katalogow
|
||||
- Nowe: z duzej litery (`Views/`, `Controllers/`)
|
||||
- Namespace `\admin\` z malej (bo katalog `admin/` jest z malej na serwerze Linux)
|
||||
- NIE uzywac `\Admin\` (duze A)
|
||||
131
docs/TESTING.md
131
docs/TESTING.md
@@ -1,131 +0,0 @@
|
||||
# Testowanie shopPRO
|
||||
|
||||
## Szybki start
|
||||
|
||||
```bash
|
||||
# Pelny suite (PowerShell — rekomendowane)
|
||||
./test.ps1
|
||||
|
||||
# Konkretny plik
|
||||
./test.ps1 tests/Unit/Domain/Product/ProductRepositoryTest.php
|
||||
|
||||
# Konkretny test
|
||||
./test.ps1 --filter testGetQuantityReturnsCorrectValue
|
||||
|
||||
# Alternatywne
|
||||
composer test # standard
|
||||
./test.bat # testdox (czytelna lista)
|
||||
./test-simple.bat # kropki
|
||||
./test-debug.bat # debug
|
||||
./test.sh # Git Bash
|
||||
```
|
||||
|
||||
## Aktualny stan
|
||||
|
||||
```text
|
||||
OK (823 tests, 2284 assertions)
|
||||
```
|
||||
|
||||
Zweryfikowano: 2026-04-18 (ver. 0.347)
|
||||
|
||||
## Konfiguracja
|
||||
|
||||
- **PHPUnit 9.6** via `phpunit.phar`
|
||||
- **Bootstrap:** `tests/bootstrap.php`
|
||||
- **Config:** `phpunit.xml`
|
||||
|
||||
## Struktura testow
|
||||
|
||||
```
|
||||
tests/
|
||||
|-- bootstrap.php
|
||||
|-- stubs/
|
||||
| |-- CacheHandler.php (inline w bootstrap)
|
||||
| |-- Helpers.php (Shared\Helpers\Helpers stub)
|
||||
| `-- ShopProduct.php (shop\Product stub)
|
||||
|-- Unit/
|
||||
| |-- Domain/
|
||||
| | |-- Article/ArticleRepositoryTest.php
|
||||
| | |-- Attribute/AttributeRepositoryTest.php
|
||||
| | |-- Banner/BannerRepositoryTest.php
|
||||
| | |-- Basket/BasketCalculatorTest.php
|
||||
| | |-- Cache/CacheRepositoryTest.php
|
||||
| | |-- Category/CategoryRepositoryTest.php
|
||||
| | |-- Coupon/CouponRepositoryTest.php
|
||||
| | |-- CronJob/CronJobTypeTest.php
|
||||
| | |-- CronJob/CronJobRepositoryTest.php
|
||||
| | |-- CronJob/CronJobProcessorTest.php
|
||||
| | |-- Dictionaries/DictionariesRepositoryTest.php
|
||||
| | |-- Integrations/IntegrationsRepositoryTest.php
|
||||
| | |-- Languages/LanguagesRepositoryTest.php
|
||||
| | |-- Layouts/LayoutsRepositoryTest.php
|
||||
| | |-- Newsletter/NewsletterRepositoryTest.php
|
||||
| | |-- Pages/PagesRepositoryTest.php
|
||||
| | |-- PaymentMethod/PaymentMethodRepositoryTest.php
|
||||
| | |-- Producer/ProducerRepositoryTest.php
|
||||
| | |-- Product/ProductRepositoryTest.php
|
||||
| | |-- ProductSet/ProductSetRepositoryTest.php
|
||||
| | |-- Promotion/PromotionRepositoryTest.php
|
||||
| | |-- Settings/SettingsRepositoryTest.php
|
||||
| | |-- ShopStatus/ShopStatusRepositoryTest.php
|
||||
| | |-- Transport/TransportRepositoryTest.php
|
||||
| | |-- Update/UpdateRepositoryTest.php
|
||||
| | `-- User/UserRepositoryTest.php
|
||||
| |-- Shared/
|
||||
| | `-- Security/
|
||||
| | `-- CsrfTokenTest.php
|
||||
| `-- admin/
|
||||
| `-- Controllers/
|
||||
| |-- ArticlesControllerTest.php
|
||||
| |-- DictionariesControllerTest.php
|
||||
| |-- IntegrationsControllerTest.php
|
||||
| |-- ProductArchiveControllerTest.php
|
||||
| |-- SettingsControllerTest.php
|
||||
| |-- ShopAttributeControllerTest.php
|
||||
| |-- ShopCategoryControllerTest.php
|
||||
| |-- ShopCouponControllerTest.php
|
||||
| |-- ShopPaymentMethodControllerTest.php
|
||||
| |-- ShopProducerControllerTest.php
|
||||
| |-- ShopProductControllerTest.php
|
||||
| |-- ShopProductSetsControllerTest.php
|
||||
| |-- ShopPromotionControllerTest.php
|
||||
| |-- ShopStatusesControllerTest.php
|
||||
| |-- ShopTransportControllerTest.php
|
||||
| `-- UsersControllerTest.php
|
||||
| |-- front/Controllers/
|
||||
| | `-- ShopBasketControllerTest.php
|
||||
| `-- api/
|
||||
| |-- ApiRouterTest.php
|
||||
| `-- Controllers/
|
||||
| |-- OrdersApiControllerTest.php
|
||||
| |-- ProductsApiControllerTest.php
|
||||
| `-- DictionariesApiControllerTest.php
|
||||
`-- Integration/ (puste — zarezerwowane)
|
||||
```
|
||||
|
||||
## Dodawanie nowych testow
|
||||
|
||||
1. Plik w `tests/Unit/Domain/<Module>/<Class>Test.php`, `tests/Unit/admin/Controllers/<Class>Test.php` lub `tests/Unit/api/Controllers/<Class>Test.php`.
|
||||
2. Rozszerz `PHPUnit\Framework\TestCase`.
|
||||
3. Nazwy metod zaczynaj od `test`.
|
||||
4. Wzorzec AAA: Arrange, Act, Assert.
|
||||
|
||||
## Mockowanie Medoo
|
||||
|
||||
```php
|
||||
$mockDb = $this->createMock(\medoo::class);
|
||||
$mockDb->method('get')->willReturn(42);
|
||||
|
||||
$repo = new ProductRepository($mockDb);
|
||||
$value = $repo->getQuantity(123);
|
||||
|
||||
$this->assertEquals(42, $value);
|
||||
```
|
||||
|
||||
## Bootstrap — stuby
|
||||
|
||||
`tests/bootstrap.php` rejestruje autoloader i definiuje stuby:
|
||||
- `Redis`, `RedisConnection` — klasy Redis (aby nie wymagac rozszerzenia)
|
||||
- `Shared\Cache\CacheHandler` — inline stub z `get()`/`set()`/`exists()`/`delete()`/`deletePattern()`
|
||||
- `Shared\Helpers\Helpers` — z `tests/stubs/Helpers.php`
|
||||
- `shop\Product` — z `tests/stubs/ShopProduct.php`
|
||||
3610
docs/TODO.md
3610
docs/TODO.md
File diff suppressed because it is too large
Load Diff
@@ -1,73 +0,0 @@
|
||||
# Instrukcja tworzenia aktualizacji shopPRO
|
||||
|
||||
## Nowy sposób (od v0.301) — automatyczny build script
|
||||
|
||||
### Wymagania
|
||||
- Git z tagami wersji (np. `v0.299`, `v0.300`)
|
||||
- PowerShell
|
||||
|
||||
### Workflow
|
||||
|
||||
```
|
||||
1. Pracuj normalnie: commit, push, commit, push...
|
||||
2. Gdy wersja gotowa:
|
||||
→ git tag v0.XXX
|
||||
→ ./build-update.ps1 -ToTag v0.XXX -ChangelogEntry "NEW - opis"
|
||||
3. Upload plików z updates/0.XX/ na serwer aktualizacji
|
||||
```
|
||||
|
||||
### Użycie build-update.ps1
|
||||
|
||||
```powershell
|
||||
# Podgląd zmian (bez tworzenia plików)
|
||||
./build-update.ps1 -ToTag v0.301 -DryRun
|
||||
|
||||
# Budowanie paczki (auto-detect poprzedniego tagu)
|
||||
./build-update.ps1 -ToTag v0.301 -ChangelogEntry "NEW - opis zmiany"
|
||||
|
||||
# Z jawnym tagiem źródłowym
|
||||
./build-update.ps1 -FromTag v0.300 -ToTag v0.301 -ChangelogEntry "NEW - opis"
|
||||
```
|
||||
|
||||
### Co robi skrypt automatycznie
|
||||
1. `git diff --name-status` między tagami → listy dodanych/zmodyfikowanych/usuniętych plików
|
||||
2. Filtrowanie przez `.updateignore` (pliki deweloperskie, konfiguracyjne itp.)
|
||||
3. Kopiowanie plików do temp, tworzenie ZIP
|
||||
4. SHA256 checksum ZIP-a
|
||||
5. Generowanie `ver_X.XXX_manifest.json`
|
||||
6. Generowanie legacy `_sql.txt` i `_files.txt` (okres przejściowy)
|
||||
7. Aktualizacja `versions.php` i `changelog.php`
|
||||
8. Cleanup
|
||||
|
||||
### Pliki wynikowe
|
||||
- `updates/0.XX/ver_X.XXX.zip` — paczka z plikami
|
||||
- `updates/0.XX/ver_X.XXX_manifest.json` — manifest z checksumem, listą zmian, SQL
|
||||
- `updates/0.XX/ver_X.XXX_sql.txt` — legacy SQL (okres przejściowy)
|
||||
- `updates/0.XX/ver_X.XXX_files.txt` — legacy lista plików do usunięcia (okres przejściowy)
|
||||
|
||||
### Migracje SQL
|
||||
Pliki SQL umieszczaj w `migrations/{version}.sql` (np. `migrations/0.301.sql`).
|
||||
Build script automatycznie je wczyta i umieści w manifeście + legacy `_sql.txt`.
|
||||
|
||||
### Format manifestu
|
||||
```json
|
||||
{
|
||||
"version": "0.301",
|
||||
"date": "2026-02-22",
|
||||
"checksum_zip": "sha256:abc123...",
|
||||
"files": {
|
||||
"modified": ["autoload/Domain/Order/OrderRepository.php"],
|
||||
"added": ["autoload/Domain/Order/NewHelper.php"],
|
||||
"deleted": ["autoload/shop/OldClass.php"]
|
||||
},
|
||||
"directories_deleted": [],
|
||||
"sql": ["ALTER TABLE pp_x ADD COLUMN y INT DEFAULT 0"],
|
||||
"changelog": "NEW - opis zmiany"
|
||||
}
|
||||
```
|
||||
|
||||
### .updateignore
|
||||
Plik w katalogu głównym projektu, wzorce plików wykluczonych z paczek (jak `.gitignore`).
|
||||
|
||||
### INFO
|
||||
pamiętaj że push czasem zwraca błąd autoryzacji, wtedy spróbuj ponownie
|
||||
Reference in New Issue
Block a user