web-dev-qa-db-ja.com

symfony-エンティティのリポジトリにアクセスする方法

Symfony2コントローラーまたはサービスでエンティティのリポジトリにアクセスする方法はいくつかありますが、それぞれに利点と欠点があります。最初にそれらをここにリストしてから、より良い解決策があるかどうか、またはこれらが私たちが持っている唯一のオプションであり、私たちの好みに基づいて1つまたはいくつかを選択する必要があるかどうかを尋ねます。また、方法5(最近使用を開始しました)が有効で、ルールに違反したり副作用が発生したりしないかどうかも知りたいです。

基本的な方法:コントローラでエンティティマネージャを使用するか、サービスに挿入してから、必要なリポジトリにアクセスします。これは、コントローラまたはサービスのリポジトリにアクセスする基本的な方法です。

class DummyController
{
    public function dummyAction($id)
    {
        $em = $this->getDoctrine()->getManager();
        $em->getRepository('ProductBundle:Product')->loadProduct($id);
    }
}

しかし、この方法に関連するいくつかの問題があります。最初の問題は、たとえばloadProduct関数をCtrlキーを押しながらクリックして、その実装に直接移動できないことです(わからない方法がない限り)。もう1つの問題は、コードのこの部分を何度も繰り返してしまうことです。

方法2:他の方法は、私のサービスまたはコントローラーにゲッターを定義して、リポジトリにアクセスすることです。

class DummyService
{
    protected $em;

    public function __construct(EntityManager $em)
    {
        $this->em = $em;
    } 

    public function dummyFunction($id)
    {
        $this->getProductRepository()->loadProduct($id);
    }

    /**
     * @return \ProductBundle\Entity\Repository\ProductRepository
     */
    public function getProductRepository()
    {
        return $this->em->getRepository('ProductBundle:Product');
    }
}

この方法は最初の問題を解決し、どういうわけか2番目の問題を解決しますが、サービスまたはコントローラーで必要なすべてのゲッターを繰り返す必要があります。また、リポジトリにアクセスするためだけに、サービスとコントローラーにいくつかのゲッターを用意します

方法3:もう1つの方法は、サービスにリポジトリを注入することです。コードに適切な制御があり、コンテナー全体をサービスに注入する他の開発者に関与していない場合は特に便利です。 。

class DummyService
{
    protected $productRepository;

    public function __construct(ProductRepository $productRepository)
    {
        $this->productRepository = $productRepository;
    } 

    public function dummyFunction($id)
    {
        $this->productRepository->loadProduct($id);
    }
}

この方法は1番目と2番目の問題を解決しますが、サービスが大きく、多くのリポジトリを処理する必要がある場合、たとえば10のリポジトリをサービスに注入するのは良い考えではありません。

方法4:別の方法は、すべてのリポジトリをラップし、このサービスを他のサービスに注入するサービスを用意することです。

class DummyService
{
    protected $repositoryService;

    public function __construct(RepositoryService $repositoryService)
    {
        $this->repositoryService = $repositoryService;
    } 

    public function dummyFunction($id)
    {
        $this->repositoryService->getProductRepository()->loadProduct($id);
    }
}

RepositoryService:

class RepositoryService
{
    protected $em;

    public function __construct(EntityManager $em)
    {
        $this->em = $em;
    } 

    /**
     * @return \ProductBundle\Entity\Repository\ProductRepository
     */
    public function getProductRepository()
    {
        return $this->em->getRepository('ProductBundle:Product');
    }

    /**
     * @return \CmsBundle\Entity\Repository\PageRepository
     */
    public function getPageRepository()
    {
        return $this->em->getRepository('CmsBundle:Page');
    }
}

この方法は、1番目と2番目の問題も解決します。しかし、たとえば200のエンティティがある場合、RepositoryServiceは非常に大きくなる可能性があります。

メソッド5:最後に、リポジトリを返す各エンティティで静的メソッドを定義できます。

