Question is simple but... So we have main service:
class ManagerOne {}
and have several another services we want to use in main service:
class ServiceOne{}
class ServiceTwo{}
class ServiceThree{}
class ServiceFour{}
...
Each named as (in services.yml)
service.one
service.two
service.three
service.four
...
Locations of services is different, not in one folder (but I don't think it's a huge trouble for custom autoloader).
Regarding manual we can inject them via __construct() in main service (ManagerOne) but what if we got 20 such services need to be injected? Or use only that we need. Describe them in services as simple inject? O.o I think it's not good idea so.... Also we can inject container and that's it. BUT! Everywhere people saying that inject container worst solution.
What I want. I need method for ManagerOne service which will load service i need by 'service.name' or 'path' with checker 'service exist'.
You could use service tagging and tag each service you want to use in your ManagerOne
class. And either use constructor dependency injection or method injection.
Example:
First of all you need a compiler pass to collect your tagged services:
namespace ...\DependencyInjection\Compiler;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\DependencyInjection\ContainerBuilder;
class ExamplePass implements CompilerPassInterface
{
public function process(ContainerBuilder $container)
{
if (!$container->hasDefinition("manager.one")) {
return;
}
$services = array();
foreach ($container->findTaggedServiceIds('managed_service') as $serviceId => $tag) {
$alias = isset($tag[0]['alias'])
? $tag[0]['alias']
: $serviceId;
// Flip, because we want tag aliases (= type identifiers) as keys
$services[$alias] = new Reference($serviceId);
}
$container->getDefinition('manager.one')->replaceArgument(0, $services);
}
}
Then you need to add the compiler pass to your bundle class:
namespace Example\ExampleBundle;
use Symfony\Component\HttpKernel\Bundle\Bundle;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use ...\DependencyInjection\Compiler\ExamplePass;
class ExampleBundle extends Bundle
{
public function build(ContainerBuilder $container)
{
parent::build($container);
$container->addCompilerPass(new ExamplePass());
}
}
Now you can use your services:
# services.yml
manager.one:
class: ManagerClass
arguments:
- [] # will be replaced by our compiler pass
services.one:
class: ServiceOne
tags:
- { name: managed_service, alias: service_one }
services.two:
class: ServiceTwo
tags:
- { name: managed_service, alias: service_two }
But caution if you get your manager, all service classes will be automatically created. If this is a performance drawback for you could pass only the service ids (not the Reference) to your management class. Add the @service_container
as second argument and create the service as needed.