How to inject a repository into a service in Symfony?

前端 未结 5 1617
佛祖请我去吃肉
佛祖请我去吃肉 2020-11-30 21:39

I need to inject two objects into ImageService. One of them is an instance of Repository/ImageRepository, which I get like this:

$         


        
相关标签:
5条回答
  • 2020-11-30 22:15

    I found this link and this worked for me:

    parameters:
        image_repository.class:            Mycompany\MainBundle\Repository\ImageRepository
        image_repository.factory_argument: 'MycompanyMainBundle:Image'
        image_manager.class:               Mycompany\MainBundle\Service\Image\ImageManager
        image_manipulator.class:           Mycompany\MainBundle\Service\Image\ImageManipulator
    
    services:
        image_manager:
            class: %image_manager.class%
            arguments:
              - @image_manipulator
              - @image_repository
    
        image_repository:
            class:           %image_repository.class%
            factory_service: doctrine.odm.mongodb
            factory_method:  getRepository
            arguments:
                - %image_repository.factory_argument%
    
        image_manipulator:
            class: %image_manipulator.class%
    
    0 讨论(0)
  • 2020-11-30 22:17

    Here is a cleaned up solution for those coming from Google like me:

    Update: here is the Symfony 2.6 (and up) solution:

    services:
    
        myrepository:
            class: Doctrine\ORM\EntityRepository
            factory: ["@doctrine.orm.entity_manager", getRepository]
            arguments:
                - MyBundle\Entity\MyClass
    
        myservice:
            class: MyBundle\Service\MyService
            arguments:
                - "@myrepository"
    

    Deprecated solution (Symfony 2.5 and less):

    services:
    
        myrepository:
            class: Doctrine\ORM\EntityRepository
            factory_service: doctrine.orm.entity_manager
            factory_method: getRepository
            arguments:
                - MyBundle\Entity\MyClass
    
        myservice:
            class: MyBundle\Service\MyService
            arguments:
                - "@myrepository"
    
    0 讨论(0)
  • 2020-11-30 22:23

    In case if do not want to define each repository as a service, starting from version 2.4 you can do following, (default is a name of the entity manager):

    @=service('doctrine.orm.default_entity_manager').getRepository('MycompanyMainBundle:Image')
    
    0 讨论(0)
  • 2020-11-30 22:27

    Symfony 3.3, 4 and 5 makes this much simpler.

    Check my post How to use Repository with Doctrine as Service in Symfony for more general description.

    To your code, all you need to do is use composition over inheritance - one of SOLID patterns.

    1. Create own repository without direct dependency on Doctrine

    <?php
    
    namespace MycompanyMainBundle\Repository;
    
    use Doctrine\ORM\EntityManagerInterface;
    use MycompanyMainBundle\Entity\Image;
    
    class ImageRepository
    {
        private $repository;
    
        public function __construct(EntityManagerInterface $entityManager)
        {
            $this->repository = $entityManager->getRepository(Image::class);
        }
    
        // add desired methods here
        public function findAll()
        {
            return $this->repository->findAll();
        }
    }
    

    2. Add config registration with PSR-4 based autoregistration

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

    3. Now you can add any dependency anywhere via constructor injection

    use MycompanyMainBundle\Repository\ImageRepository;
    
    class ImageService
    {
        public function __construct(ImageRepository $imageRepository)
        {
            $this->imageRepository = $imageRepository;
        }
    }
    
    0 讨论(0)
  • 2020-11-30 22:29

    In my case bases upon @Tomáš Votruba answer and this question I propose the following approaches:

    Adapter Approach

    Without Inheritance

    1. Create a generic Adapter Class:

      namespace AppBundle\Services;
      use Doctrine\ORM\EntityManagerInterface;
      
      class RepositoryServiceAdapter
      {
          private $repository=null;
      
          /**
          * @param EntityManagerInterface the Doctrine entity Manager
          * @param String $entityName The name of the entity that we will retrieve the repository
          */
          public function __construct(EntityManagerInterface $entityManager,$entityName)
          {
              $this->repository=$entityManager->getRepository($entityName)
          }
      
          public function __call($name,$arguments)
          {
            if(empty($arrguments)){ //No arguments has been passed
              $this->repository->$name();
            } else {
              //@todo: figure out how to pass the parameters
              $this->repository->$name(...$argument);
            }
          }
      }
      
    2. Then foreach entity Define a service, for examplein my case to define a (I use php to define symfony services):

       $container->register('ellakcy.db.contact_email',AppBundle\Services\Adapters\RepositoryServiceAdapter::class)
        ->serArguments([new Reference('doctrine'),AppBundle\Entity\ContactEmail::class]);
      

    With Inheritance

    1. Same step 1 mentioned above

    2. Extend the RepositoryServiceAdapter class for example:

      namespace AppBundle\Service\Adapters;
      
      use Doctrine\ORM\EntityManagerInterface;
      use AppBundle\Entity\ContactEmail;
      
      class ContactEmailRepositoryServiceAdapter extends RepositoryServiceAdapter
      {
        public function __construct(EntityManagerInterface $entityManager)
        {
          parent::__construct($entityManager,ContactEmail::class);
        }
      }
      
    3. Register service:

      $container->register('ellakcy.db.contact_email',AppBundle\Services\Adapters\RepositoryServiceAdapter::class)
        ->serArguments([new Reference('doctrine')]);
      

    Either the case you have a good testable way to function tests your database beavior also it aids you on mocking in case you want to unit test your service without the need to worry too much on how to do that. For example, let us suppose we have the following service:

    //Namespace definitions etc etc
    
    class MyDummyService
    {
      public function __construct(RepositoryServiceAdapter $adapter)
      {
        //Do stuff
      }
    }
    

    And the RepositoryServiceAdapter adapts the following repository:

    //Namespace definitions etc etc
    
    class SomeRepository extends \Doctrine\ORM\EntityRepository
    {
       public function search($params)
       {
         //Search Logic
       }
    }
    

    Testing

    So you can easily mock/hardcode/emulate the behavior of the method search defined in SomeRepository by mocking aither the RepositoryServiceAdapter in non-inheritance approach or the ContactEmailRepositoryServiceAdapter in the inheritance one.

    The Factory Approach

    Alternatively you can define the following factory:

    namespace AppBundle\ServiceFactories;
    
    use Doctrine\ORM\EntityManagerInterface;
    
    class RepositoryFactory
    {
      /**
      * @param EntityManagerInterface $entityManager The doctrine entity Manager
      * @param String $entityName The name of the entity
      * @return Class
      */
      public static function repositoryAsAService(EntityManagerInterface $entityManager,$entityName)
      {
        return $entityManager->getRepository($entityName);
      }
    }
    

    And then Switch to php service annotation by doing the following:

    Place this into a file ./app/config/services.php (for symfony v3.4, . is assumed your ptoject's root)

    use Symfony\Component\DependencyInjection\Definition;
    use Symfony\Component\DependencyInjection\Reference;
    $definition = new Definition();
    
    $definition->setAutowired(true)->setAutoconfigured(true)->setPublic(false);
    
    // $this is a reference to the current loader
    $this->registerClasses($definition, 'AppBundle\\', '../../src/AppBundle/*', '../../src/AppBundle/{Entity,Repository,Tests,Interfaces,Services/Adapters/RepositoryServiceAdapter.php}');
    
    
    $definition->addTag('controller.service_arguments');
    $this->registerClasses($definition, 'AppBundle\\Controller\\', '../../src/AppBundle/Controller/*');
    

    And cange the ./app/config/config.yml (. is assumed your ptoject's root)

    imports:
        - { resource: parameters.yml }
        - { resource: security.yml }
        #Replace services.yml to services.php
        - { resource: services.php }
    
    #Other Configuration
    

    Then you can clace the service as follows (used from my example where I used a Dummy entity named Item):

    $container->register(ItemRepository::class,ItemRepository::class)
      ->setFactory([new Reference(RepositoryFactory::class),'repositoryAsAService'])
      ->setArguments(['$entityManager'=>new Reference('doctrine.orm.entity_manager'),'$entityName'=>Item::class]);
    

    Also as a generic tip, switching to php service annotation allows you to do trouble-free more advanced service configuration thin one above. For code snippets use a special repository I made using the factory method.

    0 讨论(0)
提交回复
热议问题