Adderat Core MVC is a simple and efficient MVC framework tailored for Adderat projects. This guide will walk you through its installation, backend, frontend technologies, and core concepts such as routing, models, views, middleware, and more.
To get started with the Adderat Core MVC framework:
composer install
composer dump-autoload -o
Controllers are located in the app/Controllers directory. Each controller should extend the Core\Controller class to inherit core functionalities.
Views reside in the app/Views directory. They are rendered using the Core\View class, which provides methods for rendering full views, partial views, and components.
Models are stored in the app/Models directory. Every model must extend the Core\Model class, providing a structured way to handle business logic.
Routes are automatically generated based on the URL structure:
The URL /products/edit/1: invokes the edit() method in the ProductsController with the parameter 1.
ProductsControlleredit()[1]The URL /products/123 is a special case. If the second segment (method) is determined to be an identifier (such as a numeric ID or a key like key-24f-34g), the show() method is automatically invoked.
ProductsController123 is and ID, and the show() method is invoked[123]Controllers and methods that contains uppercase should use hyphens instead of camelCase in URL.
The Router will handle the conversion automatically.
URL: /ar-xbrl/list-files will invoke the listFiles() method in the ArXbrlController. Unless the method-segement contains a number, then it will be treated as an ID.
class ProductsController extends Controller
{
public function index()
{
$products = Product::getAll();
// Render list of products
}
public function edit($id)
{
$product = Product::find($id);
// Render product edit form
}
public function show($id)
{
$product = Product::find($id);
// Display product details
}
}
| Operation | HTTP Method | URL | Controller Method | Description |
|---|---|---|---|---|
| List | GET |
/products |
index() |
Retrieve and display a list of all products. |
| Create | GET |
/products/create |
create() |
Show a form to create a new product. |
| Store | POST |
/products |
store() |
Handle form submission and create a new product in the database. |
| Read | GET |
/products/{id} |
show($id) |
Display the details of a specific product by its ID. |
| Edit | GET |
/products/edit/{id} |
edit($id) |
Show a form to edit an existing product. |
| Update | POST |
/products/update/{id} |
update($id) |
Handle form submission and update the product in the database. |
| Delete | POST |
/products/delete/{id} |
destroy($id) |
Handle the deletion of a specific product. |
Models represent the database structure and logic. Each model extends Core\Model and includes common static and instance methods:
__construct($data = []): Initializes a new instance with data, particularly useful for loading related models.Example for loading child models:
public function loadChildren()
{
$sql = 'SELECT * FROM _Children WHERE _parentID = :parentID ORDER BY _index ASC';
$stmt = self::db()->query($sql, ['parentId' => $this->id]);
$results = $stmt->fetchAll(PDO::FETCH_ASSOC);
foreach ($results as $row) {
$child = new Child($row);
$this->children[$row['_childID']] = $child;
}
}
getAll(): Retrieves all records as an array of models.find($id): Finds a record by its ID and returns a single model instance.save(): Saves the current model to the database.delete(): Deletes the current model record.toArray(): Converts the model to an associative array.The Core\Database class manages the database connection. Use the db() method to access the connection in models:
// Using the chained query builder (preferred)
$productAssoc = $this->db()->table('products')->where('id', 1)->first();
$allProducts = $this->db()->table('products')->get();
// Using the query() method (for advanced queries)
$sql = 'SELECT * FROM _Children WHERE _parentID = :parentID ORDER BY _index ASC';
$stmt = self::db()->query($sql, ['parentId' => $this->id]);
$results = $stmt->fetchAll(PDO::FETCH_ASSOC);
Views, stored in the app/Views directory, can be rendered using the following methods provided by Core\View:
render(): Renders a complete view with the layout.renderPartial(): Renders a view without the layout (for partial content).renderAuto(): Automatically selects full or partial rendering based on the request header (htmx, json and ajax renders partial).renderComponent(): Used for rendering reusable components like navigation bars or footers.Example of rendering a view:
$this->view->setTitle('Products');
$this->view->set('products', $productsDTO);
return $this->view->renderAuto();
DTOs are used to transfer data securely and in a structured manner. They extend Core\DTO and help format data for the view layer or other services.
Methods:
fill(): Fills a DTO with data.toArray(): Converts the DTO to an associative array.toJson(): Converts the DTO to a JSON string.Example usage:
class ProductDTO extends DTO
{
public static function createFromModel(Product $product)
{
$dto = new self();
$dto->fill($product->toArray());
return $dto;
}
}
Middleware functions as a request filter, applied before reaching controller methods. Add middleware in the controller's constructor:
public function __construct()
{
parent::__construct();
// Example: rate-limiting middleware
$this->addMiddleware(RateLimitMiddleware::class, RateLimitMiddleware::config('auth', 5, 60));
}
| HTTP Method | Action | Common Method Name | Description |
|---|---|---|---|
GET |
Read (all) | index() |
Retrieve and display a list of resources (e.g., all products). |
GET |
Read (one) | show($id) |
Retrieve and display a single resource by its ID. |
POST |
Create | store() |
Store a newly created resource. |
PUT / PATCH |
Update | update($id) |
Update an existing resource by its ID. |
DELETE |
Delete | destroy($id) |
Delete a resource by its ID. |
To send a POST request with a specific method (e.g., DELETE or PUT), use the hidden _method field in the form:
<form action="/products/123" method="POST">
<input type="hidden" name="_method" value="PUT">
<label for="name">Product Name:</label>
<input type="text" id="name" name="name" value="Existing Product Name">
<button type="submit">Update Product</button>
</form>
// app/Controllers/ProductsController.php
class ProductsController extends Controller
{
public function update($id)
{
$product = Product::find($id);
$product->name = $_POST['name'];
$product->save();
// Redirect to product details
}
}
Use the following convention for naming your branches:
feature/feature-name e.g., feature/implement-search-barfix/fix-name e.g., fix/responsive-layout-issuehotfix/hotfix-name e.g., hotfix/restore-deleted-filesrelease/release-name e.g., release/v1.0.0We use the Block Element Modifier (BEM) naming convention primarily for styling components.
Each BEM element should be directly related to its block and not nested under another element. Their class names should reflect their relationship to the block.
/* Block */
.block {}
/* Element */
.block__element {}
/* Modifier */
.block__element--modifier {}
Examples:
.navigation {}
.navigation__item {}
.navigation__item--highlighted {}
Avoid using CSS classes in JavaScript. Instead, use data attributes or ID selectors for JS functionality.
Ensure that your JavaScript code interacts with HTML using data attributes instead of CSS classes to maintain separation of concerns:
<button class="btn btn-primary" data-plan-action="buy-now">Buy Now</button>
document.querySelectorAll('[data-plan-action="buy-now"]').forEach(button => {
button.addEventListener('click', () => {
const plan = button.closest('[data-plan]').dataset.plan;
console.log('Selected plan:', plan);
});
});
For ajax requests, use the fetch API wrapped in a custom app.fetch method. app makes sure the correct headers are set.
https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API
app.fetch('/ocr/40')
.then(response => response.text())
.then(data => {
tabDataElement.innerHTML = data;
})
Toast messages provide feedback to the user. Available types:
Toast::addDefault('Message')Toast::addSuccess('Message')Toast::addError('Message')Toast::addWarning('Message')Toast::addInfo('Message')This guide covers the fundamental aspects of the Adderat Core MVC Framework. Follow the outlined structure, conventions, and best practices to ensure maintainability and performance in your projects. Happy coding!