Find framework reference pages for controllers, routes, middleware, modules, database, views, and security.

Request Lifecycle

This page explains what happens between an HTTP request and the response a user receives.

Lifecycle Overview

Browser or API client
  -> public/index.php
  -> framework bootstrap
  -> public/routes.php
  -> route file
  -> middleware
  -> controller
  -> module or repository
  -> view or response

Understanding this flow helps you decide where code belongs.

1. Front Controller

Requests enter through:

public/index.php

The front controller loads environment configuration, creates the container, registers framework services, starts request handling, and dispatches the router.

You normally do not edit this file for features.

2. Route Registration

Application routes start from:

public/routes.php

Small projects can keep routes there. Larger projects should split them:

$blogRoutes = PROJECT_ROOT . '/src/Routes/blog.php';
if (is_file($blogRoutes)) {
    (require $blogRoutes)($router);
}

Generate a route file with:

php coriander make:route blog

3. Middleware

Middleware runs before or around the route handler. Use it for request gates:

  • authentication
  • admin-only sections
  • API limits
  • security headers
  • CSRF protection

Example group:

use Middleware\AdminMiddleware;

$router->group('admin', [new AdminMiddleware()], function ($router): void {
    $router->get('dashboard', fn () => 'Admin dashboard');
});

Middleware should not render complex pages or perform business writes. It should decide whether the request may continue.

4. Controller

Controllers coordinate the request:

final class BlogController
{
    public function index(): void
    {
        $posts = (new BlogRepository())->latest();

        $this->view->render('blog/index', [
            'posts' => $posts,
        ]);
    }
}

Keep controllers readable. If a method becomes a long business workflow, move the workflow into a service under src/Modules.

5. Module Or Repository

Modules hold app-owned logic. Repositories hold persistence details.

namespace Modules\Blog;

use CorianderCore\Core\Database\SQLManager;

final class BlogRepository
{
    public function latest(): array
    {
        $rows = SQLManager::sqlScript(
            'SELECT id, title FROM posts ORDER BY created_at DESC LIMIT 10'
        );

        return $rows === [] ? [] : (array_is_list($rows) ? $rows : [$rows]);
    }
}

This keeps SQL out of views and keeps controllers focused on flow.

6. View Or Response

HTML pages render views from:

public/public_views/

API endpoints return JSON responses instead of HTML views.

Use views for presentation, not data loading. Escape public strings before output.

Write Request Flow

For forms and other writes, use Post/Redirect/Get:

POST /posts
  -> validate input
  -> authorize action
  -> write through service or repository
  -> store flash message
  -> redirect to GET page

This prevents browser refresh from resubmitting the form.

Debugging The Flow

When a route does not work, check in order:

  1. Is the route file required by public/routes.php?
  2. Does the URL pattern match the actual request?
  3. Is middleware blocking the request?
  4. Is the controller method being called?
  5. Does the controller render an existing view?
  6. Does the module/repository throw an exception?