Controller (Symfony Docs) (original) (raw)
A controller is a PHP function you create that reads information from theRequest object and creates and returns a Response object. The response could be an HTML page, JSON, XML, a file download, a redirect, a 404 error or anything else. The controller runs whatever arbitrary logic your application needs to render the content of a page.
A Basic Controller
While a controller can be any PHP callable (function, method on an object, or a Closure), a controller is usually a method inside a controller class:
The controller is the number() method, which lives inside the controller class LuckyController.
This controller is quite simple:
- line 2: Symfony takes advantage of PHP's namespace functionality to namespace the entire controller class.
- line 4: Symfony again takes advantage of PHP's namespace functionality: the
usekeyword imports theResponseclass, which the controller must return. - line 7: The class can technically be called anything, but it's suffixed with
Controllerby convention. - line 10: The action method is allowed to have a
$maxargument thanks to the{max}wildcard in the route. - line 14: The controller creates and returns a
Responseobject.
The Base Controller Class & Services
To aid development, Symfony comes with an optional base controller class calledAbstractController. It can be extended to gain access to helper methods.
Add the use statement atop your controller class and then modifyLuckyController to extend it:
That's it! You now have access to methods like $this->render()and many others that you'll learn about next.
Generating URLs
The generateUrl()method is just a helper method that generates the URL for a given route:
Redirecting
If you want to redirect the user to another page, use the redirectToRoute()and redirect() methods:
Rendering Templates
If you're serving HTML, you'll want to render a template. The render()method renders a template and puts that content into a Responseobject for you:
Templating and Twig are explained more in theCreating and Using Templates article.
Fetching Services
Symfony comes packed with a lot of useful classes and functionalities, called services. These are used for rendering templates, sending emails, querying the database and any other "work" you can think of.
If you need a service in a controller, type-hint an argument with its class (or interface) name and Symfony will inject it automatically. This requires your controller to be registered as a service:
Awesome!
What other services can you type-hint? To see them, use the debug:autowiring console command:
Like with all services, you can also use regularconstructor injection in your controllers.
For more information about services, see the Service Container article.
Generating Controllers
To save time, you can install Symfony Maker and tell Symfony to generate a new controller class:
If you want to generate an entire CRUD from a Doctrine entity, use:
Managing Errors and 404 Pages
When things are not found, you should return a 404 response. To do this, throw a special type of exception:
The createNotFoundException()method is just a shortcut to create a specialNotFoundHttpExceptionobject, which ultimately triggers a 404 HTTP response inside Symfony.
If you throw an exception that extends or is an instance ofHttpException, Symfony will use the appropriate HTTP status code. Otherwise, the response will have a 500 HTTP status code:
In every case, an error page is shown to the end user and a full debug error page is shown to the developer (i.e. when you're in "Debug" mode - seeConfiguring Symfony).
To customize the error page that's shown to the user, see theHow to Customize Error Pages article.
The Request object as a Controller Argument
What if you need to read query parameters, grab a request header or get access to an uploaded file? That information is stored in Symfony's Requestobject. To access it in your controller, add it as an argument andtype-hint it with the Request class:
Keep reading for more information about using the Request object.
Automatic Mapping Of The Request
It is possible to automatically map request's payload and/or query parameters to your controller's action arguments with attributes.
Mapping Query Parameters Individually
Let's say a user sends you a request with the following query string:https://example.com/dashboard?firstName=John&lastName=Smith&age=27. Thanks to the MapQueryParameterattribute, arguments of your controller's action can be automatically fulfilled:
The MapQueryParameter attribute supports the following argument types:
\BackedEnumarrayboolfloatintstring- Objects that extend AbstractUid
#[MapQueryParameter] can take an optional argument called filter. You can use theValidate Filters constants defined in PHP:
Mapping The Whole Query String
Another possibility is to map the entire query string into an object that will hold available query parameters. Let's say you declare the following DTO with its optional validation constraints:
You can then use the MapQueryStringattribute in your controller:
You can customize the validation groups used during the mapping and also the HTTP status to return if the validation fails:
The default status code returned if the validation fails is 404.
If you want to map your object to a nested array in your query using a specific key, set the key option in the #[MapQueryString] attribute:
If you need a valid DTO even when the request query string is empty, set a default value for your controller arguments:
Mapping Request Payload
When creating an API and dealing with other HTTP methods than GET (likePOST or PUT), user's data are not stored in the query string but directly in the request payload, like this:
In this case, it is also possible to directly map this payload to your DTO by using the MapRequestPayloadattribute:
This attribute allows you to customize the serialization context as well as the class responsible of doing the mapping between the request and your DTO:
You can also customize the validation groups used, the status code to return if the validation fails as well as supported payload formats:
The default status code returned if the validation fails is 422.
Tip
If you build a JSON API, make sure to declare your route as using the JSONformat. This will make the error handling output a JSON response in case of validation errors, rather than an HTML page:
Make sure to install phpstan/phpdoc-parser and phpdocumentor/type-resolverif you want to map a nested array of specific DTOs:
Instead of returning an array of DTO objects, you can tell Symfony to transform each DTO object into an array and return something like this:
To do so, map the parameter as an array and configure the type of each element using the type option of the attribute:
Mapping Uploaded Files
Symfony provides an attribute called #[MapUploadedFile] to map one or moreUploadedFile objects to controller arguments:
In this example, the associated argument resolverfetches the UploadedFile based on the argument name ($picture). If no file is submitted, an HttpException is thrown. You can change this by making the controller argument nullable:
The #[MapUploadedFile] attribute also allows to pass a list of constraints to apply to the uploaded file:
The validation constraints are checked before injecting the UploadedFile into the controller argument. If there's a constraint violation, an HttpExceptionis thrown and the controller's action is not executed.
If you need to upload a collection of files, map them to an array or a variadic argument. The given constraint will be applied to all files and if any of them fails, an HttpException is thrown:
Use the name option to rename the uploaded file to a custom value:
In addition, you can change the status code of the HTTP exception thrown when there are constraint violations:
Managing the Session
You can store special messages, called "flash" messages, on the user's session. By design, flash messages are meant to be used exactly once: they vanish from the session automatically as soon as you retrieve them. This feature makes "flash" messages particularly great for storing user notifications.
For example, imagine you're processing a form submission:
Reading for more information about using Sessions.
The Request and Response Object
As mentioned earlier, Symfony will pass the Request object to any controller argument that is type-hinted with the Request class:
The Request class has several public properties and methods that return any information you need about the request.
Like the Request, the Response object has a public headers property. This object is of the type ResponseHeaderBagand provides methods for getting and setting response headers. The header names are normalized. As a result, the name Content-Type is equivalent to the name content-type or content_type.
In Symfony, a controller is required to return a Response object:
To facilitate this, different response objects are included to address different response types. Some of these are mentioned below. To learn more about theRequest and Response (and different Response classes), see theHttpFoundation component documentation.
Note
Technically, a controller can return a value other than a Response. However, your application is responsible for transforming that value into aResponse object. This is handled using events(specifically the kernel.view event), an advanced feature you'll learn about later.
Returning JSON Response
To return JSON from a controller, use the json() helper method. This returns aJsonResponse object that encodes the data automatically:
If the serializer service is enabled in your application, it will be used to serialize the data to JSON. Otherwise, the json_encode function is used.
Streaming File Responses
You can use the file()helper to serve a file from inside a controller:
The file() helper provides some arguments to configure its behavior:
Sending Early Hints
Early hints tell the browser to start downloading some assets even before the application sends the response content. This improves perceived performance because the browser can prefetch resources that will be needed once the full response is finally sent. These resources are commonly Javascript or CSS files, but they can be any type of resource.
Note
In order to work, the SAPI you're using must support this feature, likeFrankenPHP.
You can send early hints from your controller action thanks to thesendEarlyHints()method:
Technically, Early Hints are an informational HTTP response with the status code103. The sendEarlyHints() method creates a Response object with that status code and sends its headers immediately.
This way, browsers can start downloading the assets immediately; like thestyle.css and script.js files in the above example. ThesendEarlyHints() method also returns the Response object, which you must use to create the full response sent from the controller action.
Decoupling Controllers from Symfony
Extending the AbstractController base classsimplifies controller development and is recommended for most applications. However, some advanced users prefer to fully decouple your controllers from Symfony (for example, to improve testability or to follow a more framework-agnostic design) Symfony provides tools to help you do that.
To decouple controllers, Symfony exposes all the helpers from AbstractControllerthrough another class called ControllerHelper, where each helper is available as a public method:
You can inject the entire ControllerHelper class if you prefer, but using theAutowireMethodOf attribute as in the previous example, lets you inject only the exact helpers you need, making your code more efficient.
Since #[AutowireMethodOf] also works with interfaces, you can define interfaces for these helper methods:
Then, update your controller to use the interface instead of a closure:
Using interfaces like in the previous example provides full static analysis and autocompletion benefits with no extra boilerplate code.
Final Thoughts
In Symfony, a controller is usually a class method which is used to accept requests, and return a Response object. When mapped with a URL, a controller becomes accessible and its response can be viewed.
To facilitate the development of controllers, Symfony provides anAbstractController. It can be used to extend the controller class allowing access to some frequently used utilities such as render() andredirectToRoute(). The AbstractController also provides thecreateNotFoundException() utility which is used to return a page not found response.
In other articles, you'll learn how to use specific services from inside your controller that will help you persist and fetch objects from a database, process form submissions, handle caching and more.
This work, including the code samples, is licensed under aCreative Commons BY-SA 3.0 license.