Flag of Ukraine
SymfonyCasts stands united with the people of Ukraine

Bind Arguments Globally

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 $12.00

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

Login Subscribe

In practice, you rarely need to do anything inside of services.yaml. Most of the time, when you add an argument to the constructor of a service, it's autowireable. So you add the argument, give it a type-hint... and keep coding!

... lines 1 - 8
class MixRepository
{
public function __construct(
private HttpClientInterface $httpClient,
private CacheInterface $cache,
private bool $isDebug
) {}
... lines 16 - 25
}

But the $isDebug argument is not autowireable... since it's not a service. And that forced us to completely override the service so we could specify that one argument with bind. It works but... that was... kind of a lot of typing to do such a small thing!

... lines 1 - 12
services:
... lines 14 - 30
App\Service\MixRepository:
bind:
'$isDebug': '%kernel.debug%'

Moving bind to _defaults

So here's a different solution. Copy that bind key, delete the service entirely, and up, under _defaults, paste:

... lines 1 - 12
services:
# default configuration for services in *this* file
_defaults:
autowire: true # Automatically injects dependencies in your services.
autoconfigure: true # Automatically registers your services as commands, event subscribers, etc.
bind:
'$isDebug': '%kernel.debug%'
... lines 20 - 32

When we move over and try this... the page still works! How cool is that? And, it makes sense. This section will automatically register MixRepository as a service... and then anything under _defaults will be applied to that service. So the end result is exactly what we had before.

I love doing this! It allows me to set up project-wide conventions. Now that we have this, we could add an $isDebug argument to the constructor of any service and it will instantly work.

Binding with Type_hints

By the way, if you want, you can also include the type with the bind.

So this would now only work if we use the bool type-hint with the argument:

... lines 1 - 12
services:
# default configuration for services in *this* file
_defaults:
autowire: true # Automatically injects dependencies in your services.
autoconfigure: true # Automatically registers your services as commands, event subscribers, etc.
bind:
'bool $isDebug': '%kernel.debug%'
... lines 20 - 32

If we used string, for example, Symfony would not try to pass in that value.

The Autowire Attribute

So the global bind is awesome. But starting in Symfony 6.1, there's another way to specify a non-autowireable argument. Comment out the global bind. I do still like doing this... but let's try the new way:

... lines 1 - 12
services:
# default configuration for services in *this* file
_defaults:
autowire: true # Automatically injects dependencies in your services.
autoconfigure: true # Automatically registers your services as commands, event subscribers, etc.
# bind:
# 'bool $isDebug': '%kernel.debug%'
... lines 20 - 32

If we refresh now, we get an error because Symfony doesn't know what to pass to the $isDebug argument. To fix that, go into MixRepository and, above the argument (or before the argument if you're not using multiple lines), add a PHP 8 attribute called Autowire. Normally, PHP 8 attributes will auto-complete, but this isn't auto-completing for me. That's actually due to a bug in PhpStorm. To get around this, I'm going to type out Autowire... then go to the top and start adding the use statement for this manually, which does give us an option to auto-complete. Hit "tab" and... tah dah! If you want to make them alphabetical, you can move it around.

You may also notice that it's underlined with a message:

Attribute cannot be applied to a property [...]

Again, PhpStorm is a bit confused because this is both a property and an argument.

Anyway, go ahead and pass this an argument %kernel.debug%:

... lines 1 - 6
use Symfony\Component\DependencyInjection\Attribute\Autowire;
... lines 8 - 9
class MixRepository
{
public function __construct(
... lines 13 - 14
#[Autowire('%kernel.debug%')]
private bool $isDebug
) {}
... lines 18 - 27
}

Refresh now and... got it! Pretty cool, right?

Next: most of the time when you autowire an argument like HttpClientInterface, there's only one service in the container that implements that interface. But what if there were multiple HTTP clients in our container? How could we choose the one we want? It's time to talk about named autowiring.

Leave a comment!

5
Login or Register to join the conversation
Philipp Avatar
Philipp Avatar Philipp | posted 17 days ago | edited

The Autowire annotation is amazing!

Heads-up for anyone else who might have trouble using the #[Autowire()] attribute:

It requires Symfony 6.1 (and PHP 8.1)
My dev environment used PHP 8.0.8, and as a result always installed Symfony 6.0.
After upgrading PHP to v 8.1.11, my new Symfony project start out using Symfony 6.2, and the annotation works.

1 Reply
Braunstetter Avatar
Braunstetter Avatar Braunstetter | posted 3 months ago | edited

This Autowire Annoation is very cool. I like to use the ParameterBag (ParameterBagInterface) object whenever I need some parameter inside my services.

But when I think about it more, it might be better for testing reasons to use the parameter itself inside the constructor.
Anyways I love to watch symfonycast videos even after all these years. It doesn't stop to be interesting.

Reply

Hey Braunstetter,

Yeah, it's always a trade-off :) That service is cool, but passing parameters into the constructor might be more straightforward and so simpler to test. Probably it depends on the number of parameters you need, if it's 1-3 params - I'd go with passing them into the constructor. If you need a lot of them - the service would be better in this case.

Cheers!

Reply

I like the Symfony 6.1 new addition on how to wire the service

Reply

Hey Joshua,

We're really happy to hear it, the new way is cool indeed :)

Cheers!

Reply
Cat in space

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

What PHP libraries does this tutorial use?

// composer.json
{
    "require": {
        "php": ">=8.1",
        "ext-ctype": "*",
        "ext-iconv": "*",
        "knplabs/knp-time-bundle": "^1.18", // v1.19.0
        "symfony/asset": "6.1.*", // v6.1.0-RC1
        "symfony/console": "6.1.*", // v6.1.0-RC1
        "symfony/dotenv": "6.1.*", // v6.1.0-RC1
        "symfony/flex": "^2", // v2.1.8
        "symfony/framework-bundle": "6.1.*", // v6.1.0-RC1
        "symfony/http-client": "6.1.*", // v6.1.0-RC1
        "symfony/monolog-bundle": "^3.0", // v3.8.0
        "symfony/runtime": "6.1.*", // v6.1.0-RC1
        "symfony/twig-bundle": "6.1.*", // v6.1.0-RC1
        "symfony/ux-turbo": "^2.0", // v2.1.1
        "symfony/webpack-encore-bundle": "^1.13", // v1.14.1
        "symfony/yaml": "6.1.*", // v6.1.0-RC1
        "twig/extra-bundle": "^2.12|^3.0", // v3.4.0
        "twig/twig": "^2.12|^3.0" // v3.4.0
    },
    "require-dev": {
        "symfony/debug-bundle": "6.1.*", // v6.1.0-RC1
        "symfony/maker-bundle": "^1.41", // v1.42.0
        "symfony/stopwatch": "6.1.*", // v6.1.0-RC1
        "symfony/web-profiler-bundle": "6.1.*" // v6.1.0-RC1
    }
}