Skip to content

Controllers

Controllers allow you to organize your route logic into classes instead of using closures. Sroute supports both controller classes and anonymous functions.

For simple routes, you can use closures directly:

<?php
use Krag\Sroute\Router;
$router = new Router();
$router->get('/', function() {
echo 'Welcome to the homepage!';
});
$router->get('/about', function() {
require __DIR__ . '/views/about.php';
});
$router->get('/user/{id}', function(string $id) {
echo "User ID: " . htmlspecialchars($id);
});
$router->get('/post/{slug}/comment/{commentId}', function(string $slug, string $commentId) {
echo "Post: {$slug}, Comment: {$commentId}";
});

For complex applications, organize your logic into controller classes:

<?php
namespace Controllers;
class HomeController
{
public function index()
{
require __DIR__ . '/../views/home.php';
}
public function about()
{
require __DIR__ . '/../views/about.php';
}
}
use Krag\Sroute\Router;
use Controllers\HomeController;
$router = new Router();
$router->get('/', HomeController::class, 'index');
$router->get('/about', HomeController::class, 'about');

Controllers automatically receive route parameters:

<?php
namespace Controllers;
class UserController
{
public function show(string $id)
{
// Fetch user from database
$user = $this->findUser($id);
require __DIR__ . '/../views/user/profile.php';
}
public function edit(string $id)
{
$user = $this->findUser($id);
require __DIR__ . '/../views/user/edit.php';
}
private function findUser(string $id)
{
// Database logic here
return ['id' => $id, 'name' => 'John Doe'];
}
}
$router->get('/user/{id}', UserController::class, 'show');
$router->get('/user/{id}/edit', UserController::class, 'edit');

Here’s a complete controller handling a resource:

<?php
namespace Controllers;
class ProductController
{
public function index()
{
// List all products
$products = $this->getAllProducts();
require __DIR__ . '/../views/products/index.php';
}
public function show(string $id)
{
// Show single product
$product = $this->findProduct($id);
if (!$product) {
http_response_code(404);
echo 'Product not found';
return;
}
require __DIR__ . '/../views/products/show.php';
}
public function create()
{
// Show create form
require __DIR__ . '/../views/products/create.php';
}
public function store()
{
// Validate and save new product
$name = $_POST['name'] ?? '';
$price = $_POST['price'] ?? 0;
if (empty($name)) {
$_SESSION['errors'] = ['name' => 'Name is required'];
header('Location: /products/create');
exit;
}
$this->saveProduct(['name' => $name, 'price' => $price]);
header('Location: /products');
exit;
}
private function getAllProducts()
{
// Database query
return [];
}
private function findProduct(string $id)
{
// Database query
return null;
}
private function saveProduct(array $data)
{
// Database insert
}
}
use Controllers\ProductController;
// List products
$router->get('/products', ProductController::class, 'index')
->name('products.index');
// Show create form
$router->get('/products/create', ProductController::class, 'create')
->middleware(['auth'])
->name('products.create');
// Store new product
$router->post('/products', ProductController::class, 'store')
->middleware(['auth', 'csrf'])
->name('products.store');
// Show single product
$router->get('/products/{id}', ProductController::class, 'show')
->name('products.show');

Organize controllers by feature or resource:

Controllers/
├── Auth/
│ ├── LoginController.php
│ └── RegisterController.php
├── Admin/
│ ├── DashboardController.php
│ └── UserController.php
├── HomeController.php
└── ProductController.php
  1. Single Responsibility: Each controller should handle one resource or feature
  2. Keep methods focused: Controller methods should be concise and delegate to services/repositories
  3. Use dependency injection: Pass dependencies through the constructor when needed
  4. Return early: Use early returns for validation failures
  5. Separate concerns: Move business logic to service classes, keep controllers thin