# Controllers — `public/controllers/` UI page controllers. Each file maps to a URL segment via auto-routing. ## File Naming - `PascalCase.php` → e.g. `ClientReports.php` - URL `/client-reports` auto-routes to `ClientReports::index()` ## Base Class All UI controllers extend `AppController` (not `Controller` directly): ``` core/Controller.php ← framework core, never modify ↑ app/Controllers/AppController.php ← app-wide concerns (injects $UserProfile) ↑ public/controllers/YourController.php ``` ## Standard Controller Structure ```php view->Footer = $this->view->PartialView('admin_footer'); $this->view->SideBar = $this->view->PartialView('admin_sidebar'); $this->view->Header = $this->view->PartialView('admin_header'); $this->view->ToolBar = $this->view->PartialView('admin_toolbar'); } function index() { $clients = Db::select("SELECT * FROM sp_clients WHERE active = 1"); $this->view->Clients = $clients; $this->view->render(__CLASS__ . '/' . __FUNCTION__); } function show($id) { $client = Db::getRow("SELECT * FROM sp_clients WHERE id = :id", [':id' => $id]); if (!$client) { http_response_code(404); die('Not found'); } $this->view->Client = $client; $this->view->render(__CLASS__ . '/' . __FUNCTION__); } } ``` ## Render Conventions ```php // Standard admin page (default wrapper) $this->view->render(__CLASS__ . '/' . __FUNCTION__); // Login / minimal wrapper $this->view->render(__CLASS__ . '/' . __FUNCTION__, 'login'); // Welcome screen (config flag) if (WELCOME) { $this->view->render(__CLASS__ . '/welcome', '', false); } ``` ## Injecting Page-Specific Assets ```php function index() { // CSS $this->Styles[] = '/public/assets/plugins/datatables/datatables.bundle.css'; $this->view->Styles = $this->Styles; // JS $this->JavaScript[] = '/public/assets/plugins/datatables/datatables.bundle.js'; $this->JavaScript[] = '/public/assets/js/page-script.js'; $this->view->JavaScript = $this->JavaScript; $this->view->render(__CLASS__ . '/' . __FUNCTION__); } ``` ## Authentication ```php Auth::handleLogin(); // redirects to /login if not authenticated Auth::can('users.create') // returns bool Auth::requirePermission('users.delete'); // halts with 403 JSON if denied ``` ## Passing Data to Views ```php $this->view->Users = Db::select("SELECT * FROM sp_users"); $this->view->PageTitle = 'User List'; // accessed in view as $Users, $PageTitle ``` ## CRUD Pattern ```php function store() { try { if (!Validator::required($_POST['name'])) { throw new Exception('Name is required'); } $id = Db::insert('sp_clients', [ 'name' => $_POST['name'], 'created_at' => date('Y-m-d H:i:s') ]); redirect('/clients/show/' . $id); } catch (Exception $e) { if (DEBUG) { echo $e->getMessage(); } else { $this->view->Error = 'Could not save. Please try again.'; $this->view->FormData = $_POST; $this->view->render(__CLASS__ . '/create'); } } } function update($id) { Db::update('sp_clients', ['name' => $_POST['name'], 'updated_at' => date('Y-m-d H:i:s')], 'id = :id', [':id' => $id] ); redirect('/clients/show/' . $id); } function delete($id) { Db::delete('sp_clients', 'id = :id', [':id' => $id]); redirect('/clients'); } ``` ## Error Handling ```php function create() { try { Db::beginTransaction(); // ... operations ... Db::commit(); redirect('/clients'); } catch (Exception $e) { Db::rollback(); if (DEBUG) { echo "
Error: {$e->getMessage()}\nFile: {$e->getFile()}:{$e->getLine()}
"; } else { error_log("Client creation failed: " . $e->getMessage()); $this->view->Error = 'An error occurred. Please try again.'; $this->view->render(__CLASS__ . '/create'); } } } ``` ## Helper / Utility Code Rule Reusable, stateless functions → `public static` method in `app/Helpers/`, never as a private controller method. Call via `HelperClass::methodName(...)`.