自zend-mvc版本的2.7.0版本ServiceLocatorAwareInterface
被删除后,控制器内的$this->serviceLocator->get()
调用也被删除。
这就是为什么前几天我对我的所有模块进行了大量重构,以便通过构建器使用工厂注入所需的服务/对象。
当然,我明白为什么这是更好/更清洁的做事方式,因为现在更容易看到家属。但另一方面:
这会导致繁重的开销和更多从未使用过的类实例,不是吗?
让我们看一个例子:
因为我的所有控制器都有依赖关系,所以我为所有控制器创建了工厂。
CustomerControllerFactory.php 的
namespace Admin\Factory\Controller;
class CustomerControllerFactory implements FactoryInterface {
public function createService(ServiceLocatorInterface $controllerManager) {
$serviceLocator = $controllerManager->getServiceLocator();
$customerService = $serviceLocator->get('Admin\Service\CustomerService');
$restSyncService = $serviceLocator->get('Admin\Service\SyncRestClientService');
return new \Admin\Controller\CustomerController($customerService, $restSyncService);
}
}
CustomerController.php 的
namespace Admin\Controller;
class CustomerController extends AbstractRestfulController {
public function __construct($customerService, $restSyncService) {
$this->customerService = $customerService;
$this->restSyncService = $restSyncService;
}
}
module.config.php 的
'controllers' => [
'factories' => [
'Admin\Controller\CustomerController' => 'Admin\Factory\Controller\CustomerControllerFactory',
]
],
'service_manager' => [
'factories' => [
'Admin\Service\SyncRestClientService' => 'Admin\Factory\SyncRestClientServiceFactory',
]
]
SyncRestClientServiceFactory.php 的
namespace Admin\Factory;
class SyncRestClientServiceFactory implements FactoryInterface {
public function createService(ServiceLocatorInterface $serviceLocator) {
$entityManager = $serviceLocator->get('doctrine.entitymanager.orm_default');
$x1 = $serviceLocator->get(...);
$x2 = $serviceLocator->get(...);
$x3 = $serviceLocator->get(...);
// ...
return new \Admin\Service\SyncRestClientService($entityManager, $x1, $x2, $x3, ...);
}
}
SyncRestService是一个复杂的服务类,它查询我们系统的某些内部服务器。它有很多依赖项,并且总是在请求来到CustomerController时创建。但是这个同步服务仅在CustomerController的syncAction()
内使用!在我$this->serviceLocator->get('Admin\Service\SyncRestClientService')
中使用syncAction()
之前,只有它才被实例化。
通常,看起来很多实例是在每个请求中通过工厂创建的,但是大多数依赖项都没有使用。这是一个问题,因为我的设计还是“通过构造函数进行依赖注入”的正常副作用行为?
答案 0 :(得分:7)
在我看来,这是通过构造函数进行依赖注入的正常效果。
我认为您现在有两个选项(不是互斥的)来改善您的应用程序的工作方式:
拆分控制器,以便仅在需要时才会实现依赖关系。这肯定会产生更多的课程,更多的工厂等等,但你的代码将更多地达到单一的责任原则
你可以使用Lazy Services,这样,即使某些服务是整个控制器的依赖关系,它们实际上只会在它们第一次被调用时才会被实现(所以从不对它们所处的行为进行实例化)不叫!)
答案 1 :(得分:1)
如果您只在控制器内使用SyncRestClientService
,则应考虑将其从服务更改为控制器插件(或者在您注入SyncRestClientService
的位置制作控制器插件)。
就像你一样,你仍然可以在你的控制器syncAction
方法中获得它,就像你之前一样。这正是ZF2控制器插件的目的。
首先,您需要创建控制器插件类(扩展Zend\Mvc\Controller\Plugin\AbstractPlugin
):
<?php
namespace Application\Controller\Plugin;
use Zend\Mvc\Controller\Plugin\AbstractPlugin;
class SyncPlugin extends AbstractPlugin{
protected $syncRestClientService;
public function __constuct(SyncRestClientService $syncRestClientService){
$this->syncRestClientService = $syncRestClientService
}
public function sync(){
// do your syncing using the service that was injected
}
}
然后是工厂在课堂上注入你的服务:
<?php
namespace Application\Controller\Plugin\Factory;
use Application\Controller\Plugin\SyncPlugin;
class SyncPluginFactory implements FactoryInterface
{
/**
* @param ServiceLocatorInterface $serviceController
* @return SyncPlugin
*/
public function createService(ServiceLocatorInterface $serviceController)
{
$serviceManager = $serviceController->getServiceLocator();
$syncRestClientService = $serviceManager>get('Admin\Service\SyncRestClientService');
return new SyncPlugin($syncRestClientService);
}
}
然后您需要在module.config.php
:
<?php
return array(
//...
'controller_plugins' => array(
'factories' => array(
'SyncPlugin' => 'Application\Controller\Plugin\Factory\SyncPluginFactory',
)
),
// ...
);
现在您可以在控制器操作中使用它,如下所示:
protected function syncAction(){
$plugin = $this->plugin('SyncPlugin');
//now you can call your sync logic using the plugin
$plugin->sync();
}
详细了解控制器插件here in the documentation
答案 2 :(得分:0)
也许您只需要将一个依赖项注入到控制器构造函数(ServiceManager实例)中。我没有看到任何警察......
namespace Admin\Factory\Controller;
class CustomerControllerFactory implements FactoryInterface {
public function createService(ServiceLocatorInterface $controllerManager)
{
$serviceLocator = $controllerManager->getServiceLocator();
return new \Admin\Controller\CustomerController($serviceLocator);
}
}
答案 3 :(得分:0)
我个人在控制器工厂获取动作名称,以便按行动注入服务。
查看我的网站控制器。
namespace Admin\Controller\Service;
use Zend\ServiceManager\FactoryInterface;
use Zend\ServiceManager\ServiceLocatorInterface;
use Admin\Controller\SitesController;
use Admin\Model\Sites as Models;
class SitesControllerFactory implements FactoryInterface
{
public function createService(ServiceLocatorInterface $serviceLocator)
{
$actionName = $serviceLocator->getServiceLocator()->get('Application')->getMvcEvent()->getRouteMatch()->getParam('action');
$controller = new SitesController();
switch ($actionName) {
case 'list':
$controller->setModel($serviceLocator->getServiceLocator()->get(Models\ListSitesModel::class));
break;
case 'view':
$controller->setModel($serviceLocator->getServiceLocator()->get(Models\ViewSiteModel::class));
break;
case 'add':
$controller->setModel($serviceLocator->getServiceLocator()->get(Models\AddSiteModel::class));
break;
case 'edit':
$controller->setModel($serviceLocator->getServiceLocator()->get(Models\EditSiteModel::class));
break;
}
return $controller;
}
}
如您所见,我使用$serviceLocator->getServiceLocator()->get('Application')->getMvcEvent()->getRouteMatch()->getParam('action');
获取操作名称,并在需要时使用switch语句注入依赖项。
我不知道这是否是最佳解决方案,但它对我有用。
希望这有帮助。