Files
adsPRO/.paul/codebase/architecture.md
2026-05-06 23:19:35 +02:00

110 lines
6.0 KiB
Markdown

# Architecture
## Entry points
| File | Purpose |
|---|---|
| [index.php](index.php) | Main router. Autoloader (lines 3-12), session init, `$route_aliases` table (lines 40-59), URL-segment fallback (lines 70-79), auth via session or persistent cookie (lines 88-102), public-path whitelist for `/api/*`, `/cron/*`, `/login` (lines 106-118). Default module: `campaigns/main_view`. |
| [ajax.php](ajax.php) | AJAX handler. Same autoloader. Session regeneration on first request, IP binding check (lines 28-33). Responses: `echo json_encode(...); exit`. |
| [api.php](api.php) | External API endpoints (~1350 lines). Uses RedBeanPHP. Helpers `api_json_response()` and `api_validate_api_key()`. |
| [cron.php](cron.php) | Legacy cron. Calls `\Cron::tasks_emails()` and `\Cron::recursive_tasks()`. |
Modern cron routes (dispatched through index.php → `\controls\Cron`):
| Route | Purpose |
|---|---|
| `/cron/cron_universal` | Google Ads campaigns + products daily snapshot |
| `/cron/cron_campaigns_product_alerts_merchant` | Product alerts from Merchant Center |
| `/cron/cron_products_urls` | Bulk fetch product URLs |
| `/cron/cron_facebook_ads` | Facebook Ads sync (30-day window) |
| `/cron/cron_xml_feed_import` | Import supplemental/product XML feeds |
## Routing
`index.php` builds a request URL into `$_GET['module']` and `$_GET['action']`:
1. Apply `$route_aliases` map (e.g. `/login``users/login_form`).
2. Fallback: `/$seg0/$seg1``module=$seg0`, `action=$seg1`.
3. Default: `campaigns/main_view`.
4. `\controls\Site::route()` instantiates `\controls\{Module}` and calls action method.
## Layers
### Controllers — `autoload/controls/` (namespace `\controls`)
Static action methods. Pattern:
```php
$id = (int) \S::get('client_id');
$rows = \factory\Campaigns::get_campaigns_list($id);
echo json_encode($rows); exit; // AJAX
return \Tpl::view('campaigns/main_view', ['clients' => $rows]); // page
```
Representative files: [autoload/controls/class.Campaigns.php](autoload/controls/class.Campaigns.php), [autoload/controls/class.Products.php](autoload/controls/class.Products.php), [autoload/controls/class.Cron.php](autoload/controls/class.Cron.php) (~5,200 lines — see [concerns.md](concerns.md)).
### Factories — `autoload/factory/` (namespace `\factory`)
Static methods wrapping `$mdb` queries. Examples:
- [autoload/factory/class.Campaigns.php](autoload/factory/class.Campaigns.php) (~400 lines)
- [autoload/factory/class.Products.php](autoload/factory/class.Products.php) (~1,540 lines)
- [autoload/factory/class.Logs.php](autoload/factory/class.Logs.php)
### Services — `autoload/services/` (namespace `\services`)
External API integrations (see [integrations.md](integrations.md)).
### Views — `autoload/view/` (namespace `\view`)
Thin orchestrators. [autoload/view/class.Site.php](autoload/view/class.Site.php) wraps controller output in `site/layout-logged.php`, injecting `campaign_alerts_count`, `user`, `current_module`, flash alerts.
### Templates — `templates/{module}/`
Rendered via `\Tpl::view('module/file', $data)`. Lookup order: `templates_user/` (override) → `templates/`. Data accessed in templates as `$this->varName` (magic `__get`). Output captured with `ob_start/ob_get_clean`.
## Modules
| Module | Controller | Purpose |
|---|---|---|
| campaigns | `\controls\Campaigns` | Google Ads campaign list, history, charts, alerts |
| products | `\controls\Products` | Merchant Center product feed, AI title/desc suggestions |
| clients | `\controls\Clients` | Merchant accounts (Google Ads ID, Merchant ID, settings) |
| users | `\controls\Users` | Auth, settings, API key, cron status dashboard |
| feeds | `\controls\Feeds` | Supplemental TSV feed generation |
| logs | `\controls\Logs` | System event log viewer |
| campaign_alerts | `\controls\CampaignAlerts` | AI-detected campaign issues |
| campaign_terms | `\controls\CampaignTerms` | Search term aggregation, keyword suggestions |
| facebook_ads | `\controls\FacebookAds` | Facebook Ads sync and tracking |
| allegro | `\controls\Allegro` | Allegro.pl marketplace integration (legacy) |
| cron | `\controls\Cron` | Cron execution dispatcher and status UI |
| site | layout only | `layout-logged.php`, `layout-unlogged.php` |
| html | components | Reusable form elements (input, textarea, select, etc.) |
## Database schema (selected)
Migrations in [migrations/](migrations/) (30+ files, e.g. `001_google_ads_settings.sql`). Tracked in `schema_migrations`. Run via `php install.php` (`--force`, `--with_demo`).
Key tables:
- `settings` — global key-value config store
- `clients` — merchant accounts (Google Ads Customer ID, Merchant ID)
- `campaigns`, `campaigns_history` — campaign metadata + daily snapshots
- `cron_sync_status` — pipeline phase tracking (pending → fetch → aggregate_30 → done)
- `campaign_alerts`, `campaign_search_terms_history`, `campaign_ad_groups`, `campaign_keywords`, `campaign_negative_keywords`
- `products`, `products_aggregate`, `products_keyword_planner_terms`, `products_merchant_sync_log`
- `logs` — structured event log (level/source/message/context JSON/client_id)
- `facebook_campaigns`, `facebook_campaigns_history`, `facebook_ad_sets`, `facebook_ads`, `facebook_ads_history`
## Request flow
**Page (HTML):** request → `.htaccess``index.php` → routing → auth → `\view\Site::show()``\controls\Site::route()` → controller action → factory → `\Tpl::view(...)` → wrapped in `site/layout-logged.php` → response.
**AJAX (JSON):** POST/GET → `ajax.php` → session+IP check → controller action → `echo json_encode(...); exit`.
**Cron (JSON):** external GET → `index.php` (whitelisted) → `\controls\Cron::cron_universal()` → service API call → write to `*_history` + `cron_sync_status``self::output_cron_response([...])`.
## Auth
Session-based, with persistent cookie. Cookie stores JSON `{email, hash}` (salted). On revisit, `index.php` lines 92-102 verify and rehydrate session. AJAX adds IP binding (`$_SESSION['ip']` vs `$_SERVER['REMOTE_ADDR']`); mismatch → `session_destroy()`.