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

时间:2015-08-12 03:55:44

标签: symfony design-patterns doctrine-orm doctrine symfony-2.3

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

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

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

但是这个方法存在一些问题。第一个问题是我无法按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');
    }
}

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

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

class DummyService
{
    protected $productRepository;

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

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

此方法解决了第一个和第二个问题,但是如果我的服务很大 它需要处理很多存储库然后它不是一个好主意 为我的服务注入例如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');
    }
}

这种方法也解决了第一个和第二个问题。但是当我们拥有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__);
    }   
}

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

3 个答案:

答案 0 :(得分:8)

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

这一切都取决于你想要与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;
}

答案 1 :(得分:2)

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

静态遗忘:

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

答案 2 :(得分:1)

我建议您使用方法4,您的服务将遵循Single Resposability Principe,因为它只做一件事:让您访问所有存储库。

此服务主要作为其他服务的依赖项。对于控制器,我建议您使用相同的辅助函数创建自定义控制器基类。

关于代码重复,特征可能是解决方案。如果您使用&#34;类别&#34;

的特征,即使使用了多种方法