Flag of Ukraine
SymfonyCasts stands united with the people of Ukraine

Controllers as Services

Keep on Learning!

If you liked what you've learned so far, dive in!
Subscribe to get access to this tutorial plus
video, code and script downloads.

Start your All-Access Pass
Buy just this tutorial for $6.00

With a Subscription, click any sentence in the script to jump to that part of the video!

Login Subscribe

There is one other piece of auto-registration code in the new services.yml file for Symfony 3.3 involving the Controller/ directory. Copy that!

Then, paste it in our file:

... lines 1 - 8
services:
... lines 10 - 16
AppBundle\:
... lines 18 - 22
# controllers are imported separately to make sure they're public
# and have a tag that allows actions to type-hint services
AppBundle\Controller\:
resource: '../../src/AppBundle/Controller'
public: true
tags: ['controller.service_arguments']
... lines 29 - 57

This auto-registers each class in src/AppBundle/Controller as a service... which was already done above:

... lines 1 - 8
services:
... lines 10 - 14
# makes classes in src/AppBundle available to be used as services
# this creates a service per class whose id is the fully-qualified class name
AppBundle\:
resource: '../../src/AppBundle/*'
# you can exclude directories or files
# but if a service is unused, it's removed anyway
exclude: '../../src/AppBundle/{Entity,Repository}'
# controllers are imported separately to make sure they're public
# and have a tag that allows actions to type-hint services
AppBundle\Controller\:
... lines 26 - 57

This overrides those services to make sure that anything in Controller/ is public and has this very special tag. In Symfony 3.3 - controllers are the one service that must be public. And the tag gives us a special controller argument autowiring super power that we'll see soon.

Controllers are Services!?

But wait, our controllers are services!? Yes! In Symfony 3.3, we recommend that all of your controllers be registered as a service. And it's so awesome! You can still use all of your existing tricks. You can still extend Symfony's base Controller class and use its shortcuts. You can even still fetch public services directly from the container. But now, you can also use proper dependency injection if you want to. And, as long as your service's id is the class name, all of the existing routing config formats will automatically know to use your service. In other words, this just works.

Removing Unnecessary Services

Now that we're auto-registering each class as a service, we can remove these two services:

... lines 1 - 8
services:
... lines 10 - 32
AppBundle\Twig\MarkdownExtension:
#arguments: ['@app.markdown_transformer']
AppBundle\Security\LoginFormAuthenticator: ~
... lines 37 - 57

They're still being registered, but since we don't need to add any further configuration, they're redundant!

Woohoo! And when we refresh our app, everything still works! Controllers as services with four lines of code!

Leave a comment!

8
Login or Register to join the conversation
Nicola Avatar

Hello, I'm trying to call an action from a controller to another controller.
So I've UserController and "getRegistrationNumberAction(Event $event)" and from EventController I call

$number= $this->get(UserController::class)->getRegistrationNumberAction($workEvent);
The error appear here:
$em= $this->getDoctrine()->getManager();
and the error is: Call to a member function has() on null

I've modified services.yml with new symfony configurations (autowire, _defaults, etc) so I can't understand what is wrong. Can you help me?
PS: If I try to dump $this->get(UserController::class) it dump null.
Thanks

Reply

Hey Nicola

I wouldn't recommend to call a controller's action inside another controller, what are you trying to achieve?

You may like to know that you can initiate another request (call a controllers action) inside a twig's template (More info here: https://symfony.com/doc/cur...

Cheers!

Reply
Nicola Avatar
Nicola Avatar Nicola | MolloKhan | posted 5 years ago | edited

Hello MolloKhan , I've an action (getRegistrationNumber) that count users registered to an event and add 1. It's just a test but I want to add some users to an event not just with UserController but with EventController; so, from EventController, I need to know the registration number and the best way to do that was to call the action from UserController. Do you have a better idea? Thanks :-)

Reply

Ohh I got you. Of course a I have some ideas, but maybe not good ones! :P

In that case I would move all that logic to the UserRepository, so you can ask him about the registration number of the user and to do the sum up
And calling it from EventController would be as easy as


// EventController
....
$userRepo = $this->getDoctrine()->getRepository(User::class);
$userRepo->getRegistrationNumber($user);
Reply
Nicola Avatar

Great idea..I'll try :-) just a question: if using action from one controller to another isn't recommended, why add Controller as Services in 3.3?

Reply

Excellent question, is known that you can use a controller just like a service, but the idea is to take advantage of the "dependency injection" in your controller's actions, instead of retrieving services from the container

Reply
Nicola Avatar

Thank you, I need to study more about dependency injection. BTW I've moved the logic to user Repository and It works great. Thank you :-)

Reply

Awesome!
Let's keep learning ;)

Reply
Cat in space

"Houston: no signs of life"
Start the conversation!

What PHP libraries does this tutorial use?

// composer.json
{
    "require": {
        "php": ">=5.5.9",
        "symfony/symfony": "3.3.0-RC1", // v3.3.0-RC1
        "doctrine/orm": "^2.5", // 2.7.5
        "doctrine/doctrine-bundle": "^1.6", // 1.10.3
        "doctrine/doctrine-cache-bundle": "^1.2", // 1.3.5
        "symfony/swiftmailer-bundle": "^2.3", // v2.6.7
        "symfony/monolog-bundle": "^3.1", // v3.2.0
        "symfony/polyfill-apcu": "^1.0", // v1.23.0
        "sensio/distribution-bundle": "^5.0", // v5.0.25
        "sensio/framework-extra-bundle": "^3.0.2", // v3.0.29
        "incenteev/composer-parameter-handler": "^2.0", // v2.1.4
        "composer/package-versions-deprecated": "^1.11", // 1.11.99.4
        "knplabs/knp-markdown-bundle": "^1.4", // 1.7.1
        "doctrine/doctrine-migrations-bundle": "^1.1", // v1.3.2
        "stof/doctrine-extensions-bundle": "^1.2" // v1.3.0
    },
    "require-dev": {
        "sensio/generator-bundle": "^3.0", // v3.1.7
        "symfony/phpunit-bridge": "^3.0", // v3.4.47
        "nelmio/alice": "^2.1", // v2.3.6
        "doctrine/doctrine-fixtures-bundle": "^2.3" // v2.4.1
    }
}
userVoice