How to Make Service Arguments/References Optional
Sometimes, one of your services may have an optional dependency, meaning that the dependency is not required for your service to work properly. You can configure the container to not throw an error in this case.
Setting Missing Dependencies to null
You can use the null strategy to explicitly set the argument to null
if the service does not exist:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
<!-- 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\Newsletter\NewsletterManager">
<argument type="service" id="logger" on-invalid="null"/>
</service>
</services>
</container>
Note
The "null" strategy is not currently supported by the YAML driver.
Ignoring Missing Dependencies
The behavior of ignoring missing dependencies is the same as the "null" behavior except when used within a method call, in which case the method call itself will be removed.
In the following example the container will inject a service using a method call if the service exists and remove the method call if it does not:
1 2 3 4 5
# config/services.yaml
services:
App\Newsletter\NewsletterManager:
calls:
- setLogger: ['@?logger']
Note
If the argument to the method call is a collection of arguments and any of them is missing, those elements are removed but the method call is still made with the remaining elements of the collection.
In YAML, the special @? syntax tells the service container that the
dependency is optional. The NewsletterManager must also be rewritten by
adding a setLogger() method:
1 2 3 4
public function setLogger(LoggerInterface $logger): void
{
// ...
}
Ignoring Uninitialized Dependencies
Some services only need a dependency if the application already used it
during the current request. For example, a listener that flushes a newsletter
queue at the end of the request doesn't need to create the queue when nothing
was added to it. Use the ignore_uninitialized strategy to inject the
service only if the container already created it, and null otherwise:
1 2 3 4 5 6
# config/services.yaml
services:
App\EventListener\NewsletterQueueFlusher:
# the '@!' syntax injects the service only if the container
# already created it; otherwise, it injects null
arguments: ['@!App\Newsletter\NewsletterQueue']
Since the container can inject null, the argument must be nullable:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
// src/EventListener/NewsletterQueueFlusher.php
namespace App\EventListener;
use App\Newsletter\NewsletterQueue;
class NewsletterQueueFlusher
{
public function __construct(
private ?NewsletterQueue $queue,
) {
}
public function __invoke(): void
{
// the queue is null if no code used it during this request
$this->queue?->flush();
}
}
The container checks whether it already created the dependency at the moment
it creates the service that uses it, not later. This works in the example
because the container creates event listeners lazily, the first time the
application dispatches their event. When used within a method call, the
container removes the method call if it hasn't created the dependency yet,
like the ignore strategy does for missing services.