Skip to content
  • About
    • What is Symfony?
    • Community
    • News
    • Contributing
    • Support
  • Documentation
    • Symfony Docs
    • Symfony Book
    • Screencasts
    • Symfony Bundles
    • Symfony Cloud
    • Training
  • Services
    • SensioLabs Professional services to help you with Symfony
    • Platform.sh for Symfony Best platform to deploy Symfony apps
    • SymfonyInsight Automatic quality checks for your apps
    • Symfony Certification Prove your knowledge and boost your career
    • Blackfire Profile and monitor performance of your apps
  • Other
  • Blog
  • Download
sponsored by SensioLabs
  1. Home
  2. Documentation
  3. Controller
  • Documentation
  • Book
  • Reference
  • Bundles
  • Cloud

Table of Contents

  • A Simple Controller
    • Mapping a URL to a Controller
  • The Base Controller Classes & Services
    • Generating URLs
    • Redirecting
    • Rendering Templates
    • Fetching Services as Controller Arguments
    • Accessing the Container Directly
  • Managing Errors and 404 Pages
  • The Request object as a Controller Argument
  • Managing the Session
    • Flash Messages
  • The Request and Response Object
    • JSON Helper
    • File helper
  • Final Thoughts
  • Keep Going!
  • Learn more about Controllers

Controller

Edit this page

Warning: You are browsing the documentation for Symfony 3.4, which is no longer maintained.

Read the updated version of this page for Symfony 6.2 (the current stable version).

Controller

A controller is a PHP function you create that reads information from the Symfony's Request 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 you can dream up. The controller executes whatever arbitrary logic your application needs to render the content of a page.

See how simple this is by looking at a Symfony controller in action. This renders a page that prints a lucky (random) number:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// src/AppBundle/Controller/LuckyController.php
namespace AppBundle\Controller;

use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;

class LuckyController
{
    /**
     * @Route("/lucky/number")
     */
    public function numberAction()
    {
        $number = random_int(0, 100);

        return new Response(
            '<html><body>Lucky number: '.$number.'</body></html>'
        );
    }
}

But in the real world, your controller will probably do a lot of work in order to create the response. It might read information from the request, load a database resource, send an email or set information on the user's session. But in all cases, the controller will eventually return the Response object that will be delivered back to the client.

Tip

If you haven't already created your first working page, check out Create your First Page in Symfony and then come back!

A Simple Controller

While a controller can be any PHP callable (a function, method on an object, or a Closure), a controller is usually a method inside a controller class:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// src/AppBundle/Controller/LuckyController.php
namespace AppBundle\Controller;

use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;

class LuckyController
{
    /**
     * @Route("/lucky/number/{max}")
     */
    public function numberAction($max)
    {
        $number = random_int(0, $max);

        return new Response(
            '<html><body>Lucky number: '.$number.'</body></html>'
        );
    }
}

The controller is the numberAction() method, which lives inside a controller class LuckyController.

