0

Doctrine Entity Manager を取得できるようにするために、サービスマネージャーを Doctrine リポジトリに挿入するにはどうすればよいですか?

私は ZF2-Commons DoctrineORMModule を使用しており、Doctrine チュートリアル (以下のリンクのチュートリアルの下部) にリストされているリポジトリの例を実装しようとしています:

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

ただし、「Fatal error: Call to a member function get() on a non-object in C:\zendProject\zf2 ...」というメッセージが表示され続けます。これは、サービス ロケーターの有効なインスタンスがないことを示しています。 .

私の 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 エンティティにサービスを認識させるにはどうすればよいでしょうか。現時点では、クラスを指す doc ブロックに注釈を含めています。

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

2 に答える 2

6

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、Service Manager) から永続化レイヤー (Doctrine) に行く必要があることを常に心に留めています。私の永続性 (エンティティ、リポジトリ...) レイヤーは、Web フロントを参照したり、その存在を認識したりしてはなりません。私のシステムがあるレベルで逆のことをしている場合、おそらく私は何か間違ったことをしています。

ハッピーエンド

于 2012-12-31T17:45:59.120 に答える
6

私が物事にアプローチする方法は次のとおりです。

まず、エンティティごとにサービスを登録します。これは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)

あなたの次の質問は (願わくば) 「今、コントローラーからこれを使用する方法は?」 だけです。最初のステップで設定した Service を呼び出すのと同じくらい簡単です。コントローラーでこのコードを想定します

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->getEntityManager()そこから続行します。

このアプローチが過度に複雑かどうかはわかりません。新しい Entity/EntityRepository を設定するときは、新しい Factory と新しい Service を追加するだけです。これらは両方とも、各クラスのコードを 2 行変更したコピー ペーストです。

これがあなたの質問に答え、ZF2 での作業をどのように整理するかについての洞察を与えてくれたことを願っています。

于 2012-12-31T15:31:17.700 に答える