这个问题可以通过ZF2+原则+MVC编程实践的棱镜来看待,也可以只从OOP的角度来看待。
我关心的是关注点分离,以及删除依赖关系。
我在控制器中使用的代码如下:
class MyController
{
private $em; //entityManager
function __construct()
{
$this->em = DoctrineConnector::getEntityManager();
}
function indexAction()
{
//Input
$inputParameter = filter_input(...);
//request for Data
$queryBuilder = $this->em->createQuery(...)
->setParameter('param', $inputParameter);
$query = $queryBuilder->getQuery();
//$services is the user-defined data type requested
$services = $query->getResult();
//use data to produce a view model
$view = new ViewModel();
$view->setVariables(array('services' => $services));
return $view;
}
}
我对上述情况并不完全满意,希望有第二种意见。首先,我的EntityManager
是类的部分,所以当我认为它不应该是控制器的一部分时,我的类知道实体管理器构造。我是否可以使用Factory
或Builder
设计模式来帮助创建MyController
类?
如果我这样做了,我可以将em
(entityManager(构造移动到Factory模式中,并在Factory中创建和填充MyController
。然后,MyController
可以改为具有私有变量$services
。
即
class MyController
{
private $services;
function setServices($services)
{
$this->services = $services;
}
function indexAction()
{
//use data to produce a view model
$view = new ViewModel();
$view->setVariables(array('services' => $this->services));
return $view;
}
}
class MyFactoryMethod
{
function createMyController()
{
//Input
$inputParameter = filter_input(INPUT_GET...);
//request for Data
$queryBuilder = $this->em->createQuery(...)
->setParameter('param', $inputParameter);
$query = $queryBuilder->getQuery();
//$services is the user-defined data type requested
$services = $query->getResult();
//create and return MyController instance
$controller = new MyController();
$controller->setServices($services);
return $controller;
}
}
我通常尝试使用PHP的mysql
扩展来消除对各种对象中数据的依赖。我现在正在使用Doctrine2,它是一个ORM,我想知道我是否应该继续做同样的事情(即更喜欢第二个例子而不是第一个…
问题:
我可以用两种方式编写代码。它的工作原理基本相同。我的问题是——第二个例子中所写的代码是否比第一个例子中的代码更受欢迎?
注释/澄清:
在我的例子中,变量$services
是一个特定于域的变量(而不是ZF2的ServiceLocator
(。即将MyController
视为业务特定"服务"的控制器。
我没有充分利用ZF2的配置、路由器、事件和一切功能。我根据需要在现有的遗留代码库上使用ZF2模块。
当您的控制器具有硬依赖关系时,我建议使用通用ZF2解决方案,方法是创建控制器并在工厂实例中注入依赖关系,然后在'controllers'
配置数组中的'factories'
键下注册控制器。
在您的module.config.php
中
'controllers' => array(
'factories' => array(
'Application'Controller'MyController' => 'Application'Controller'MyControllerFactory'
)
)
在您的控制器中,我会在__construct
方法中设置硬依赖关系。像这样,您可以防止在没有依赖项的情况下实例化控制器(它将引发异常(。
永远不要注入像$services
(如果这是ServiceLocator
(这样的东西,从中提取实际的依赖项,因为不清楚类实际需要什么。对于其他开发人员来说,这将更难理解,也很难测试,因为您无法如此容易地为单个依赖项设置mock。
您的控制器类:
<?php
namespace Application'Controller;
use Doctrine'ORM'EntityManager;
class MyController
{
/**
* @var EntityManager
*/
private $entityManager;
/**
* @param EntityManager $entityManager
*/
public function __construct(EntityManager $entityManager)
{
$this->entityManager = $entityManager;
}
/**
*
*/
function indexAction()
{
//Do stuff
$entityManager = $this->getEntityManager();
}
/**
* @return EntityManager
*/
public function getEntityManager()
{
return $this->entityManager;
}
}
您的工厂:
<?php
namespace Application'Controller;
use Zend'ServiceManager'FactoryInterface;
use Zend'ServiceManager'ServiceLocatorInterface;
use Doctrine'ORM'EntityManager;
class MyControllerFactory implements FactoryInterface
{
/**
* @param ServiceLocatorInterface $serviceLocator
* @return MyController
*/
public function createService(ServiceLocatorInterface $serviceLocator)
{
/** @var EntityManager $entityManager */
$serviceManager = $serviceLocator->getServiceLocator()
$entityManager = $serviceManager->get('doctrine.entitymanager.orm_default');
$myController = new MyController($entityManager);
return $myController;
}
}
ZF2提供了两种不同的方法来解决这个问题。
-
使用
ServiceLocator
通过工厂检索EntityManager
。在
Module.php
中,添加一个匿名函数或Factory。public function getServiceConfig() { return [ 'factories' => [ 'Doctrine'ORM'EntityManager' => function (ServiceManager $sm) { $entityManager = $sm->get('doctrine.entitymanager.orm_default'); return $entityManager; } ], ], }
在您的控制器中
$em = $this->getServiceLocator()->get('Doctrine'ORM'EntityManager');
-
创建一个
Initializer
和AwareInterface
,将EntityManger
注入控制器。CCD_ 25可以被添加到由CCD_ 26初始化的任何类中。
interface EntityManagerAwareInterface { /** * Set EntityManager locator * * @param EntityManager $entityManager */ public function setEntityManager(EntityManager $entityManager); /** * Get service locator * * @return EntityManager */ public function getServiceLocator(); }
当服务由CCD_ 28初始化时,CCD_。如果CCD_ 29是CCD_。
use Application'EntityManager'EntityManagerAwareInterface; use Zend'ServiceManager'InitializerInterface; use Zend'ServiceManager'ServiceLocatorInterface; class EntityManagerInitializer implements InitializerInterface { /** * Initialize * * @param $instance * @param ServiceLocatorInterface $serviceLocator * @return mixed */ public function initialize($instance, ServiceLocatorInterface $serviceLocator) { if ($instance instanceof EntityManagerAwareInterface) { $entityManager = $serviceLocator->get('doctrine.entitymanager.orm_default'); $instance->setEntityManager($entityManager); } } }
接下来将
Initializer
添加到Module.php
public function getServiceConfig() { return [ 'initializers' => [ 'entityManager' => new EntityManagerInitializer(), ], ], }
走Initializer
路线的优点是有一个一次性设置。任何实现EntityManagerAwareInterface
的类都将在初始化该类时注入EntityManager
。