# 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)