我正在将Symfony 4用于一个项目,并且对工厂有疑问。
假设我有一种策略,具体取决于一种字符串。
我想基于此道具创建不同的服务,每个服务都有自己的依赖关系,并且我想创建工厂服务,以便界面简单。
让我举个例子:
class ServiceBar implements Doing{
public function __construct($dep1,$dep2){
}
public function do();
}
class ServiceBaz implements Doing{
public function __construct($dep3,$dep4){
}
public function do();
}
// Factory Class
class MyServiceFactory{
protected $services = [
'bar' => 'app.service.bar',
'baz' => 'app.service.baz'
];
public function __construct(ContainerInterface $sc){
$this->sc = $sc;
}
public function factory($string){
if(!$this->sc->has($this->services[$string])){
throw new Exception("Missing Service");
}
$this->sc->get($this->services[$string])->do();
}
}
// IndexController.php
public function indexAction(Request $request, MyServiceFactory $factory)
{
$factory->factory($request->get('action'));
}
通过此实现,我创建了具有所有依赖关系的服务,并从控制器中调用了一个工厂。
对于此解决方案,您还有其他意见吗? 我已经用工厂构造函数注入了服务容器;还有其他方法可以从工厂创建服务吗?这种方法有什么问题吗?
预先感谢
答案 0 :(得分:1)
可以使用Symfony Service Locator来避免注入整个容器的麻烦。定位器的作用就像一个容器,但只能访问有限数量的服务。
配置所有内容需要一些魔术。就您而言,您只希望定位器访问实现Doing接口的服务。
从定位器开始,该定位器将像所有容器一样继承get / has方法:
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]);
}
}
魔法来了。实际上,您可以按照文档在services.yaml中手动配置它,但是自动执行会更有趣。
首先使您的内核类成为编译器传递:
# src/Kernel.php
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
class Kernel extends BaseKernel implements CompilerPassInterface
下一步,自动标记实现Doing接口的所有服务:
# Kernel.php
protected function build(ContainerBuilder $container)
{
$container->registerForAutoconfiguration(Doing::class)
->addTag('doing');
}
最后,添加一个编译器通道以构建您的定位器服务:
# 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]);
}
然后保存。大功告成现在,您可以注入您的DoingLocator(又名MyServiceFactory),并且一切都会很好。
答案 1 :(得分:0)
您可以使用自己的编译器通道,扩展和服务定位符。这是Symfony允许的一种方式,但需要大量代码。
最简单的方法是通过自动装配数组自动装配参数。
/**
* @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;
}
这也称为收集器模式。