Symfony’s autowiring is one of the best things to come to the framework in the 3.X series. Without it we would all still be extending ContainerAware
base classes and be using a service locator.
But what if we need multiple instances of somethign in the container? The docs talk about dealing with multiple implementations of a single interface. But what about multiple definitions that use the same class?
A real life example, just encountered today: a generic console command that takes a common interface and a name. In my application, there are serveral implementations of this Example
interface and each one needs to go into it’s own command with a different name.
<?php interface Example { // pretend there's stuff here } class ExampleCommand extends Command { public function __construct(Example $example, string $commandName) { $this->example = $example; parent::__construct($commandName); } }
A typical autowiring config might point to the console commands directory and autowire all of it.
<?xml version="1.0" encoding="UTF-8" ?> <container xmlns="http://symfony.com/schema/dic/services" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd"> <services> <prototype autowire="true" autoconfigure="true" namespace="App\Cli\Command\" resource="%kernel.project_dir%/src/Cli/Command/*"> <tag name="console.command" /> </prototype> </services> </container>
But the above errors because autowiring can’t figure out what to do with the $commandName
argument. And, of course, autowiring does not know that the application requires each implementation of Example
to go to its own command.
Solution: Manually Wire Things
Well, of course that’s the solution! But there’s a trick here. At least one of the manually wired commands has to have an ID as the class name. This tells the autowiring system that we’ve manually wired this. Other service definitions can have other IDs.
<?xml version="1.0" encoding="UTF-8" ?> <container xmlns="http://symfony.com/schema/dic/services" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd"> <services> <!-- prototype (see above) here --> <!-- one service id === command class name --> <service id="App\Cli\Command\ExampleCommand"> <argument type="service" id="App\FirstExample" /> <argument>example:first</argument> <tag name="console.command" /> </service> <!-- other definitions should have a different id --> <service id="app.cli.second_example" class="App\Cli\Command\ExampleCommand"> <argument type="service" id="App\SecondExample" /> <argument>example:second</argument> <tag name="console.command" /> </service> <service id="app.cli.third_example" class="App\Cli\Command\ExampleCommand"> <argument type="service" id="App\ThirdExample" /> <argument>example:third</argument> <tag name="console.command" /> </service> </services> </container>