I\'m using Symfony 4 for a project, and I have a question regarding factories.
Assume that I have a strategy depending on a kind of string.
I\'d like to crea
A Symfony Service Locator can be used to avoid the need to inject the complete container. The locator acts like a container but only has access to a limited number of services.
It takes a bit of magic to configure everything. In your case, you only want the locator to access services implementing the Doing interface.
Start with the locator which will inherit get/has methods like all containers:
use Symfony\Component\DependencyInjection\ServiceLocator;
class DoingLocator extends ServiceLocator
{
protected $services = [
'bar' => 'app.service.bar',
'baz' => 'app.service.baz'
];
public function locate($string) {
return $this->get($this->services[$string]);
}
}
Now comes the magic. You actually could configure this manually in services.yaml per the documentation but it is more fun to do it automatically.
Start by making your Kernel class a compiler pass:
# src/Kernel.php
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
class Kernel extends BaseKernel implements CompilerPassInterface
Next, automatically tag all services that implement the Doing interface:
# Kernel.php
protected function build(ContainerBuilder $container)
{
$container->registerForAutoconfiguration(Doing::class)
->addTag('doing');
}
Finally, add a compiler pass to build your locator service:
# Kernel.php
public function process(ContainerBuilder $container)
{
$doingLocatorIds = [];
foreach ($container->findTaggedServiceIds('doing') as $id => $tags) {
$doingLocatorIds[$id] = new Reference($id);
}
$doingLocator = $container->getDefinition(DoingLocator::class);
$doingLocator->setArguments([$doingLocatorIds]);
}
And presto. You are done. You can now inject your DoingLocator (aka MyServiceFactory) and all should be well.
You can use own compiler passes, extensions and service locators. It's a way Symfony allows that, but it requires lot of code.
The simplest approach is to autowire arguments by autowired array.
/**
* @param Doing[] $doings
*/
public function __construct(array $doings)
{
$this->doings = $doings;
}
public function create(string $name): Doing
{
foreach ($this->doings as $doing) {
if ($doing->getName() === name) { // this depends on your design; can be also "is_a" or "instanceof"
return $doing;
}
}
throw new MissingDoingException;
}
This is also called collector pattern.