_defaults, autowire & autoconfigure
If you started a brand new Symfony 3.3 project, its services.yml
file will look like this.
You're actually seeing at least four new features all at once! Wow. All of this is built on top of the existing service configuration system and you need to "opt in" to any of the new features. That means that the traditional way of configuring services that you've been using until now still works and always will. Winning!
But even if you ultimately choose not to use some of these new features, you need to understand how they work, because you'll see them a lot. For example, the Symfony documentation has already been updated to assume you're using these.
First, a word of warning: using the new features is fun. But, upgrading an existing project to use them... well... it's less fun. It takes some work, and when you're done... your project works... the same as before. I'm also going to show you the ugliest parts of the new system so you can handle them in your project. But stick with me! At the end, we'll use the new features to build some new code. And that, is a blast.
_defaults: File-side Service Defaults
Let's look at this _defaults
thing first. Open up your app/config/services.yml
file. At the top of the services
section - though order isn't important - add _defaults
. Then below that, autowire: true
and autoconfigure: true
:
// ... lines 1 - 5 | |
services: | |
_defaults: | |
autowire: true | |
autoconfigure: true | |
// ... lines 10 - 41 |
Let's unpack this. First, _defaults
is a new special keyword that allows you to set default configuration for all services in this file. It's equivalent to adding autowire
and autoconfigure
keys under every service in this file only. And of course, any config from _defaults
can be overridden by a specific service.
Autowiring
Autowiring is not new: we talk about it in our Symfony series. When a service is autowired, it means that its constructor arguments are automatically configured when possible by reading type-hints. For example, the MarkdownExtension
is autowired:
// ... lines 1 - 5 | |
services: | |
// ... lines 7 - 10 | |
app.markdown_extension: | |
class: AppBundle\Twig\MarkdownExtension | |
tags: | |
- { name: twig.extension } | |
#arguments: ['@app.markdown_transformer'] | |
autowire: true | |
// ... lines 17 - 43 |
And its first constructor argument is type-hinted with MarkdownTransformer
:
// ... lines 1 - 2 | |
namespace AppBundle\Twig; | |
use AppBundle\Service\MarkdownTransformer; | |
class MarkdownExtension extends \Twig_Extension | |
{ | |
// ... lines 9 - 10 | |
public function __construct(MarkdownTransformer $markdownTransformer) | |
{ | |
// ... line 13 | |
} | |
// ... lines 15 - 33 | |
} |
Thanks to that, Symfony determines which service to pass here.
The way that autowiring works has changed in Symfony 3.3. But more on that later.
Since we have autowire
under _defaults
, we can remove it from everywhere else: it's redundant. And yes, this does mean that some services that were not autowired before are now set to autowire: true
. For example, app.markdown_transformer
is now being autowired:
// ... lines 1 - 5 | |
services: | |
app.markdown_transformer: | |
class: AppBundle\Service\MarkdownTransformer | |
arguments: ['@markdown.parser', '@doctrine_cache.providers.my_markdown_cache'] | |
// ... lines 10 - 43 |
But... that's no problem! Both of its arguments are being explicitly set:
// ... lines 1 - 2 | |
namespace AppBundle\Service; | |
use Doctrine\Common\Cache\Cache; | |
use Knp\Bundle\MarkdownBundle\MarkdownParserInterface; | |
class MarkdownTransformer | |
{ | |
// ... lines 10 - 12 | |
public function __construct(MarkdownParserInterface $markdownParser, Cache $cache) | |
{ | |
// ... lines 15 - 16 | |
} | |
// ... lines 18 - 33 | |
} |
so autowiring simply doesn't do anything. Setting autowire: true
under _defaults
is safe to add to an existing project.
Autoconfigure
Next, this autoconfigure
key is a brand new feature. When a service is autoconfigured, it means that Symfony will automatically tag it when possible. For example, our MarkdownExtension
extends \Twig_Extension
:
// ... lines 1 - 6 | |
class MarkdownExtension extends \Twig_Extension | |
{ | |
// ... lines 9 - 33 | |
} |
Which implements Twig_ExtensionInterface
:
abstract class Twig_Extension implements Twig_ExtensionInterface
{
// ...
}
That's actually the important part. When a service is autoconfigured and its class implements Twig_ExtensionInterface
, the twig.extension
tag is automatically added for you:
// ... lines 1 - 5 | |
services: | |
// ... lines 7 - 10 | |
app.markdown_extension: | |
// ... line 12 | |
tags: | |
- { name: twig.extension } | |
// ... lines 15 - 43 |
Basically, Symfony is saying:
Hey! I see you configured a service that implements
Twig_ExtensionInterface
. Obviously, that's a Twig extension, so let me configure it for you.
// ... lines 1 - 5 | |
services: | |
// ... lines 7 - 14 | |
app.markdown_extension: | |
class: AppBundle\Twig\MarkdownExtension | |
#arguments: ['@app.markdown_transformer'] | |
// ... lines 18 - 41 |
This works for many - but not all tags. It does not work for doctrine.event_subscriber
or form.type_extension
:
// ... lines 1 - 5 | |
services: | |
// ... lines 7 - 21 | |
app.doctrine.hash_password_listener: | |
class: AppBundle\Doctrine\HashPasswordListener | |
tags: | |
- { name: doctrine.event_subscriber } | |
app.form.help_form_extenion: | |
class: AppBundle\Form\TypeExtension\HelpFormExtension | |
tags: | |
- { name: form.type_extension, extended_type: Symfony\Component\Form\Extension\Core\Type\FormType } | |
// ... lines 31 - 41 |
Because it has an extended_type
tag option... which the system can't guess for you. When you're developing a feature, the docs will tell you whether or not you need to add the tag manually. If you do add a tag, even though you didn't need to, no problem! Your tag takes precedence.
So, all our services are autowired and autoconfigured! But, it doesn't make any difference, besides shortening our config just a little. And when we refresh, everything still works!
Hi !
I have like 40 differents services.yml in my app.
Shall I add these keys in all this 40 files ?