Adderat Core MVC Framework Guide

Introduction

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.


Installation

To get started with the Adderat Core MVC framework:

  1. Clone the repository to your local machine.
  2. Install dependencies by running:
    composer install
    
  3. Optimize autoloading:
    composer dump-autoload -o
    

Backend Requirements


Frontend Stack


App File Structure

Controller files

Controllers are located in the app/Controllers directory. Each controller should extend the Core\Controller class to inherit core functionalities.

View files

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.

Model files

Models are stored in the app/Models directory. Every model must extend the Core\Model class, providing a structured way to handle business logic.


Controllers and Routing

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.

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.

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
    }
}

Common controller operations, methods, and URLs

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

Models represent the database structure and logic. Each model extends Core\Model and includes common static and instance methods:

Constructor Methods

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;
    }
}

Common Static Methods

Common Instance Methods


Database Access

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

Views, stored in the app/Views directory, can be rendered using the following methods provided by Core\View:

Example of rendering a view:

$this->view->setTitle('Products');
$this->view->set('products', $productsDTO);
return $this->view->renderAuto();

DTOs (Data Transfer Objects)

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:

Example usage:

class ProductDTO extends DTO
{
    public static function createFromModel(Product $product)
    {
        $dto = new self();
        $dto->fill($product->toArray());
        return $dto;
    }
}

Middleware

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));
}

Posts and method spoofing

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
    }
}

Git Branch Naming Convention

Use the following convention for naming your branches:


CSS (BEM Methodology)

We 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.


JavaScript Best Practices

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 Notifications

Toast messages provide feedback to the user. Available types:


Conclusion

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!