Vincent Barrault

Injecter un service sans passer par construct

Injecter un service sans passer par construct

Table des matières

Faire un service à injecter

Si j'ai un service à injecter, mais que je ne sais pas à l'avance si je vais pouvoir utiliser le constructeur, je dois trouver une autre manière de le faire

Il se pourrait que le constructeur de mon service soit déjà gros, ou encore que le service ai été crée avec une Factory...

Voici le service que je vais injecter:

./src/Utils/MyService.php
<?php

namespace App\Utils;

class MyService
{
    public function doSomething(): void
    {
        // perform operations here...
    }
}

Faire une interface pour injecter le service

Pour "marquer" les services où je vais injecter MyService, je crée une interface qui certifie que je possède un accès à MyService via une méthode getMyService.

./src/Utils/MyServiceAwareInterface.php
<?php

namespace App\Utils;

interface MyServiceAwareInterface
{
    function getMyService(): MyService;
}

Faire un Trait

Et pour ne pas à avoir à implémenter plein de fois la même logique, je fais un Trait qui sera utilisé dans mes services implémentant MyServiceAwareInterface.

./src/Utils/MyServiceAwareInterface.php
<?php

namespace App\Utils;

trait MyServiceAwareTrait
{
    private $myService;

    public function setMyService(MyService $myService): void
    {
        $this->myService = $myService;
    }

    public function getMyService(): MyService
    {
        if (!isset($this->myService)) {
            throw new \RuntimeException("MyService has not been injected properly.");
        }

        return $this->myService;
    }
}

Le Trait conserve une instance de MyService qu'il obtient avec la méthode setMyService et implémente getMyService de MyServiceAwareInterface.

Definir les services dans la configuration

Pour finir, il ne me reste plus qu'à trouver les services implémentant MyServiceAwareInterface et à leur injecter '@App\Utils\MyService' grâce au Trait.

./config/services.yml
services:
    _instanceof:
        App\Utils\MyServiceAwareInterface:
            calls:
                - setMyService: ['@App\Utils\MyService']

Il est aussi possible de le faire avec un CompilerPass.

Voici un exemple de service qui implémente l'interface:

./src/Domain/Example.php
<?php

namespace App\Domain;

use App\Utils\MyServiceAwareInterface;
use App\Utils\MyServiceAwareTrait;

class ExampleService implements MyServiceAwareInterface
{
    use MyServiceAwareTrait;

    public function performExample(): void
    {
        $this->getMyService()->doSomething();
    }
}