【问题标题】:zf2 + doctrine architecture. How to use object manager?zf2+学说架构。如何使用对象管理器?
【发布时间】:2014-04-10 07:13:09
【问题描述】:

使用 Doctrine ObjectManager 的最佳方式是什么?我将它注入到 module.config.php 的控制器中

'Telecom\Controller\Users' => function($sm){
    $ctr = new Telecom\Controller\UsersController();
    $ctr->setEntityManager(
               $sm->getServiceLocator()
                    ->get('Doctrine\ORM\EntityManager')
           );

    return $ctr;
},

然后我在我的控制器中使用它如下

$this->getEntityManager()->persist($entity);
$this->getEntityManager()->flush();

但是 Marco Pivetta(Doctrine 团队,zf2 贡献者)教导“如果你在控制器中注入 objectmanager,你的架构就会很糟糕”http://marco-pivetta.com/doctrine-orm-zf2-tutorial/#/39/11

所以请帮助我,使用实体管理器的最佳架构方式是什么。我应该使用像我自己的服务这样的另一层来处理实体管理器吗?

【问题讨论】:

    标签: architecture doctrine-orm zend-framework2


    【解决方案1】:

    如果您在控制器中有实体管理器,您将引入“域逻辑”(您的数据库查询)的耦合,它们与应用程序逻辑密不可分(控制器应该只读取请求并返回正确的响应)。这种耦合使得重用和维护代码变得相当困难。

    一种解决方案是创建注入控制器的“服务”。服务封装业务逻辑(例如数据库查询)并为控制器提供定义良好的 API。好消息是,如果业务逻辑在任何时候发生变化(它总是会发生变化);您只需要更改上述服务的实现,控制器将继续运行。

    ZF2 非常灵活,有很多方法可以完成相同的任务。我个人执行以下操作:

    服务

    服务并不意味着只封装一个实体;它们应该封装执行该特定任务所需的所有实体。这完全取决于服务试图做什么。例如,更复杂的服务可能需要其他服务。

    在我的实现中,我有一个名为AbstractEntityService 的抽象类,该类被扩展为所有需要持久性的服务(任何需要数据库的东西)。

    这里的课程很长,但关键部分如下。

    abstract class AbstractEntityService 
      extends Service\AbstractService 
      implements EntityMapperAwareInterface, FormProviderInterface
    { 
    
    public function __construct(
      EntityMapper $entityMapper, 
      FormElementManager $formElementManager)
    {
        $this->entityMapper = $entityMapper;
        $this->formElementManager = $formElementManager;
    
        $this->getEventManager()->addIdentifiers(array(__CLASS__));
    
        parent::__construct();
    }
    
    public function getForm($name)
    {
       return $this->formElementManager->get($name);
    }
    
    public function create(Entity\EntityInterface $entity)
    {
        $this->triggerEvent(static::EVENT_CREATE, array('entity' => $entity));
    
        $entity = $this->entityMapper->insert($entity);
    
        $this->triggerEvent(static::EVENT_CREATE_POST, array('entity' => $entity));
    
        return $entity;
    }
    

    注意 EntityMapperFormElementManager 已注入 - 此处没有 ObjectManagerServiceLocator

    实体映射器

    只是对象管理器周围的一个薄层;这允许我们将EntityManager 换成DocumentManager,例如,如果我们从MySQL 换成MongoDB

    interface MapperInterface
    {
       public function getObjectManager();
    
       public function setObjectManager(ObjectManager $objectManager);
    
       public function getRepository($className);
    
       public function insert(EntityInterface $entity);
    
       public function save(EntityInterface $entity);
    
       public function delete(EntityInterface $entity);
    
       public function flush();
    
     }
    

    因此,具体服务的示例是:

    class ListService extends AbstractEntityService
    {
        public function __construct(
          EntityMapper $entityMapper,
          FormElementManager $formElementManager,
          ListRepository $listRepository
        ){
          parent::__construct($entityMapper, $formElementManager);
          $this->listRepository = $listRepository;
      }
    
      protected function init(EventManagerInterface $eventManager){
        parent::init($eventManager);
        $eventManager->attach(static::EVENT_CREATE, array($this, 'createList'));
      }
    
      public function createList(EventInterface $event)
      {
        $list = $event->getParam('entity');
    
        if (! $list instanceof Entity\ValueList) return;
        $name =  $list->getName();
        if (empty($name)) {
            $name = $this->formatName($list->getTitle());
            $list->setName($name);
        }
       }
    

    列表控制器

    然后控制器简单地使用服务(在上面的例子中它是一个“ListService”)

    class ListController extends AbstractActionController {
    
    public function __construct(
        ListService $listService
    ){
        $this->listService = $listService;
    }
    
    public function createAction(){
       // validate request/form data...
       $form = $this->listService->getListCreateForm();
    
       $list = $this->listService->create($form->getData());
    
       // return view...
    }
    

    哇;比计划的时间长一点,但希望对您有所帮助。

    【讨论】:

    • 谢谢,让我考虑一下。
    • 上面的文字很清楚,但代码 - 不是。你为什么使用事件?很难理解有很多事件的代码。它们对建筑有什么好处?为什么要通过__construct而不是setter注入?
    • @shukshin.ivan AbstractEntityService 中的事件允许任何扩展服务类重用create 方法;无需修改/重载它。如果我想为任何实体服务做一些特定的事情(比如在列表服务中),我可以在init() 方法中附加一个事件,就像我为createList(EventInterface $event) 所做的那样。我注入构造函数(dependency injection),因为该类具有 hard 依赖项而不是 soft 依赖项(这将是您应该使用 setter 的时候)。
    • 嗨,亚历克斯。您在网络上的某个地方有更完善的代码示例吗?
    • 不错的答案!我有一个疑问,我应该为每个模块的控制器创建一项服务,还是应该在所有控制器中使用一项服务注入?
    【解决方案2】:

    你可以为每个模块做这样的事情:

    | | - App
    | | | - Controller
    | | | | -IndexController.php
    | | | - Entity
    | | | | - User.php
    | | | - Factory
    | | | - Service
    | | | | - UserService.php
    

    在这种架构中,您必须在 Service 的类中注入您的实体管理器,以便您的服务必须实现 ServiceManagerAwareInterface。

    use Zend\ServiceManager\ServiceManagerAwareInterface;
    
    class UsersService implements ServiceManagerAwareInterface {
    
        protected $sm;
    /**
     * Retrouver l'entityManager voulue
     * @var [Object EntityManager]
     */
    protected $em;
    
    public function setServiceManager(ServiceManager $serviceManager)
    {
        $this->sm = $serviceManager;
        return $this;
    }
    
    /*
     * Retrieve service manager instance
     *
     * @return ServiceManager
     */
    public function getServiceManager()
    {
        return $this->sm;
    }
    
    // do your crazy stuff here
    ...
    }
    

    您的实体(如 User)在此示例中具有 UserService。 而 User 是 Doctrine 的映射类。

    您的架构从低级到高级如下所示: 模型(实体)

    编辑:

    'invokables' => array(
            'UsersService' => 'Application\Service\UsersService',
        ),
    

    您可以在 module.config.php 中注入每个服务是的。 如果你有任何问题我听。 (对不起我的英语)我希望我很清楚

    【讨论】:

    • 所以,每个Entity都应该有自己的Service,通过在module.config.php中注入来获取entitymanager,对吧?但是有另一个程序层的原因是什么?为什么 EntityManager 还不够?如果需要,要更改 ORM 层?
    • 原因是:尊重 MVC,控制器不是为与 DAO 和服务层交互而设计的,另外,在 zf3 中几乎计划删除控制器中对 SM 的访问。 github.com/zendframework/zf2/issues/5168 这是一个不好的做法,为了简化教程,每个演示都是在控制器中完成的,这不是我们必须做的。 EntityManager 就足够了,服务管理器在服务层获取 entityManager。就是这样。
    • 这个链接帮助我理解了所有这些东西:blog.astrumfutura.com/2008/12/…我建议你阅读它,它真的很好
    • 谢谢,我完全忘记了,厚控制器 = 糟糕的架构
    • 另一个答案中的 AbstractService 和你的 ServiceManagerAwareInterface 有什么区别?我对很多服务类有点困惑。
    猜你喜欢
    • 1970-01-01
    • 2023-03-24
    • 2013-08-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多