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!

2
Login or Register to join the conversation
JoshuaGugun Avatar
JoshuaGugun Avatar JoshuaGugun | posted 29 days ago

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
    }
}