phpsymfony

Alias interfaces by adding an environment parameter as a condition in services.yaml


I have two repositories implementing the same interface.

  1. App\Repository\RepoA
  2. App\Repository\Legacy\RepoA

I also have added the USE_LEGACY env variable(as a parameter in the services.yaml as well):

parameters:
    use_legacy: '%env(bool:USE_LEGACY)%'

What I wanna achieve is, if USE_LEGACY is set to true the interface when called will call the repository from legacy, if not, it will call the other(non-legacy) repository.

So far I tried:

services:
      App\Repository\Interface\RepoAInterface: 
        alias: '@=service("App\Repository" ~ (parameter("use_legacy:") == false ? "\\RepoA" : "\Legacy\\RepoA"))'

But this is not working. Is there any solution?

FYI, I'm using Symfony 6.4 and PHP 8.3


Solution

  • Use factory class to dynamically build services:

    services:
       App\Repository\Interface\RepoAInterface:
           factory: '@App\Factory\RepoAFactory'
           arguments:
               - '%use_legacy%'
    
    use App\Repository\RepoA;
    use App\Repository\Legacy\RepoA as LegacyRepoA;
    use App\Repository\Interface\RepoAInterface;
    use Symfony\Component\DependencyInjection\Attribute\AutowireLocator;
    use Psr\Container\ContainerInterface;
    
    class RepoAFactory
    {
        public function __construct(
            #[AutowireLocator([
                RepoA::class,
                LegacyRepoA::class,
            ])]
            private ContainerInterface $locator,
        ) {
        }
    
        public function __invoke(bool $useLegacy): RepoAInterface
        {
            return $useLegacy === true
                ? $this->locator->get(LegacyRepoA::class)
                : $this->locator->get(RepoA::class);
        }
    }