【问题标题】:Symfony 2: Creating a service from a RepositorySymfony 2:从存储库创建服务
【发布时间】:2013-06-21 05:42:40
【问题描述】:

我正在学习 Symfony,我一直在尝试使用存储库创建服务。 我已经从 generate:entity 创建了我的存储库和实体,所以它们应该没问题。

到目前为止,我在 services.yml 中得到的是:

parameters:
    mytest.entity: TestTestBundle:Brand
    mytest.class:  Test\TestBundle\Entity\Brand
    default_repository.class: Doctrine\ORM\EntityRepository

services:
     myservice:
          class: %default_repository.class%
          factory-service: doctrine.orm.default_entity_manager
          factory-method: getRepository
          arguments:
            - %mytest.entity%

但是当我尝试调用服务时,我得到了这个错误:

Catchable Fatal Error: Argument 2 passed to Doctrine\ORM\EntityRepository::__construct() must be an instance of Doctrine\ORM\Mapping\ClassMetadata, none given, called in 

然后我尝试仅使用实体来创建服务。我的 services.yml 看起来像:

services:
     myservice:
          class: %mytest.class%
          factory-service: doctrine.orm.default_entity_manager
          factory-method: getRepository
          arguments:
            - %mytest.entity%

但为此,我得到:

Error: Call to undefined method 
                Test\TestBundle\Entity\Brand::findAll

有人知道我做错了什么吗?

谢谢

