Skip to content

Full Example

This example demonstrates a complete routing setup for a simple e-commerce application.

project/
├── Controllers/
│ ├── HomeController.php
│ ├── AuthController.php
│ ├── ProductController.php
│ └── OrderController.php
├── Middleware/
│ ├── AuthMiddleware.php
│ ├── GuestMiddleware.php
│ └── CsrfMiddleware.php
├── routes/
│ └── web.php
└── index.php
<?php
namespace Middleware;
class AuthMiddleware
{
public function handle(callable $next)
{
if (!isset($_SESSION['user_id'])) {
$_SESSION['redirect_after_login'] = $_SERVER['REQUEST_URI'];
header('Location: /login');
exit;
}
$next();
}
}
<?php
namespace Middleware;
class GuestMiddleware
{
public function handle(callable $next)
{
if (isset($_SESSION['user_id'])) {
header('Location: /dashboard');
exit;
}
$next();
}
}
<?php
namespace Middleware;
class CsrfMiddleware
{
public function handle(callable $next)
{
$token = $_POST['csrf_token'] ?? '';
$sessionToken = $_SESSION['csrf_token'] ?? '';
if (!hash_equals($sessionToken, $token)) {
http_response_code(403);
echo 'CSRF token mismatch';
exit;
}
$next();
}
}
<?php
namespace Controllers;
class HomeController
{
public function index()
{
$products = $this->getFeaturedProducts();
require __DIR__ . '/../views/home.php';
}
public function about()
{
require __DIR__ . '/../views/about.php';
}
private function getFeaturedProducts()
{
// Database query here
return [];
}
}
<?php
namespace Controllers;
class ProductController
{
public function index()
{
$products = $this->getAllProducts();
require __DIR__ . '/../views/products/index.php';
}
public function show(string $id)
{
$product = $this->findProduct($id);
if (!$product) {
http_response_code(404);
require __DIR__ . '/../views/errors/404.php';
return;
}
require __DIR__ . '/../views/products/show.php';
}
private function getAllProducts()
{
// Database query
return [];
}
private function findProduct(string $id)
{
// Database query
return null;
}
}
<?php
namespace Controllers;
class OrderController
{
public function index()
{
$userId = $_SESSION['user_id'];
$orders = $this->getUserOrders($userId);
require __DIR__ . '/../views/orders/index.php';
}
public function show(string $id)
{
$order = $this->findOrder($id);
// Verify ownership
if (!$order || $order['user_id'] !== $_SESSION['user_id']) {
http_response_code(403);
echo 'Access denied';
return;
}
require __DIR__ . '/../views/orders/show.php';
}
public function checkout()
{
$cart = $_SESSION['cart'] ?? [];
require __DIR__ . '/../views/checkout.php';
}
public function processCheckout()
{
// Validate payment
$orderId = $this->createOrder($_POST);
// Redirect to confirmation
header("Location: /orders/{$orderId}/confirmation");
exit;
}
public function confirmation(string $id)
{
$order = $this->findOrder($id);
require __DIR__ . '/../views/orders/confirmation.php';
}
private function getUserOrders(int $userId)
{
// Database query
return [];
}
private function findOrder(string $id)
{
// Database query
return null;
}
private function createOrder(array $data)
{
// Create order in database
return uniqid('order_');
}
}
<?php
use Krag\Sroute\Router;
use Controllers\HomeController;
use Controllers\AuthController;
use Controllers\ProductController;
use Controllers\OrderController;
$router = new Router();
// Register middleware aliases
$router->aliasMiddleware('auth', \Middleware\AuthMiddleware::class);
$router->aliasMiddleware('guest', \Middleware\GuestMiddleware::class);
$router->aliasMiddleware('csrf', \Middleware\CsrfMiddleware::class);
// ============================================
// Public Routes
// ============================================
$router->get('/', HomeController::class, 'index')
->name('home');
$router->get('/about', HomeController::class, 'about')
->name('about');
// Products
$router->get('/products', ProductController::class, 'index')
->name('products.index');
$router->get('/products/{id}', ProductController::class, 'show')
->name('products.show');
// ============================================
// Guest Routes (Unauthenticated Only)
// ============================================
$router->get('/login', AuthController::class, 'showLogin')
->middleware(['guest'])
->name('auth.login.get');
$router->post('/login', AuthController::class, 'login')
->middleware(['csrf', 'guest'])
->name('auth.login.post');
$router->get('/register', AuthController::class, 'showRegister')
->middleware(['guest'])
->name('auth.register.get');
$router->post('/register', AuthController::class, 'register')
->middleware(['csrf', 'guest'])
->name('auth.register.post');
// ============================================
// Authenticated Routes
// ============================================
$router->get('/logout', AuthController::class, 'logout')
->middleware(['auth'])
->name('auth.logout');
// Orders
$router->get('/orders', OrderController::class, 'index')
->middleware(['auth'])
->name('orders.index');
$router->get('/orders/{id}', OrderController::class, 'show')
->middleware(['auth'])
->name('orders.show');
$router->get('/checkout', OrderController::class, 'checkout')
->middleware(['auth'])
->name('checkout.get');
$router->post('/checkout', OrderController::class, 'processCheckout')
->middleware(['csrf', 'auth'])
->name('checkout.post');
$router->get('/orders/{id}/confirmation', OrderController::class, 'confirmation')
->middleware(['auth'])
->name('orders.confirmation');
// Dispatch the router
$router->load();

Remember that middleware executes in LIFO (Last-In-First-Out) order:

$router->post('/checkout', OrderController::class, 'processCheckout')
->middleware(['csrf', 'auth']);

Execution flow:

  1. AuthMiddleware runs first (last in array)
  2. CsrfMiddleware runs second (first in array)
  3. OrderController::processCheckout() runs last

This ensures the user is authenticated before CSRF validation occurs.

  1. Organize by feature: Group related routes together
  2. Use middleware wisely: Apply authentication and CSRF protection where needed
  3. Name your routes: Makes URL generation easier and more maintainable
  4. Keep controllers thin: Delegate business logic to service classes
  5. Understand LIFO: Middleware order matters for proper request filtering