Controllers
Introduction
Instead of defining all of your request handling logic as closures in route files, you may wish to organize this behavior using "controller" classes. Controllers can group related request handling logic into a single class. For example, a UserController class might handle all incoming requests related to users, including showing, creating, updating, and deleting users.
By default, controllers are stored in the app/Http/Controllers directory.
Writing Controllers
Basic Controllers
To quickly generate a new controller, you may run the make:controller console command:
php console make:controller UserController
Controllers extend the App\Http\Controllers\BaseController class included with Lacelium:
<?php
declare(strict_types=1);
namespace App\Http\Controllers;
use App\Models\User;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
class UserController extends BaseController
{
/**
* Show the profile for a given user.
*/
public function show(Request $request, int $id): Response
{
$user = User::findOrFail($id);
return $this->render('users/profile.twig', [
'user' => $user,
]);
}
}
Once you have written a controller class and method, you may define a route to the controller method in config/routes.php:
use App\Http\Controllers\UserController;
$routes->controller(UserController::class, function ($routes) {
$routes->get('/users/{id}', 'show', 'users.show');
});
Dependency Injection & Controllers
Constructor Injection
The Lacelium service container is used to resolve all controllers. As a result, you are able to type-hint any dependencies your controller may need in its constructor. The declared dependencies will automatically be resolved and injected into the controller instance:
namespace App\Http\Controllers;
use App\Services\UserRepository;
use Psr\Log\LoggerInterface;
class UserController extends BaseController
{
public function __construct(
private UserRepository $users,
private LoggerInterface $logger
) {}
public function index(): Response
{
$allUsers = $this->users->allActive();
return $this->render('users/index.twig', [
'users' => $allUsers,
]);
}
}
Method Parameter Injection
In addition to constructor injection, you may also type-hint dependencies on your controller's action methods. For example, type-hinting Symfony\Component\HttpFoundation\Request will automatically pass the current HTTP request instance into your method:
public function store(Request $request): Response
{
$name = $request->request->get('name');
$email = $request->request->get('email');
// Process storage...
return $this->json(['status' => 'success']);
}
Returning Responses
All controller actions should return a Symfony Response instance. BaseController provides several convenience helper methods:
Rendering Views
To render a Twig template and return an HTML response with an optional HTTP status code:
return $this->render('docs/index.twig', [
'title' => 'Documentation Hub',
], 200);
JSON Responses
To return a structured JSON response (automatically setting the Content-Type: application/json header):
return $this->json([
'success' => true,
'data' => $tasks,
], 200);
Redirects
To redirect the user to another URI or route:
use Symfony\Component\HttpFoundation\RedirectResponse;
return new RedirectResponse('/dashboard');
Controller Composition
If you need to invoke an action on another controller without executing an external HTTP redirect, you can use the callController method on BaseController:
return $this->callController(AnalyticsController::class, 'summary', $request);
Next Steps
- Learn how to design layouts in Views & Templating.
- Map incoming URLs in Routing.
- Filter requests using Middleware.