Symfony - 如何访问实体的存储库

Sam*_*igh 11 doctrine design-patterns symfony doctrine-orm symfony-2.3

我们可以通过多种方式访问​​Symfony2控制器或服务中的实体存储库,这些控制器或服务各有优缺点.首先我在这里列出它们然后询问是否有更好的解决方案或者这些是我们唯一的选择,我们应该根据我们的偏好选择一个或一些.我还想知道方法5(我最近开始使用它)是否良好并且不会违反任何规则或有任何副作用.

基本方法:在控制器中使用实体管理器或将其注入服务,然后访问我想要的任何存储库.这是在控制器或服务中访问存储库的基本方法.

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

但是这个方法存在一些问题.第一个问题是我无法按Ctrl +单击例如loadProduct函数并直接执行它的实现(除非有一种我不知道的方法).另一个问题是我将一遍又一遍地重复这部分代码.

方法2:另一种方法是在我的服务或控制器中定义一个getter来访问我的存储库.

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');
    }
}
Run Code Online (Sandbox Code Playgroud)

这个方法解决了第一个问题,不知怎的,第二个问题,但我仍然必须在我的服务或控制器中重复我需要的所有getter,我也会在我的服务和控制器中有几个getter只是为了访问存储库

方法3:另一种方法是为我的服务注入一个存储库,特别是如果我们对我们的代码有一个很好的控制并且我们没有参与将整个Container注入您的服务的其他开发人员,这是很好的.

class DummyService
{
    protected $productRepository;

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

    public function dummyFunction($id)
    {
        $this->productRepository->loadProduct($id);
    }
}
Run Code Online (Sandbox Code Playgroud)

这个方法解决了第一个和第二个问题,但如果我的服务很大并且它需要处理很多存储库,那么向我的服务注入例如10个存储库并不是一个好主意.

方法4:另一种方法是使用服务来包装我的所有存储库并将此服务注入其他服务.

class DummyService
{
    protected $repositoryService;

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

    public function dummyFunction($id)
    {
        $this->repositoryService->getProductRepository()->loadProduct($id);
    }
}
Run Code Online (Sandbox Code Playgroud)

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');
    }
}
Run Code Online (Sandbox Code Playgroud)

该方法还解决了第一和第二个问题.但是,当我们拥有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);
    }
}
Run Code Online (Sandbox Code Playgroud)

我的实体:

/**
 * 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__);
    }   
}
Run Code Online (Sandbox Code Playgroud)

此方法解决了第一个和第二个问题,我也不需要定义访问存储库的服务.我最近用过它,到目前为止它是我最好的方法.我不认为这种方法会破坏实体的规则,因为它在类范围内定义,也是如此.但我仍然不确定它是否有任何副作用.

qoo*_*mao 8

在Doctrine世界中,实体应该是getter和setter(并添加或删除)的贫血模型,因此注入存储库是不对的.

这一切都取决于你想要与Doctrine的结合程度.如果您可以通过该@doctrine服务,那么您可以使用以下内容:

$this->repository = $doctrine->getRepository('CmsBundle:Page');
Run Code Online (Sandbox Code Playgroud)

..但是,如上所述,将要求您将@doctrine服务传递到每个对象.这意味着如果您因任何原因决定不使用Doctrine,您需要重构所有代码以适应您的新方法(无论可能是什么),但这对您来说可能不是问题.此外,存储库将是类型提示,因此无法保证(除了检查它是否是代码中的正确类)以保证它是正确的服务.

在我看来,最干净的方法是创建一个服务,如:

XML

<service id="cms.page_repository"
    class="Acme\CmsBundle\Repository\PageRepository">
    <factory service="doctrine" method="getRepository" />
    <argument>AcmeDemoBundle:ExampleRepository</argument>
</service>
Run Code Online (Sandbox Code Playgroud)

YAML

cms.page_repository:
    class: Acme\CmsBundle\Repository\PageRepository
    factory: [ @doctrine, 'getRepository' ]
Run Code Online (Sandbox Code Playgroud)

..然后,您可以将存储库服务传递到任何您想要的位置,而无需在实际代码中使用doctrine服务.通过这种方法,如果您决定放弃Doctrine,您只需要更改服务定义而不需要重构所有内容.此外,由于您正在创建特定存储库的服务,因此您可以在自己中使用类型提示__construct来保证正在注入的服务如下:

public function __construct(PageRepository $repository)
{
    $this->repository = $repository;
}
Run Code Online (Sandbox Code Playgroud)


Rou*_*ute 2

对我来说,你的建议都不正确。
因为我不明白为什么您需要创建实体的服务。
如果你需要接触这个实体,你唯一需要的就是接触学说。
教义有一个服务(@doctrine)。
您需要在构造中做好准备,以便只能访问该实体。

静态是被遗忘的:

并且您在方法 5 中提交的内容不正确,您的 Product 实体已经通过 ProductRepository 使用 getEntityManager() 方法访问了entityManager。

  • 我通过接口为我的存储库使用服务。它减少了我的代码在确实没有必要时直接与 Doctrine 耦合的数量。服务/对象所需的只是获得一个适合接口的存储库,而不是获取 Doctrine,以获取存储库(也没有进行类型检查,因此可能没有任何自定义方法)。 (2认同)