【问题标题】:Injecting the Service Manager to Build a Doctrine Repository in ZF2在 ZF2 中注入 Service Manager 以构建 Doctrine 存储库
【发布时间】:2012-12-31 14:42:13
【问题描述】:

如何将服务管理器注入 Doctrine 存储库以允许我检索 Doctrine 实体管理器?

我使用 ZF2-Commons DoctrineORMModule 并尝试实现 Doctrine Tutorial 中列出的存储库示例(下面链接中的教程底部):

http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/tutorials/getting-started.html

但是,我不断收到一条消息“致命错误:在 C:\zendProject\zf2 中的非对象上调用成员函数 get() ...”,这表明我没有服务定位器的工作实例。

我的 Doctrine 存储库如下所示:

namespace Calendar\Repository;

use  Doctrine\ORM\EntityRepository,
     Calendar\Entity\Appointment,
     Calendar\Entity\Diary;

use Zend\ServiceManager\ServiceLocatorAwareInterface;
use Zend\ServiceManager\ServiceLocatorInterface;

class ApptRepository extends EntityRepository implements ServiceLocatorAwareInterface 
{
   protected $services;

   public function setServiceLocator(ServiceLocatorInterface $serviceLocator)
   {
       $this->services = $serviceLocator;
   }

   public function getServiceLocator()
   {
        return $this->services;
   }

  public function getUserApptsByDate()
  {
     $dql = "SELECT a FROM Appointment a";

     $em = $this->getServiceLocator()->get('Doctrine\ORM\EntityManager');

     $query = $em()->createQuery($dql);

     return $query->getResult();
   }
}

然后我想在我的控制器中使用以下模式调用它:

$diary = $em->getRepository('Calendar\Entity\Appointment')->getUserApptsByDate();

编辑:附加的链接表明我可能需要将类转换为服务,
https://stackoverflow.com/a/13508799/1325365

但是,如果这是最好的路线,那么我将如何让我的 Doctrine Entity 了解该服务。目前,我在 doc 块中包含一个指向该类的注释。

@ORM\Entity (repositoryClass="Calendar\Repository\ApptRepository") 

