Files
shopPRO/.paul/codebase/architecture.md
2026-03-12 13:36:06 +01:00

236 lines
10 KiB
Markdown

# Architecture & Structure
## Directory Layout
```
shopPRO/
├── autoload/ # Core application code (custom autoloader)
│ ├── Domain/ # Business logic — 29 modules
│ ├── Shared/ # Cross-cutting utilities
│ │ ├── Cache/ # CacheHandler, RedisConnection
│ │ ├── Email/ # Email (PHPMailer wrapper)
│ │ ├── Helpers/ # Static utility methods
│ │ ├── Html/ # HTML escaping/generation
│ │ ├── Image/ # ImageManipulator
│ │ └── Tpl/ # Template engine
│ ├── admin/ # Admin panel layer
│ │ ├── App.php # Router & DI factory
│ │ ├── Controllers/ # 28 DI controllers
│ │ ├── Support/ # Forms, TableListRequestFactory
│ │ ├── Validation/ # FormValidator
│ │ └── ViewModels/ # Forms/, Common/
│ ├── front/ # Frontend layer
│ │ ├── App.php # Router & DI factory
│ │ ├── LayoutEngine.php # Placeholder-based layout engine
│ │ ├── Controllers/ # 8 DI controllers
│ │ └── Views/ # 11 static view classes
│ └── api/ # REST API layer
│ ├── ApiRouter.php # Auth + routing
│ └── Controllers/ # 4 DI controllers
├── admin/
│ ├── index.php # Admin entry point
│ ├── ajax.php # Admin AJAX handler
│ ├── templates/ # Admin view templates
│ └── layout/ # Admin CSS/JS/icons
├── templates/ # Frontend view templates
├── libraries/ # Third-party libraries
├── tests/ # PHPUnit test suite
├── docs/ # Technical documentation
├── index.php # Frontend entry point
├── ajax.php # Frontend AJAX handler
├── api.php # REST API entry point
├── cron.php # Background job processor
└── config.php # DB/Redis config (NOT in repo)
```
## Autoloader
Custom autoloader in each entry point — tries two conventions:
1. `autoload/{namespace}/class.{ClassName}.php` (legacy)
2. `autoload/{namespace}/{ClassName}.php` (PSR-4 style, preferred)
**Namespace → directory mapping (case-sensitive on Linux):**
- `\Domain\``autoload/Domain/`
- `\admin\``autoload/admin/` (**lowercase a** — never `\Admin\`)
- `\front\``autoload/front/`
- `\api\``autoload/api/`
- `\Shared\``autoload/Shared/`
## Dependency Injection
Manual factory pattern in router classes. Each entry point wires dependencies once:
```php
// Example from admin\App::getControllerFactories()
'ShopProduct' => function() {
global $mdb;
return new \admin\Controllers\ShopProductController(
new \Domain\Product\ProductRepository($mdb),
new \Domain\Integrations\IntegrationsRepository($mdb),
new \Domain\Languages\LanguagesRepository($mdb)
);
}
```
DI wiring locations:
- Admin: `autoload/admin/App.php``getControllerFactories()`
- Frontend: `autoload/front/App.php``getControllerFactories()`
- API: `autoload/api/ApiRouter.php``getControllerFactories()`
## Routing
### Admin (`\admin\App`)
- URL: `/admin/?module=shop_product&action=view_list`
- `module` → PascalCase (`shop_product``ShopProduct`) → controller lookup
- `action` → method call on controller
- Auth checked before routing; 2FA supported
### Frontend (`\front\App`)
- Routes stored in `pp_routes` table (regex patterns, cached in Redis as `pp_routes:all`)
- Match URI → extract destination params → merge with `$_GET`
- Special params: `?product=ID`, `?category=ID`, `?article=ID`
- Controller dispatch via `getControllerFactories()`
- Unmatched → static page content
### API (`\api\ApiRouter`)
- URL: `/api.php?endpoint=orders&action=getOrders`
- Stateless — auth via `X-Api-Key` header (`hash_equals()`)
- `endpoint` → controller, `action` → method
## Request Lifecycle (Frontend)
```
HTTP GET /produkt/nazwa-produktu
→ index.php (autoload, init Medoo, session, language)
→ Fetch pp_routes from Redis (or DB)
→ Regex match → extract ?product=123
→ front\LayoutEngine::show()
→ Determine layout (pp_layouts)
→ Replace placeholders [MENU:ID], [BANER_STRONA_GLOWNA], etc.
→ Call view classes / repositories for each placeholder
→ Output HTML (with GTM, meta OG, WebP, lazy loading)
```
## Request Lifecycle (Admin)
```
HTTP GET /admin/?module=shop_order&action=view_list
→ admin/index.php (IP check, session, auth cookie check)
→ admin\App::update() (run pending DB migrations)
→ admin\App::special_actions() (handle s-action=user-logon etc.)
→ admin\App::render()
→ Auth check → if not logged in, show login form
→ admin\App::route()
→ 'shop_order' → ShopOrder → factory()
→ new ShopOrderController(OrderAdminService, ProductRepository)
→ ShopOrderController::viewList()
→ Tpl::view('shop-order/orders-list', [...])
→ Tpl::render('site/main-layout', ['content' => $html])
→ Output admin HTML
```
## Domain Modules (29)
All in `autoload/Domain/{Module}/{Module}Repository.php`:
| Module | Repository | Notes |
|--------|-----------|-------|
| Article | ArticleRepository | Blog/news |
| Attribute | AttributeRepository | Product attributes (color, size) |
| Banner | BannerRepository | Promo banners |
| Basket | (static) | Cart calculations |
| Cache | (utilities) | Cache key constants |
| Category | CategoryRepository | Category tree |
| Client | ClientRepository | Customer accounts |
| Coupon | CouponRepository | Discount codes |
| CronJob | CronJobRepository, CronJobProcessor | Job queue |
| Dashboard | DashboardRepository | Admin stats |
| Dictionaries | DictionariesRepository | Units, enums |
| Integrations | IntegrationsRepository | Apilo, Ekomi (**875 lines — too large**) |
| Languages | LanguagesRepository | i18n translations |
| Layouts | LayoutsRepository | Page templates |
| Newsletter | NewsletterRepository, NewsletterPreviewRenderer | Email campaigns |
| Order | OrderRepository, OrderAdminService | Orders, status |
| Pages | PagesRepository | Static pages |
| PaymentMethod | PaymentMethodRepository | Payment gateways |
| Producer | ProducerRepository | Brands |
| Product | ProductRepository | Core catalog (**3583 lines — too large**) |
| ProductSet | ProductSetRepository | Bundles |
| Promotion | PromotionRepository | Special offers |
| Scontainers | ScontainersRepository | Content blocks |
| Settings | SettingsRepository | Shop config |
| ShopStatus | ShopStatusRepository | Order statuses |
| Transport | TransportRepository | Shipping |
| Update | UpdateRepository | DB migrations |
| User | UserRepository | Admin users, 2FA |
## Admin Controllers (28)
All in `autoload/admin/Controllers/`:
`ArticlesController`, `ArticlesArchiveController`, `BannerController`, `DashboardController`, `DictionariesController`, `FilemanagerController`, `IntegrationsController`, `LanguagesController`, `LayoutsController`, `NewsletterController`, `PagesController`, `ProductArchiveController`, `ScontainersController`, `SettingsController`, `ShopAttributeController`, `ShopCategoryController`, `ShopClientsController`, `ShopCouponController`, `ShopOrderController`, `ShopPaymentMethodController`, `ShopProducerController`, `ShopProductController` (1199 lines), `ShopProductSetsController`, `ShopPromotionController`, `ShopStatusesController`, `ShopTransportController`, `UpdateController`, `UsersController`
## Frontend Controllers (8)
`autoload/front/Controllers/`: `NewsletterController`, `SearchController`, `ShopBasketController`, `ShopClientController`, `ShopCouponController`, `ShopOrderController`, `ShopProducerController`, `ShopProductController`
## Frontend Views (11, static)
`autoload/front/Views/`: `Articles`, `Banners`, `Languages`, `Menu`, `Newsletter`, `Scontainers`, `ShopCategory`, `ShopClient`, `ShopPaymentMethod`, `ShopProduct`, `ShopSearch`
## API Controllers (4)
`autoload/api/Controllers/`: `OrdersApiController`, `ProductsApiController`, `CategoriesApiController`, `DictionariesApiController`
## Template System
### Tpl Engine (`\Shared\Tpl\Tpl`)
```php
// Controller
return \Shared\Tpl\Tpl::view('shop-category/category-edit', [
'category' => $data,
'languages' => $langs,
]);
// Template (templates/shop-category/category-edit.php)
<h1><?= $this->category['name'] ?></h1>
```
Search order: `templates_user/`, `templates/`, `../templates_user/`, `../templates/`
### Frontend Layout Engine (`\front\LayoutEngine`)
Replaces placeholders in layout HTML loaded from `pp_layouts.html`:
- `[MENU:ID]`, `[KONTENER:ID]`, `[LANG:key]`
- `[PROMOWANE_PRODUKTY:limit]`, `[PRODUKTY_TOP:limit]`, `[PRODUKTY_NEW:limit]`
- `[BANER_STRONA_GLOWNA]`, `[BANERY]`, `[COPYRIGHT]`
- `[AKTUALNOSCI:layout_id:limit]`, `[PRODUKTY_KATEGORIA:cat_id:limit]`
## Admin Form System
Universal form system for CRUD views. Full docs: `docs/FORM_EDIT_SYSTEM.md`.
| Component | Class | Location |
|-----------|-------|----------|
| View model | `FormEditViewModel` | `autoload/admin/ViewModels/Forms/` |
| Field definition | `FormField` | same |
| Field type enum | `FormFieldType` | same |
| Tab | `FormTab` | same |
| Action | `FormAction` | same |
| Validation | `FormValidator` | `autoload/admin/Validation/` |
| POST parsing | `FormRequestHandler` | `autoload/admin/Support/Forms/` |
| Rendering | `FormFieldRenderer` | `autoload/admin/Support/Forms/` |
| Template | `form-edit.php` | `admin/templates/components/` |
## Authentication
### Admin
- Session: `$_SESSION['user']` after successful login
- 2FA: 6-digit code sent by email; `twofa_pending` in session during verification
- Remember Me: 14-day HMAC-SHA256 signed cookie
### API
- Stateless; `X-Api-Key` header vs `pp_settings.api_key` via `hash_equals()`
### Frontend
- Customer session in `$_SESSION['client']`
- IP validation on every request (`$_SESSION['ip']` vs `REMOTE_ADDR`)