Skip to content
  • About
    • What is Symfony?
    • Community
    • News
    • Contributing
    • Support
  • Documentation
    • Symfony Docs
    • Symfony Book
    • Screencasts
    • Symfony Bundles
    • Symfony Cloud
    • Training
  • Services
    • 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
    • SensioLabs Professional services to help you with Symfony
    • Blackfire Profile and monitor performance of your apps
  • Other
  • Blog
  • Download
sponsored by
  1. Home
  2. Documentation
  3. Controller
  4. How to Define Controllers as Services

How to Define Controllers as Services

Edit this page

In Symfony, a controller does not need to be registered as a service. But if you're using the default services.yaml configuration, and your controllers extend the AbstractController class, they are automatically registered as services. This means you can use dependency injection like any other normal service.

If your controllers don't extend the AbstractController class, you must explicitly mark your controller services as public. Alternatively, you can apply the controller.service_arguments tag to your controller services. This will make the tagged services public and will allow you to inject services in method parameters:

1
2
3
4
5
6
7
# config/services.yaml

# controllers are imported separately to make sure services can be injected
# as action arguments even if you don't extend any base controller class
App\Controller\:
   resource: '../src/Controller/'
   tags: ['controller.service_arguments']

Note

If you don't use either autowiring or autoconfiguration and you extend the AbstractController, you'll need to apply other tags and make some method calls to register your controllers as services:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# config/services.yaml

# this extended configuration is only required when not using autowiring/autoconfiguration,
# which is uncommon and not recommended

abstract_controller.locator:
    class: Symfony\Component\DependencyInjection\ServiceLocator
    arguments:
        -
            router: '@router'
            request_stack: '@request_stack'
            http_kernel: '@http_kernel'
            session: '@session'
            parameter_bag: '@parameter_bag'
            # you can add more services here as you need them (e.g. the `serializer`
            # service) and have a look at the AbstractController class to see
            # which services are defined in the locator

App\Controller\:
    resource: '../src/Controller/'
    tags: ['controller.service_arguments']
    calls:
        - [setContainer, ['@abstract_controller.locator']]

If you prefer, you can use the #[AsController] PHP attribute to automatically apply the controller.service_arguments tag to your controller services:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// src/Controller/HelloController.php
namespace App\Controller;

use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Attribute\AsController;
use Symfony\Component\Routing\Attribute\Route;

#[AsController]
class HelloController
{
    #[Route('/hello', name: 'hello', methods: ['GET'])]
    public function index(): Response
    {
        // ...
    }
}

Registering your controller as a service is the first step, but you also need to update your routing config to reference the service properly, so that Symfony knows to use it.

Use the service_id::method_name syntax to refer to the controller method. If the service id is the fully-qualified class name (FQCN) of your controller, as Symfony recommends, then the syntax is the same as if the controller was not a service like: App\Controller\HelloController::index:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// src/Controller/HelloController.php
namespace App\Controller;

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

class HelloController
{
    #[Route('/hello', name: 'hello', methods: ['GET'])]
    public function index(): Response
    {
        // ...
    }
}
1
2
3
4
5
# config/routes.yaml
hello:
    path:       /hello
    controller: App\Controller\HelloController::index
    methods:    GET
1
2
3
4
5
6
7
8
9
10
<!-- config/routes.xml -->
<?xml version="1.0" encoding="UTF-8" ?>
<routes xmlns="http://symfony.com/schema/routing"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://symfony.com/schema/routing
        https://symfony.com/schema/routing/routing-1.0.xsd">

    <route id="hello" path="/hello" controller="App\Controller\HelloController::index" methods="GET"/>

</routes>
1
2
3
4
5
6
7
8
9
10
// config/routes.php
use App\Controller\HelloController;
use Symfony\Component\Routing\Loader\Configurator\RoutingConfigurator;

return function (RoutingConfigurator $routes): void {
    $routes->add('hello', '/hello')
        ->controller([HelloController::class, 'index'])
        ->methods(['GET'])
    ;
};

Invokable Controllers

Controllers can also define a single action using the __invoke() method, which is a common practice when following the ADR pattern (Action-Domain-Responder):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// src/Controller/Hello.php
namespace App\Controller;

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

#[Route('/hello/{name}', name: 'hello')]
class Hello
{
    public function __invoke(string $name = 'World'): Response
    {
        return new Response(sprintf('Hello %s!', $name));
    }
}
1
2
3
4
# config/routes.yaml
hello:
    path:       /hello/{name}
    controller: App\Controller\HelloController
1
2
3
4
5
6
7
8
9
10
11
12
<!-- config/routes.xml -->
<?xml version="1.0" encoding="UTF-8" ?>
<routes xmlns="http://symfony.com/schema/routing"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://symfony.com/schema/routing
        https://symfony.com/schema/routing/routing-1.0.xsd">

    <route id="hello" path="/hello/{name}">
        <default key="_controller">App\Controller\HelloController</default>
    </route>

</routes>
1
2
3
4
5
6
use App\Controller\HelloController;

// app/config/routing.php
$collection->add('hello', new Route('/hello', [
    '_controller' => HelloController::class,
]));

Alternatives to base Controller Methods

When using a controller defined as a service, you can still extend the AbstractController base controller and use its shortcuts. But, you don't need to! You can choose to extend nothing, and use dependency injection to access different services.

The base Controller class source code is a great way to see how to accomplish common tasks. For example, $this->render() is usually used to render a Twig template and return a Response. But, you can also do this directly:

In a controller that's defined as a service, you can instead inject the twig service and use it directly:

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

use Symfony\Component\HttpFoundation\Response;
use Twig\Environment;

class HelloController
{
    public function __construct(
        private Environment $twig,
    ) {
    }

    public function index(string $name): Response
    {
        $content = $this->twig->render(
            'hello/index.html.twig',
            ['name' => $name]
        );

        return new Response($content);
    }
}

You can also use a special action-based dependency injection to receive services as arguments to your controller action methods.

Base Controller Methods and Their Service Replacements

The best way to see how to replace base Controller convenience methods is to look at the AbstractController class that holds its logic.

If you want to know what type-hints to use for each service, see the getSubscribedServices() method in AbstractController.

This work, including the code samples, is licensed under a Creative Commons BY-SA 3.0 license.
TOC
    Version

    Symfony 7.1 is backed by

    Show your Sylius expertise

    Show your Sylius expertise

    Put the code quality back at the heart of your project

    Put the code quality back at the heart of your project

    Version:

    Table of Contents

    • Invokable Controllers
    • Alternatives to base Controller Methods
      • Base Controller Methods and Their Service Replacements

    Symfony footer

    Avatar of Guillaume Aveline, a Symfony contributor

    Thanks Guillaume Aveline for being a Symfony contributor

    2 commits • 9 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?

      • What is Symfony?
      • Symfony at a Glance
      • Symfony Components
      • 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

      • Symfony Community
      • SymfonyConnect
      • Events & Meetups
      • Projects using Symfony
      • Contributors
      • Symfony Jobs
      • Backers
      • Code of Conduct
      • Downloads Stats
      • Support
    • Blog

      • All Blog Posts
      • A Week of Symfony
      • Case Studies
      • Cloud
      • Community
      • Conferences
      • Diversity
      • Living on the edge
      • Releases
      • Security Advisories
      • Symfony Insight
      • Twig
      • SensioLabs Blog
    • Services

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

      Powered by

    Follow Symfony