169 lines
4.6 KiB
Markdown
169 lines
4.6 KiB
Markdown
|
|
# 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
|
||
|
|
<?php
|
||
|
|
class Clients extends AppController
|
||
|
|
{
|
||
|
|
public function __construct() {
|
||
|
|
parent::__construct();
|
||
|
|
Auth::handleLogin(); // redirect to /login if not authenticated
|
||
|
|
|
||
|
|
$this->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 "<pre>Error: {$e->getMessage()}\nFile: {$e->getFile()}:{$e->getLine()}</pre>";
|
||
|
|
} 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(...)`.
|