class DummyService
{
    protected $em;

    public function __construct(EntityManager $em)
    {
        $this->em = $em;
    } 

    public function dummyFunction($id)
    {
        Product::getRepository($this->em)->loadProduct($id);
    }
}

私のエンティティ:

/**
 * Product
 *
 * @ORM\Table(name="saman_product")
 * @ORM\Entity(repositoryClass="ProductBundle\Entity\ProductRepository")
 */
class Product
{
    /**
     *
     * @param \Doctrine\ORM\EntityManagerInterface $em
     * @return \ProductBundle\Entity\ProductRepository
     */
    public static function getRepository(EntityManagerInterface $em)
    {
        return $em->getRepository(__CLASS__);
    }   
}

この方法は、1番目と2番目の問題を解決します。リポジトリにアクセスするためのサービスを定義する必要もありません。私は最近それを使用しましたが、これまでのところ、それは私にとって最良の方法です。このメソッドはエンティティのルールに違反しないと思います。クラスのスコープで定義されており、非常に薄いためです。しかし、それでも、それが何か副作用があるかどうかはわかりません。

11
Saman Shafigh

Doctrineの世界では、エンティティはゲッターとセッター(および追加または削除)の貧血モデルであると想定されているため、リポジトリを注入するのは間違っています。

それはすべて、Doctrineにどのように結合したいかに依存します。 @doctrineサービスを渡すことに問題がない場合は、次のようなものを使用できます。

$this->repository = $doctrine->getRepository('CmsBundle:Page');

..しかし、前述のように、@doctrineサービスをすべてのオブジェクトに渡す必要があります。これは、何らかの理由でDoctrineを使用しないことを決定した場合、新しい方法に適合するようにすべてのコードをリファクタリングする必要があることを意味します)が、これはまた、リポジトリはタイプヒントされるため、それがコード内の正しいクラスであるかどうかを確認する以外に、それが正しいサービスであることを保証する保証はありません。

私の意見では、最もクリーンな方法は次のようなサービスを作成することです。

XML

<service id="cms.page_repository"
    class="Acme\CmsBundle\Repository\PageRepository">
    <factory service="doctrine" method="getRepository" />
    <argument>AcmeDemoBundle:ExampleRepository</argument>
</service>

YAML

cms.page_repository:
    class: Acme\CmsBundle\Repository\PageRepository
    factory: [ @doctrine, 'getRepository' ]

..そして、実際のコードでdoctrineサービスを使用する必要なく、必要な場所にリポジトリサービスを渡すことができます。このアプローチでは、Doctrineから離れることを決定した場合、すべてをリファクタリングする必要はなく、サービス定義を変更するだけで済みます。また、特定のリポジトリのサービスを作成しているため、__constructで型ヒントを使用して、正しいサービスを保証できます。次のように注入されます:

public function __construct(PageRepository $repository)
{
    $this->repository = $repository;
}
8
qooplmao

私にとって、あなたの提案はどれも正しくありません。
エンティティのサービスを作成する必要がある理由がわかりません。
このエンティティにアクセスする必要がある場合、必要なのは教義にアクセスすることだけです。
そしてdoctrineはサービス(@doctrine)を持っています。
そのエンティティにのみアクセスできるように構成で準備するのはあなた次第です。

静力学は忘れられるべきです:

また、メソッド5で送信する内容が正しくありません。Productエンティティは、getEntityManager()メソッドを使用してProductRepository経由ですでにentityManagerにアクセスしています。

2
Roukmoute

方法4を使用することをお勧めします。サービスは単一の責任を負うため、すべてのリポジトリへのアクセスを提供します。

このサービスは、主に他のサービスの依存関係になります。コントローラーについては、同じヘルパー関数を使用してカスタムコントローラーの基本クラスを作成することをお勧めします。

コードの重複については、トレイトが解決策になるかもしれません。 「カテゴリ」による特性を使用する場合、メソッド数が多い場合でも

1
Yassine Guedidi