【发布时间】:2019-09-19 14:04:46
【问题描述】:
我正在开发一个应用程序,其中有一些处理程序作为我希望能够调用的服务。他们都实现了ItemHandlerInterface。
我希望能够在控制器中检索所有ItemHandlerInterface 服务集合,而无需手动连接它们。
到目前为止,我专门标记了它们:
services.yaml
_instanceof:
App\Model\ItemHandlerInterface:
tags: [!php/const App\DependencyInjection\ItemHandlersCompilerPass::ITEM_HANDLER_TAG]
lazy: true
并尝试在控制器中检索我的服务集合。如果只有一个服务实现ItemHandlerInterface,它就可以工作,但是一旦我创建了几个服务(比如下面的TestHandler 和Test2Handler,我最终会得到一个The service "service_locator.03wqafw.App\Controller\ItemUpdateController" has a dependency on a non-existent service "App\Model\ItemHandlerInterface".
如何动态检索实现我的接口的所有服务?
一个肮脏的解决方案是用public: true 强制所有ItemHandlerInterface 并将Container 传递给我的控制器构造函数。但这很丑陋,我想找到一种更优雅的方式。
ItemUpdateController
namespace App\Controller;
use App\Model\ItemHandlerInterface;
use App\Service\ItemFinder;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Debug\Exception\ClassNotFoundException;
use Symfony\Component\DependencyInjection\ServiceSubscriberInterface;
use Symfony\Component\HttpFoundation\RequestStack;
use App\Model\Item;
use Psr\Container\ContainerInterface;
/**
* Class ItemUpdateController
*
* @package App\Controller
*/
class ItemUpdateController extends AbstractController
{
/**
* @var ContainerInterface
*/
protected $locator;
public function __construct(ContainerInterface $locator)
{
$this->locator = $locator;
}
public static function getSubscribedServices()
{
// Try to subscribe to all ItemHandlerInterface services
return array_merge(
parent::getSubscribedServices(),
['item_handler' => ItemHandlerInterface::class]
);
}
/**
* @param string $id
* @param RequestStack $requestStack
* @param ItemFinder $itemFinder
*
* @return Item
* @throws \Symfony\Component\Debug\Exception\ClassNotFoundException
*/
public function __invoke(
string $id,
RequestStack $requestStack,
ItemFinder $itemFinder
) {
// Find item
$item = $itemFinder->findById($id);
// Extract and create handler instance
$handlerName = $item->getHandlerName();
if($this->locator->has($handlerName)) {
$handler = $this->locator->get($handlerName);
$request = $requestStack->getCurrentRequest();
$payload = json_decode($request->getContent());
call_user_func($handler, $payload, $request);
return $item;
}
}
}
src/ItemHandler/TestHandler.php
namespace App\ItemHandler;
use App\Model\ItemHandlerInterface;
use Doctrine\ORM\EntityManagerInterface;
class TestHandler implements ItemHandlerInterface
{
// implementation
}
src/ItemHandler/Test2Handler.php
namespace App\ItemHandler;
use App\Model\ItemHandlerInterface;
use Doctrine\ORM\EntityManagerInterface;
class Test2Handler implements ItemHandlerInterface
{
// implementation
}
【问题讨论】:
-
不确定如果没有某种 ItemHandlers 定位器类,这将如何工作。 Symfony 如何知道将哪个定位器注入您的控制器?也许尝试按照这里的答案,看看会发生什么:stackoverflow.com/questions/54946647/…
-
您想在您的控制器中注入
ItemHandlerInterface的所有实现,是这样吗?但是您说“无需手动连接它们”……例如,您不想将代码添加到 services.yaml 中?或者这样可以吗? -
@yivi 这正是我的意思,至少不是一一。这个想法是发布应用程序,让开发人员仅添加 ItemHandlerInterface 实现,并让应用程序在运行时选择与控制器调用时给出的参数有关的应用程序。
标签: php symfony dependency-injection