【问题讨论】:

    标签: service dependency-injection doctrine zend-framework2


    【解决方案1】:

    我处理事情的方式是这样的:

    首先我为每个实体注册一个服务。这是在 Module.php

    中完成的
    public function getServiceConfig()
    {
        return array(
            'factories' => array(
                'my-service-entityname' => 'My\Factory\EntitynameServiceFactory',
            )
        );
    }
    

    接下来是创建工厂类src\My\Factory\EntitynameServiceFactory.php。这是您将 EntityManager 注入 Entity-Services 的部分(而不是注入实体本身,实体根本不需要这种依赖关系)

    这个类看起来像这样:

    <?php
    namespace My\Factory;
    
    use Zend\ServiceManager\ServiceLocatorInterface;
    use Zend\ServiceManager\FactoryInterface;
    use My\Service\EntitynameService;
    
    class EntitynameServiceFactory implements FactoryInterface
    {
        public function createService(ServiceLocatorInterface $serviceLocator)
        {
            $service = new EntitynameService();
            $service->setEntityManager($serviceLocator->get('Doctrine\ORM\EntityManager'));
            return $service;
        }
    }
    

    接下来要做的是创建 src\My\Service\EntitynameService.php。这实际上是您创建所有 getter 函数和东西的部分。我个人从全局 DoctrineEntityService 扩展这些服务,我现在首先为您提供 EntitynameService 的代码。所有这些都是为了真正获得正确的存储库!

    <?php
    namespace My\Service;
    
    class EntitynameService extends DoctrineEntityService
    {
        public function getEntityRepository()
        {
            if (null === $this->entityRepository) {
                $this->setEntityRepository($this->getEntityManager()->getRepository('My\Entity\Entityname'));
            }
            return $this->entityRepository;
        }
    }
    

    到这里为止的这部分应该很容易理解(我希望),但这还不是很有趣。神奇的事情发生在全局 DoctrineEntityService。这就是它的代码!

    <?php
    namespace My\Service;
    
    use Zend\EventManager\EventManagerAwareInterface;
    use Zend\EventManager\EventManagerInterface;
    use Zend\ServiceManager\ServiceManagerAwareInterface;
    use Zend\ServiceManager\ServiceManager;
    use Doctrine\ORM\EntityManager;
    use Doctrine\ORM\EntityRepository;
    
    class DoctrineEntityService implements
        ServiceManagerAwareInterface,
        EventManagerAwareInterface
    {
        protected $serviceManager;
        protected $eventManager;
        protected $entityManager;
        protected $entityRepository;
    
    
        /**
         * Returns all Entities
         *
         * @return EntityRepository
         */
        public function findAll()
        {
            $this->getEventManager()->trigger(__FUNCTION__ . '.pre', $this, array('entities' => $entities));
            $entities = $this->getEntityRepository()->findAll();
            $this->getEventManager()->trigger(__FUNCTION__ . '.post', $this, array('entities' => $entities));
            return $entities;
        }
    
        public function find($id) {
            return $this->getEntityRepository()->find($id);
        }
    
        public function findByQuery(\Closure $query)
        {
            $queryBuilder = $this->getEntityRepository()->createQueryBuilder('entity');
            $currentQuery = call_user_func($query, $queryBuilder);
           // \Zend\Debug\Debug::dump($currentQuery->getQuery());
            return $currentQuery->getQuery()->getResult();
        }
    
        /**
         * Persists and Entity into the Repository
         *
         * @param Entity $entity
         * @return Entity
         */
        public function persist($entity)
        {
            $this->getEventManager()->trigger(__FUNCTION__ . '.pre', $this, array('entity'=>$entity));
            $this->getEntityManager()->persist($entity);
            $this->getEntityManager()->flush();
            $this->getEventManager()->trigger(__FUNCTION__ . '.post', $this, array('entity'=>$entity));
    
            return $entity;
        }
    
        /**
         * @param \Doctrine\ORM\EntityRepository $entityRepository
         * @return \Haushaltportal\Service\DoctrineEntityService
         */
        public function setEntityRepository(EntityRepository $entityRepository)
        {
            $this->entityRepository = $entityRepository;
            return $this;
        }
    
        /**
         * @param EntityManager $entityManager
         * @return \Haushaltportal\Service\DoctrineEntityService
         */
        public function setEntityManager(EntityManager $entityManager)
        {
            $this->entityManager = $entityManager;
            return $this;
        }
    
        /**
         * @return EntityManager
         */
        public function getEntityManager()
        {
            return $this->entityManager;
        }
    
        /**
         * Inject an EventManager instance
         *
         * @param  EventManagerInterface $eventManager
         * @return \Haushaltportal\Service\DoctrineEntityService
         */
        public function setEventManager(EventManagerInterface $eventManager)
        {
            $this->eventManager = $eventManager;
            return $this;
        }
    
        /**
         * Retrieve the event manager
         * Lazy-loads an EventManager instance if none registered.
         *
         * @return EventManagerInterface
         */
        public function getEventManager()
        {
            return $this->eventManager;
        }
    
        /**
         * Set service manager
         *
         * @param ServiceManager $serviceManager
         * @return \Haushaltportal\Service\DoctrineEntityService
         */
        public function setServiceManager(ServiceManager $serviceManager)
        {
            $this->serviceManager = $serviceManager;
            return $this;
        }
    
        /**
         * Get service manager
         *
         * @return ServiceManager
         */
        public function getServiceManager()
        {
            return $this->serviceManager;
        }
    }
    

    那么这有什么作用呢?这个 DoctrineEntityService 几乎就是您在全球范围内所需要的(根据我目前的经验)。它有fincAll()find($id)findByQuery($closure)

    您的下一个问题(希望如此)只会是“现在如何从我的控制器中使用它?”。就像调用您在第一步中设置的服务一样简单!假设您的 Controllers

    中有此代码
    public function someAction()
    {
        /** @var $entityService \my\Service\EntitynameService */
        $entityService = $this->getServiceLocator()->get('my-service-entityname');
    
        // A query that finds all stuff
        $allEntities = $entityService->findAll();
    
        // A query that finds an ID 
        $idEntity = $entityService->find(1);
    
        // A query that finds entities based on a Query
        $queryEntity = $entityService->findByQuery(function($queryBuilder){
            /** @var $queryBuilder\Doctrine\DBAL\Query\QueryBuilder */
            return $queryBuilder->orderBy('entity.somekey', 'ASC'); 
        });
    }
    

    函数findByQuery() 需要一个闭包。 $queryBuilder(或者您可以选择如何命名该变量)将是\Doctrine\DBAL\Query\QueryBuilder 的一个实例。不过,这将始终与ONE Repository 相关联!因此,entity.somekeyentity. 将是您当前正在使用的任何存储库。

    如果您需要访问EntityManager,您要么只实例化DoctrineEntityService,要么调用$entityService-&gt;getEntityManager() 并从那里继续。

    我不知道这种方法是否过于复杂。在设置新的 Entity/EntityRepository 时,您需要做的就是添加一个新的工厂和一个新的服务。这两个几乎都是复制粘贴,在每个类中更改了两行代码。

    我希望这已经回答了您的问题,并让您对如何组织 ZF2 的工作有所了解。

    【讨论】:

    • Sam - 这需要一些时间才能解决,但这是一个非常好的答案 - 非常感谢。一个问题是 EventManager 触发器调用在 DoctrineEntityService 持久化方法中做了什么?我可以使用这些事件来触发其他操作吗?你用它们做什么?
    • 遗憾的是我什至无法回答这个问题。好吧,我可以,他们触发一个名称没有附加任何操作的事件。所以基本上什么都没有发生。这是剩下的复制粘贴...您可能可以将 CacheHandler 附加到这些事件或其他东西上。如果我没记错的话,我认为这在 ZF2s QuickStart 教程中的某个地方有所介绍
    • 这个答案似乎有点矫枉过正。不需要DoctrineEntityService 或任何此类东西。只需从 zf2 ServiceManager 获取教义 EntityManager(或将其注入工厂)并使用 `$entityManager->getRepository('MyEntity')。更简单、更快,这就是 Doctrine 的用途。
    • @superdweebie Service 所做的只是提供全局功能,如findAll()persist() 等。当然,您可以从 EntityManager 本身完成这一切,但我认为拥有一些基本功能封装在一个基类中是一件好事。此外,这个 entityService 只写一次。所有子实体 - 如上面的代码示例所示,只有几行。
    • @sam 我理解你想要做什么,但这不是教义的本意。很久以前,我为 DoctrineMongoODMModule 公关了一个类似的东西,并被轻轻拒绝,并解释了一些东西。 Doctrine 本身可以提供对 Repositories 上的 find() 等方法的更直接访问,但它并不是为了促进良好的关注点分离和清晰的架构 - 所以很清楚哪些代码做什么。调用 -&gt;getRepository 并不费力,也不会像您的解决方案那样混淆 Doctrine 约定。
    【解决方案2】:

    只要您扩展 Doctrine\ORM\EntityRepository,您就可以通过调用 EntityRepository::getEntityManager()$_em 属性立即访问实体管理器。从 Doctrine\ORM\EntityRepository 类的继承允许您这样做。

    您的方法现在应该如下所示:

    public function getUserApptsByDate()
    {
        $dql = "SELECT a FROM Appointment a";
        $em = $this->getEntityManager();// Or $em=$this->_em;
        $query = $em()->createQuery($dql);
        return $query->getResult();
    }
    

    我始终牢记,对我的数据的访问应该从 Web 前端(Zend MVC、服务管理器)到持久层(Doctrine)。我的持久性(实体、存储库...)层不应该引用网络前端,也不应该知道它的存在。如果我的系统在某种程度上做相反的事情,那么很可能我做错了什么。

    新年快乐

    【讨论】:

    • 感谢这是一个非常简单的解决方案。我没有考虑尝试直接访问 $em。考虑到这些调用将在许多模块中使用,我已经将这组数据库调用作为服务包含在内。我仍在使用代码,但这应该允许我使用标准的学说代码格式简化代码。
    • 为@yechabbi +1。我想在第 54 行添加 a link to the doctrine entity repository,您可以在其中看到 entityManager 为 $_em
    猜你喜欢
    • 2013-07-29
    • 2014-10-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-08-16
    • 1970-01-01
    相关资源
    最近更新 更多