# Architecture

**Analysis Date:** 2026-04-27

## Pattern Overview

**Overall:** Custom PHP MVC Monolith (dual frontend — public + admin)

**Key Characteristics:**
- Single shared `core/` layer powering both public and admin frontends
- Front Controller routing via Apache rewrite → `index.php` → Router → Controller
- All templating via Smarty (no JSON API, no SPA)
- Session-based admin auth; anonymous public form with reCAPTCHA

## Layers

**Router / Front Controller:**
- Purpose: Parse URL, select controller and action
- Contains: `Router.class.php`, `index.php` entry points
- Location: `_rejestracja/index.php`, `_rejestracja/Admin/index.php`
- Depends on: Core class loader
- Used by: Nothing (entry point)

**Controller Layer:**
- Purpose: Handle request lifecycle — auth, business logic dispatch, template assignment
- Contains: `*Controller.php` files; each has `preDispatch`, `{Name}Action`, `postDispatch`
- Location: `_rejestracja/controller/` (public), `_rejestracja/Admin/controller/`
- Depends on: DAL layer, Smarty, SessionProxy, Request
- Used by: Router

**DAL Layer (Data Access):**
- Purpose: Typed database access; maps rows to model objects
- Contains: `Mf{Entity}DAL.class.php` files, `DefaultDAL.class.php` base
- Location: `_rejestracja/core/model/`
- Depends on: MySQL connection, `DalData` query config objects, `DataObject` base model
- Used by: Controllers

**Model Layer:**
- Purpose: Typed domain objects with getters/setters
- Contains: `Mf{Entity}.class.php` with `$fields` array mapping DB columns to properties
- Location: `_rejestracja/core/model/`
- Depends on: `DataObject.class.php` base
- Used by: DAL (hydration), Controllers, Templates (via Smarty assigns)

**Template Layer:**
- Purpose: HTML rendering via Smarty
- Contains: `.tpl` files, Smarty plugins (`{translate}`, `{formField}`, `{url}`, `{dropDownContainer}`)
- Location: `_rejestracja/template/`, `_rejestracja/Admin/template/`
- Depends on: Variables assigned in controller (`$this->smarty->assign(...)`)
- Used by: Nothing (output layer)

## Data Flow

**Public Registration Request:**

1. Browser `POST /_rejestracja/index` → Apache rewrite → `_rejestracja/index.php`
2. `Router` parses URL → selects `IndexController`
3. `IndexController::preDispatch()` sets up shared context (Smarty, layout)
4. `IndexController::IndexAction()` runs:
   - Validates reCAPTCHA
   - Reads POST via `Request::GetPost()`
   - Hydrates `MfParticipant` model
   - Calls `MfParticipantDAL::Save($obj)` → INSERT/UPDATE
   - Sends confirmation email via PHPMailer
   - `$this->smarty->assign(...)` + Smarty renders confirmation template
5. HTML response returned to browser

**Admin Registration List Request:**

1. Browser `GET /_rejestracja/Admin/Calc/Reg` → `Admin/index.php` → Router
2. `CalcController::preDispatch()` — calls `RunShared('Auth', $param)` (session check)
3. `CalcController::RegAction()`:
   - `MfParticipantDAL::GetResult($dalData)` with sort DESC
   - `$this->smarty->assign('arrayObj', $arrayObjReg)`
4. Smarty renders `Admin/template/partial/Calc/Reg.tpl`

**State Management:**
- PHP sessions via `SessionProxy` for admin auth and transient UI state
- No in-memory cache; every request hits MySQL

## Key Abstractions

**DataObject / Model:**
- Purpose: Typed domain objects with `$fields` array auto-mapping DB↔PHP
- Examples: `MfParticipant`, `MfParameters`, `MfDictionary`
- Pattern: Active-record-lite; `$fields = ['db_col' => 'PropName']`, auto-generates getters/setters via `__get`/`__set` or explicit methods

**DefaultDAL / DalData:**
- Purpose: Generic CRUD over MySQL; `DalData` is a query config object (conditions, sort, limit)
- Examples: `MfParticipantDAL`, `MfParametersDAL`, `MfDictionaryDAL`
- Pattern: `GetDalDataObj()` → configure → `GetResult()` / `GetById()` / `Save()` / `Delete()`

**Smarty Plugin Suite:**
- Purpose: Template-level abstractions for common patterns
- Key plugins: `{translate word='key'}` (dictionary lookup), `{formField name="" type=""}` (form inputs — text/hidden only), `{url label=X}` (router-aware URL generation), `{dropDownContainer}` (collapsible admin sections)
- Pattern: Custom Smarty plugins registered in `_rejestracja/core/`

## Entry Points

**Public Frontend:**
- Location: `_rejestracja/index.php`
- Triggers: HTTP request to `/_rejestracja/`
- Responsibilities: Bootstrap core, invoke Router → Controller

**Admin Frontend:**
- Location: `_rejestracja/Admin/index.php`
- Triggers: HTTP request to `/_rejestracja/Admin/`
- Responsibilities: Same bootstrap + admin-specific auth via `RunShared('Auth')`

## Error Handling

**Strategy:** Mostly unhandled — exceptions catch in isolated blocks, errors logged to PHP error_log

**Patterns:**
- `try/catch` in `RegDeleteAction` (DAL delete errors logged but swallowed)
- `Validator` class collects field errors and returns them to template via `$out` array
- No global exception handler observed

## Cross-Cutting Concerns

**Logging:**
- PHP `error_log` only; `Utils::ArrayDisplay()` for dev debug (should be removed pre-deploy)

**Validation:**
- `Validator` class at controller level for required-field checks
- reCAPTCHA for bot protection on public form
- No input sanitization layer beyond Smarty auto-escaping in templates

**Authentication:**
- Admin: `RunShared('Auth', $param)` in every admin controller `preDispatch`
- Public: None (anonymous registration form)

**Internationalization:**
- `{translate word='key'}` Smarty plugin reads from `mf_dictionary` table
- All visible strings for the registration form are dictionary-backed

---

*Architecture analysis: 2026-04-27*
*Update when major patterns change*
