Skip to content

Controllers

Introduction

Controllers organize request handling logic into classes.

Creating Controllers

Extend the base Controller class:

php
<?php

namespace App\Controllers;

use Lighthouse\Controller;
use Psr\Http\Message\ResponseInterface;

class UserController extends Controller
{
    public function index(): ResponseInterface
    {
        $users = [
            ['id' => 1, 'name' => 'John'],
            ['id' => 2, 'name' => 'Jane'],
        ];

        return $this->view('users.index', ['users' => $users]);
    }

    public function show(string $id): ResponseInterface
    {
        return $this->json(['id' => $id, 'name' => 'John']);
    }
}

Registering Routes

Point routes to controller methods:

php
use App\Controllers\UserController;

$app->get('/users', [UserController::class, 'index']);
$app->get('/users/{id}', [UserController::class, 'show']);
$app->post('/users', [UserController::class, 'store']);

Response Methods

The base controller provides convenient response methods:

View Response

php
public function index(): ResponseInterface
{
    return $this->view('users.index', ['users' => $users]);
}

JSON Response

php
public function show(string $id): ResponseInterface
{
    return $this->json(['id' => $id, 'name' => 'John']);
}

// With status code
return $this->json(['error' => 'Not found'], 404);

Redirect Response

php
public function store(): ResponseInterface
{
    // Create user...

    return $this->redirect('/users');
}

// Redirect to named route
return $this->redirectToRoute('users.show', ['id' => $user->id]);

Text Response

php
return $this->text('Plain text content');

HTML Response

php
return $this->html('<h1>Hello</h1>');

Empty Response

php
return $this->empty(204); // No Content

Dependency Injection

Controllers are resolved through the container. Type-hint dependencies:

php
<?php

namespace App\Controllers;

use Lighthouse\Application;
use Lighthouse\Controller;
use App\Services\UserService;

class UserController extends Controller
{
    public function __construct(
        Application $app,
        private UserService $users
    ) {
        parent::__construct($app);
    }

    public function index(): ResponseInterface
    {
        $users = $this->users->all();
        return $this->view('users.index', ['users' => $users]);
    }
}

Accessing the Request

Inject ServerRequestInterface into methods:

php
use Psr\Http\Message\ServerRequestInterface;

public function store(ServerRequestInterface $request): ResponseInterface
{
    $data = $request->getParsedBody();
    
    // Create user with $data['name'], $data['email'], etc.
    
    return $this->redirect('/users');
}

Route Parameters

Route parameters are injected by name:

php
// Route: /users/{id}/posts/{postId}

public function show(string $id, string $postId): ResponseInterface
{
    return $this->json([
        'user_id' => $id,
        'post_id' => $postId,
    ]);
}

Invokable Controllers

For single-action controllers, use __invoke:

php
<?php

namespace App\Controllers;

use Lighthouse\Controller;
use Psr\Http\Message\ResponseInterface;

class ShowDashboardController extends Controller
{
    public function __invoke(): ResponseInterface
    {
        return $this->view('dashboard');
    }
}

Register without a method:

php
$app->get('/dashboard', ShowDashboardController::class);

Released under the MIT License.