Service Method Calls and Setter Injection

Tip

If you’re using autowiring, you can use @required to automatically configure method calls.

Usually, you’ll want to inject your dependencies via the constructor. But sometimes, especially if a dependency is optional, you may want to use “setter injection”. For example:

namespace App\Service;

use Psr\Log\LoggerInterface;

class MessageGenerator
{
    private $logger;

    public function setLogger(LoggerInterface $logger)
    {
        $this->logger = $logger;
    }

    // ...
}

To configure the container to call the setLogger method, use the calls key:

  • YAML
    1
    2
    3
    4
    5
    6
    # config/services.yaml
    services:
        App\Service\MessageGenerator:
            # ...
            calls:
                - [setLogger, ['@logger']]
    
  • XML
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    <!-- config/services.xml -->
    <?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
            https://symfony.com/schema/dic/services/services-1.0.xsd">
    
        <services>
            <service id="App\Service\MessageGenerator">
                <!-- ... -->
                <call method="setLogger">
                    <argument type="service" id="logger"/>
                </call>
            </service>
        </services>
    </container>
    
  • PHP
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    // config/services.php
    namespace Symfony\Component\DependencyInjection\Loader\Configurator;
    
    use App\Service\MessageGenerator;
    
    return function(ContainerConfigurator $configurator) {
        // ...
    
        $services->set(MessageGenerator::class)
            // In versions earlier to Symfony 5.1 the service() function was called ref()
            ->call('setLogger', [service('logger')]);
    };
    

To provide immutable services, some classes implement immutable setters. Such setters return a new instance of the configured class instead of mutating the object they were called on:

namespace App\Service;

use Psr\Log\LoggerInterface;

class MessageGenerator
{
    private $logger;

    /**
     * @return static
     */
    public function withLogger(LoggerInterface $logger)
    {
        $new = clone $this;
        $new->logger = $logger;

        return $new;
    }

    // ...
}

Because the method returns a separate cloned instance, configuring such a service means using the return value of the wither method ($service = $service->withLogger($logger);). The configuration to tell the container it should do so would be like:

  • YAML
    1
    2
    3
    4
    5
    6
    # config/services.yaml
    services:
        App\Service\MessageGenerator:
            # ...
            calls:
                - withLogger: !returns_clone ['@logger']
    
  • XML
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    <!-- config/services.xml -->
    <?xml version="1.0" encoding="UTF-8" ?>
    <container xmlns="http://symfony.com/schema/dic/services"
        xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://symfony.com/schema/dic/services
            https://symfony.com/schema/dic/services/services-1.0.xsd">
    
        <services>
            <service id="App\Service\MessageGenerator">
                <!-- ... -->
                <call method="withLogger" returns-clone="true">
                    <argument type="service" id="logger"/>
                </call>
            </service>
        </services>
    </container>
    
  • PHP
    1
    2
    3
    4
    5
    6
    // config/services.php
    use App\Service\MessageGenerator;
    use Symfony\Component\DependencyInjection\Reference;
    
    $container->register(MessageGenerator::class)
        ->addMethodCall('withLogger', [new Reference('logger')], true);