我必须将数据相关性从控制器中移出(并移入工厂)吗


Must I move data dependency out of my Controllers (and into Factories)?

这个问题可以通过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是类的部分,所以当我认为它不应该是控制器的一部分时,我的类知道实体管理器构造。我是否可以使用FactoryBuilder设计模式来帮助创建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提供了两种不同的方法来解决这个问题。

  1. 使用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');
    
  2. 创建一个InitializerAwareInterface,将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