This controller is pretty straightforward:

  • 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 use keyword imports the Response class, which the controller must return.
  • line 7: The class can technically be called anything - but should end in the word Controller (this isn't required, but some shortcuts rely on this).
  • line 12: Each action method in a controller class is suffixed with Action (again, this isn't required, but some shortcuts rely on this). This method is allowed to have a $max argument thanks to the {max} wildcard in the route.
  • line 16: The controller creates and returns a Response object.

Mapping a URL to a Controller

In order to view the result of this controller, you need to map a URL to it via a route. This was done above with the @Route("/lucky/number/{max}") annotation.

To see your page, go to this URL in your browser:

http://localhost:8000/lucky/number/100

For more information on routing, see Routing.

The Base Controller Classes & Services

For convenience, Symfony comes with two optional base Controller and AbstractController classes. You can extend either to get access to a number of helper methods.

Add the use statement atop the Controller class and then modify LuckyController to extend it:

1
2
3
4
5
6
7
8
9
// src/AppBundle/Controller/LuckyController.php
namespace AppBundle\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\Controller;

class LuckyController extends Controller
{
    // ...
}

That's it! You now have access to methods like $this->render() and many others that you'll learn about next.

Tip

You can extend either Controller or AbstractController. The difference is that when you extend AbstractController, you can't access your services via $this->get() or $this->container->get(), only to a set of common Symfony services. This forces you to write more robust code to access services.

Moreover, in Symfony 4.2 Controller was deprecated in favor of AbstractController, so using the latter will make your applications future-proof.

3.3

The AbstractController class was introduced in Symfony 3.3.

Generating URLs

The generateUrl() method is just a helper method that generates the URL for a given route:

1
$url = $this->generateUrl('blog_show', ['slug' => 'slug-value']);

Redirecting

If you want to redirect the user to another page, use the redirectToRoute() and redirect() methods:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public function indexAction()
{
    // redirects to the "homepage" route
    return $this->redirectToRoute('homepage');

    // does a permanent - 301 redirect
    return $this->redirectToRoute('homepage', [], 301);

    // redirects to a route with parameters
    return $this->redirectToRoute('blog_show', ['slug' => 'my-page']);

    // redirects to a route and maintains the original query string parameters
    return $this->redirectToRoute('blog_show', $request->query->all());

    // redirects externally
    return $this->redirect('http://symfony.com/doc');
}

For more information, see the Routing article.

Caution

The redirect() method does not check its destination in any way. If you redirect to some URL provided by the end-users, your application may be open to the unvalidated redirects security vulnerability.

Tip

The redirectToRoute() method is simply a shortcut that creates a Response object that specializes in redirecting the user. It's equivalent to:

1
2
3
4
5
6
use Symfony\Component\HttpFoundation\RedirectResponse;

public function indexAction()
{
    return new RedirectResponse($this->generateUrl('homepage'));
}

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 Response object for you:

1
2
// renders app/Resources/views/lucky/number.html.twig
return $this->render('lucky/number.html.twig', ['number' => $number]);

Templates can also live in deeper sub-directories. Just try to avoid creating unnecessarily deep structures:

1
2
3
4
// renders app/Resources/views/lottery/lucky/number.html.twig
return $this->render('lottery/lucky/number.html.twig', [
    'number' => $number,
]);

The Symfony templating system and Twig are explained more in the Creating and Using Templates article.

Fetching Services as Controller Arguments

3.3

The ability to type-hint a controller argument in order to receive a service was introduced in Symfony 3.3.

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, just type-hint an argument with its class (or interface) name. Symfony will automatically pass you the service you need:

1
2
3
4
5
6
7
8
9
10
11
use Psr\Log\LoggerInterface;
// ...

/**
 * @Route("/lucky/number/{max}")
 */
public function numberAction($max, LoggerInterface $logger)
{
    $logger->info('We are logging!');
    // ...
}

Awesome!

What other services can you type-hint? To see them, use the debug:autowiring console command:

1
$ php bin/console debug:autowiring

If you need control over the exact value of an argument, you can bind the argument by its name:

1
2
3
4
5
6
7
8
9
10
# app/config/services.yml
services:
    # ...

    # explicitly configure the service
    AppBundle\Controller\LuckyController:
        tags: [controller.service_arguments]
        bind:
            # for any $logger argument, pass this specific service
            $logger: '@monolog.logger.doctrine'
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<!-- app/config/services.xml -->
<?xml version="1.0" encoding="UTF-8" ?>
<container xmlns="http://symfony.com/schema/dic/services"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://symfony.com/schema/dic/services
        https://symfony.com/schema/dic/services/services-1.0.xsd">

    <services>
        <!-- ... -->

        <!-- Explicitly configure the service -->
        <service id="AppBundle\Controller\LuckyController">
            <tag name="controller.service_arguments"/>
            <bind key="$logger"
                type="service"
                id="monolog.logger.doctrine"
            />
        </service>
    </services>
</container>
1
2
3
4
5
6
7
8
9
10
// app/config/services.php
use AppBundle\Controller\LuckyController;
use Symfony\Component\DependencyInjection\Reference;

$container->register(LuckyController::class)
    ->addTag('controller.service_arguments')
    ->setBindings([
        '$logger' => new Reference('monolog.logger.doctrine'),
    ])
;

You can also use normal constructor injection in your controllers.

Caution

You can only pass services to your controller arguments in this way. It's not possible, for example, to pass a config parameter as a controller argument, even by using bind. If you need a parameter, use the $this->getParameter('kernel.debug') shortcut or pass the value through your controller's __construct() method and specify its value with bind.

For more information about services, see the Service Container article.

Note

If this isn't working, make sure your controller is registered as a service, is autoconfigured and extends either Controller or AbstractController. If you use the services.yml configuration from the Symfony Standard Edition, then your controllers are already registered as services and autoconfigured.

If you're not using the default configuration, you can tag your service manually with controller.service_arguments.

Accessing the Container Directly

If you extend the base Controller class, you can access any Symfony service via the get() method. Here are several common services you might need:

1
2
3
4
5
6
7
8
$templating = $this->get('templating');

$router = $this->get('router');

$mailer = $this->get('mailer');

// you can also fetch parameters
$someParameter = $this->getParameter('some_parameter');

If you receive an error like:

1
You have requested a non-existent service "my_service_id"

Check to make sure the service exists (use debug:container) and that it's public.

Managing Errors and 404 Pages

When things are not found, you should play well with the HTTP protocol and return a 404 response. To do this, you'll throw a special type of exception. If you're extending the base Controller or the base AbstractController class, do the following:

1
2
3
4
5
6
7
8
9
10
public function indexAction()
{
    // retrieve the object from database
    $product = ...;
    if (!$product) {
        throw $this->createNotFoundException('The product does not exist');
    }

    return $this->render(...);
}

The createNotFoundException() method is just a shortcut to create a special NotFoundHttpException object, which ultimately triggers a 404 HTTP response inside Symfony.

If you throw an exception that extends or is an instance of HttpException, Symfony will use the appropriate HTTP status code. Otherwise, the response will have a 500 HTTP status code:

1
2
// this exception ultimately generates a 500 status error
throw new \Exception('Something went wrong!');

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 using the app_dev.php front controller - see Configuring Symfony (and Environments)).

You'll want to customize the error page your user sees. To do that, see the How 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? All of that information is stored in Symfony's Request object. To get it in your controller, just add it as an argument and type-hint it with the Request class:

1
2
3
4
5
6
7
8
use Symfony\Component\HttpFoundation\Request;

public function indexAction(Request $request, $firstName, $lastName)
{
    $page = $request->query->get('page', 1);

    // ...
}

Keep reading for more information about using the Request object.

Managing the Session

Symfony provides a nice session object that you can use to store information about the user between requests. By default, Symfony stores the token in a cookie and writes the attributes to a file by using native PHP sessions.

3.3

The ability to request a Session instance in controllers was introduced in Symfony 3.3.

To retrieve the session, add the SessionInterface type-hint to your argument and Symfony will provide you with a session:

1
2
3
4
5
6
7
8
9
10
11
12
13
use Symfony\Component\HttpFoundation\Session\SessionInterface;

public function indexAction(SessionInterface $session)
{
    // stores an attribute for reuse during a later user request
    $session->set('foo', 'bar');

    // gets the attribute set by another controller in another request
    $foobar = $session->get('foobar');

    // uses a default value if the attribute doesn't exist
    $filters = $session->get('filters', []);
}

Stored attributes remain in the session for the remainder of that user's session.

Tip

Every SessionInterface implementation is supported. If you have your own implementation, type-hint this in the arguments instead.

Flash Messages

You can also 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:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
use Symfony\Component\HttpFoundation\Request;

public function updateAction(Request $request)
{
    // ...

    if ($form->isSubmitted() && $form->isValid()) {
        // do some sort of processing

        $this->addFlash(
            'notice',
            'Your changes were saved!'
        );
        // $this->addFlash() is equivalent to $request->getSession()->getFlashBag()->add()

        return $this->redirectToRoute(...);
    }

    return $this->render(...);
}

After processing the request, the controller sets a flash message in the session and then redirects. The message key (notice in this example) can be anything: you'll use this key to retrieve the message.

In the template of the next page (or even better, in your base layout template), read any flash messages from the session using app.flashes():

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
{# app/Resources/views/base.html.twig #}

{# read and display just one flash message type #}
{% for message in app.flashes('notice') %}
    <div class="flash-notice">
        {{ message }}
    </div>
{% endfor %}

{# read and display several types of flash messages #}
{% for label, messages in app.flashes(['success', 'warning']) %}
    {% for message in messages %}
        <div class="flash-{{ label }}">
            {{ message }}
        </div>
    {% endfor %}
{% endfor %}

{# read and display all flash messages #}
{% for label, messages in app.flashes %}
    {% for message in messages %}
        <div class="flash-{{ label }}">
            {{ message }}
        </div>
    {% endfor %}
{% endfor %}

3.3

The app.flashes() Twig function was introduced in Symfony 3.3. Prior, you had to use app.session.flashBag().

Note

It's common to use notice, warning and error as the keys of the different types of flash messages, but you can use any key that fits your needs.

Tip

You can use the peek() method instead to retrieve the message while keeping it in the bag.

The Request and Response Object

As mentioned earlier, the framework will pass the Request object to any controller argument that is type-hinted with the Request class:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
use Symfony\Component\HttpFoundation\Request;

public function indexAction(Request $request)
{
    $request->isXmlHttpRequest(); // is it an Ajax request?

    $request->getPreferredLanguage(['en', 'fr']);

    // retrieves GET and POST variables respectively
    $request->query->get('page');
    $request->request->get('page');

    // retrieves SERVER variables
    $request->server->get('HTTP_HOST');

    // retrieves an instance of UploadedFile identified by foo
    $request->files->get('foo');

    // retrieves a COOKIE value
    $request->cookies->get('PHPSESSID');

    // retrieves an HTTP request header, with normalized, lowercase keys
    $request->headers->get('host');
    $request->headers->get('content-type');
}

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 also a public headers property. This is a ResponseHeaderBag that has some nice methods for getting and setting response headers. The header names are normalized so that using Content-Type is equivalent to content-type or even content_type.

The only requirement for a controller is to return a Response object. The Response class is an abstraction around the HTTP response - the text-based message filled with headers and content that's sent back to the client:

1
2
3
4
5
6
7
8
use Symfony\Component\HttpFoundation\Response;

// creates a simple Response with a 200 status code (the default)
$response = new Response('Hello '.$name, Response::HTTP_OK);

// creates a CSS-response with a 200 status code
$response = new Response('<style> ... </style>');
$response->headers->set('Content-Type', 'text/css');

There are special classes that make certain kinds of responses easier:

  • For files, there is BinaryFileResponse. See The HttpFoundation Component.
  • For streamed responses, there is StreamedResponse. See The HttpFoundation Component.

See also

Now that you know the basics you can continue your research on Symfony Request and Response object in the HttpFoundation component documentation.

JSON Helper

To return JSON from a controller, use the json() helper method on the base controller. This returns a special JsonResponse object that encodes the data automatically:

1
2
3
4
5
6
7
8
9
// ...
public function indexAction()
{
    // returns '{"username":"jane.doe"}' and sets the proper Content-Type header
    return $this->json(['username' => 'jane.doe']);

    // the shortcut defines three optional arguments
    // return $this->json($data, $status = 200, $headers = [], $context = []);
}

If the serializer service is enabled in your application, contents passed to json() are encoded with it. Otherwise, the json_encode function is used.

File helper

3.2

The file() helper was introduced in Symfony 3.2.

You can use the file() helper to serve a file from inside a controller:

1
2
3
4
5
public function fileAction()
{
    // send the file contents and force the browser to download it
    return $this->file('/path/to/some_file.pdf');
}

The file() helper provides some arguments to configure its behavior:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
use Symfony\Component\HttpFoundation\File\File;
use Symfony\Component\HttpFoundation\ResponseHeaderBag;

public function fileAction()
{
    // load the file from the filesystem
    $file = new File('/path/to/some_file.pdf');

    return $this->file($file);

    // rename the downloaded file
    return $this->file($file, 'custom_name.pdf');

    // display the file contents in the browser instead of downloading it
    return $this->file('invoice_3241.pdf', 'my_invoice.pdf', ResponseHeaderBag::DISPOSITION_INLINE);
}

Final Thoughts

Whenever you create a page, you'll ultimately need to write some code that contains the logic for that page. In Symfony, this is called a controller, and it's a PHP function where you can do anything in order to return the final Response object that will be returned to the user.

To make life easier, you'll probably extend the base Controller class because this gives access to shortcut methods (like render() and redirectToRoute()).

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.

Keep Going!

Next, learn all about rendering templates with Twig.

Learn more about Controllers

  • Extending Action Argument Resolving
  • How to Manually Validate a CSRF Token in a Controller
  • How to Customize Error Pages
  • How to Forward Requests to another Controller
  • How to Define Controllers as Services
  • How to Create a SOAP Web Service in a Symfony Controller
  • How to Upload Files
This work, including the code samples, is licensed under a Creative Commons BY-SA 3.0 license.
TOC
    Version
    We stand with Ukraine.
    Version:
    Make sure your project is risk free

    Make sure your project is risk free

    Measure & Improve Symfony Code Performance

    Measure & Improve Symfony Code Performance

    Symfony footer

    ↓ Our footer now uses the colors of the Ukrainian flag because Symfony stands with the people of Ukraine.

    Avatar of Damián Nohales, a Symfony contributor

    Thanks Damián Nohales (@eagleoneraptor) for being a Symfony contributor

    1 commit • 2 lines changed

    View all contributors that help us make Symfony

    Become a Symfony contributor

    Be an active part of the community and contribute ideas, code and bug fixes. Both experts and newcomers are welcome.

    Learn how to contribute

    Symfony™ is a trademark of Symfony SAS. All rights reserved.

    • What is Symfony?

      • Symfony at a Glance
      • Symfony Components
      • Case Studies
      • Symfony Releases
      • Security Policy
      • Logo & Screenshots
      • Trademark & Licenses
      • symfony1 Legacy
    • Learn Symfony

      • Symfony Docs
      • Symfony Book
      • Reference
      • Bundles
      • Best Practices
      • Training
      • eLearning Platform
      • Certification
    • Screencasts

      • Learn Symfony
      • Learn PHP
      • Learn JavaScript
      • Learn Drupal
      • Learn RESTful APIs
    • Community

      • SymfonyConnect
      • Support
      • How to be Involved
      • Code of Conduct
      • Events & Meetups
      • Projects using Symfony
      • Downloads Stats
      • Contributors
      • Backers
    • Blog

      • Events & Meetups
      • A week of symfony
      • Case studies
      • Cloud
      • Community
      • Conferences
      • Diversity
      • Documentation
      • Living on the edge
      • Releases
      • Security Advisories
      • SymfonyInsight
      • Twig
      • SensioLabs
    • Services

      • SensioLabs services
      • Train developers
      • Manage your project quality
      • Improve your project performance
      • Host Symfony projects

      Deployed on

    Follow Symfony