【问题讨论】:

    标签: symfony service doctrine-orm repository


    【解决方案1】:

    弃用警告:不再有factory_servicefactory_method。从 Symfony 2.6 开始,您应该这样做(对于 Symfony 3.3+,请查看下方):

    parameters:
        entity.my_entity: "AppBundle:MyEntity"
    
    services:
        my_entity_repository:
            class: AppBundle\Repository\MyEntityRepository
            factory: ["@doctrine", getRepository]
            arguments:
                - %entity.my_entity%
    

    Symfony 2.6 中引入了新的setFactory() 方法。 2.6 之前的工厂语法请参考旧版本。

    http://symfony.com/doc/2.7/service_container/factories.html

    编辑:看起来他们一直在改变这一点,所以从 Symfony 3.3 开始有了新的语法:

    # app/config/services.yml
    services:
        # ...
    
        AppBundle\Email\NewsletterManager:
            # call the static method
            factory: ['AppBundle\Email\NewsletterManagerStaticFactory', createNewsletterManager]
    

    查看:http://symfony.com/doc/3.3/service_container/factories.html

    【讨论】:

    • 这很好用,tnx。但我想更进一步:如果我还想向这个存储库注入一个服务(比如说一个 Paginator 服务)怎么办?我尝试向它添加更多参数,但这不起作用,因为 Symfony 并没有“期望”我给它的任何参数。
    • @userfuser 您是否尝试过覆盖构造函数?您可以在那里添加所有新的依赖项,只需记住调用 parent::__construct($arg) 以提供 Doctrine 期望的内容。
    • @FrancescoCasula 我确实想到了,但我还没有准备好对所有 repos 进行全局更改,因为这种注入仅在少数情况下需要。最终,我通过使用 setter 注入来解决这个特定的情况,就像在文档中定义的那样:symfony.com/doc/current/service_container/…
    【解决方案2】:

    这是我们在 KnpRadBundle 中的做法:https://github.com/KnpLabs/KnpRadBundle/blob/develop/DependencyInjection/Definition/DoctrineRepositoryFactory.php#L9

    最后应该是:

    my_service:
        class: Doctrine\Common\Persistence\ObjectRepository
        factory_service: doctrine # this is an instance of Registry
        factory_method: getRepository
        arguments: [ %mytest.entity% ]
    

    更新

    从 2.4 开始,学说允许覆盖默认存储库工厂。

    这是在 symfony 中实现它的一种可能方式:https://gist.github.com/docteurklein/9778800

    【讨论】:

    • 谢谢,如果 Doctrine\Common\Persistence\ObjectRepository,你如何实例化它? factory-service: 教义 # 这是 Registry 的一个实例,你的意思是,它是教义.orm.default_entity_manager ?
    • 不,它是 github.com/doctrine/common/blob/master/lib/Doctrine/Common/… 的实现。您可以对 EntityManager 实例执行相同的操作。事实上,class: 在这里并不重要,因为它是一个 工厂 服务,容器本身不会实例化给定的类,而是要求服务的方法来实现。
    • 但是class: Doctrine\Common\Persistence\ObjectRepository也没有错,因为返回的实例确实是ObjectRepository接口的实现
    • 谢谢弗洛里安。不,它没有,或者我做错了什么,但正如我所说:FatalErrorException:错误:无法在中实例化接口 Doctrine\Common\Persistence\ObjectRepository
    • 哈哈!我知道了,我想 :) 它应该是 factory_service 而不是 factory-service。注意_
    【解决方案3】:

    您可能使用了错误的 YAML 密钥。你的第一个配置对我来说很好用

    • factory_service 而不是 factory-service
    • factory_method 代替 factory-method

    【讨论】:

    【解决方案4】:

    自 2017 年和 Symfony 3.3+ 以来,现在这更容易了。

    注意:尽量避免使用像 generate:entity 这样的通用命令。它们是为初学者设计的,可以使项目快速工作。他们往往会暴露不良做法并且需要很长时间才能改变。

    查看我的帖子 How to use Repository with Doctrine as Service in Symfony 以获得更一般的描述。

    到你的代码:

    1。更新您的配置注册以使用PSR-4 based autoregistration

    # app/config/services.yml
    services:
        _defaults:
            autowire: true
    
        Test\TestBundle\:
            resource: ../../src/Test/TestBundle
    

    2。组合优于继承 - 创建自己的存储库而不直接依赖于 Doctrine

    <?php
    
    namespace Test\TestBundle\Repository;
    
    use Doctrine\ORM\EntityManagerInterface;
    
    class BrandRepository
    {
        private $repository;
    
        public function __construct(EntityManagerInterface $entityManager)
        {
            $this->repository = $entityManager->getRepository(Brand::class);
        }
    
        public function findAll()
        {
            return $this->repository->findAll();
        }
    }
    

    3。通过构造函数注入在任何服务或控制器中使用

    use Test\TestBundle\Repository\BrandRepository;
    
    class MyController
    {
        /**
         * @var BrandRepository
         */
        private $brandRepository;
    
        public function __construct(BrandRepository $brandRepository)
        {
            $this->brandRepository = $brandRepository;
        }
    
        public function someAction()
        {
            $allBrands = $this->brandRepository->findAll();
            // ...
        }
    
    }
    

    【讨论】:

    • 这确实通过自动装配实例化了一个服务,但该服务不是存储库本身,这并不能解决问题。我认为通过将服务重命名为Test\TestBundle\Entity\Brand、删除classfactory_servicefactory_method 配置并添加factory: 'Doctrine\ORM\EntityManagerInterface:getRepository',原始配置将与自动装配一起使用。也许引用包装论点。我对当前具有类似配置的项目仍有很多重构,所以我还不能说绝对,但我至少可以清除缓存。
    • 另外一件事——我也在定义Doctrine\ORM\EntityManagerInterface: { alias: doctrine.orm.default_entity_manager }
    【解决方案5】:

    我将 service.yml 转换为 service.xml,并更新 DependencyInjection Extension,一切都为我工作。我不知道为什么,但是 yml 配置会抛出 Catchable Fatal Error。您可以尝试使用 xml config 进行服务配置。

    service.yml:

    services:
        acme.demo.apikey_userprovider:
            class: Acme\DemoBundle\Entity\UserinfoRepository
            factory-service: doctrine.orm.entity_manager
            factory-method: getRepository
            arguments: [ AcmeDemoBundle:Userinfo ]
    
        acme.demo.apikey_authenticator:
            class: Acme\DemoBundle\Security\ApiKeyAuthenticator
            arguments: [ "@acme.demo.apikey_userprovider" ]
    

    service.xml:

    <services>
        <service id="acme.demo.apikey_userprovider" class="Acme\DemoBundle\Entity\UserinfoRepository"  factory-service="doctrine.orm.entity_manager" factory-method="getRepository">
            <argument>AcmeDemoBundle:Userinfo</argument>
        </service>
    
        <service id="acme.demo.apikey_authenticator" class="Acme\DemoBundle\Security\ApiKeyAuthenticator">
            <argument type="service" id="acme.demo.apikey_userprovider" />
        </service>
    </services>
    

    【讨论】:

    • 你的 yml 键错误,这个factory-service 应该是factory_service(下划线)。
    【解决方案6】:

    Symfony 3.3doctrine-bundle 1.8 有一个 Doctrine\Bundle\DoctrineBundle\Repository\ContainerRepositoryFactory 这有助于将存储库创建为服务。

    示例

    我们想要什么

    $rep = $kernel->getContainer()
        ->get('doctrine.orm.entity_manager')
        ->getRepository(Brand::class);
    

    ORM 描述

    # Brand.orm.yaml
    ...
    repositoryClass: App\Repository\BrandRepository
    ...
    

    服务说明

    # service.yaml
    
    App\Repository\BrandRepository:
        arguments:
          - '@doctrine.orm.entity_manager'
          - '@=service("doctrine.orm.entity_manager").getClassMetadata("App\\Entity\\Brand")'
        tags:
            - { name: doctrine.repository_service }
        calls:
            - method: setDefaultLocale
              arguments:
                  - '%kernel.default_locale%'
            - method: setRequestStack
              arguments:
                  - '@request_stack'
    

    【讨论】:

      【解决方案7】:

      sf 2.6+

      parameters:
          mytest.entity: TestTestBundle:Brand
          mytest.class:  Test\TestBundle\Entity\Brand
          default_repository.class: Doctrine\ORM\EntityRepository
      
      services:
           myservice:
                class: %default_repository.class%
                factory: ["@doctrine.orm.default_entity_manager", "getRepository"]
                arguments:
                  - %mytest.entity%
      

      【讨论】:

      • 欢迎来到 SO。我认为如果您能添加一些解释为什么这可以解决问题,我们将不胜感激。
      • symfony.com/doc/2.6/components/dependency_injection/… Symfony\Component\DependencyInjection\Definition::setFactoryMethod(getRepository) 自 2.6 版起已弃用,并将在 3.0 版中删除。使用定义::setFactory()
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2017-12-05
      • 1970-01-01
      • 2018-08-15
      • 1970-01-01
      • 2019-02-14
      • 2012-07-06
      • 2012-06-18
      相关资源
      最近更新 更多