Cannot autowire service: Argument references class but no such service exists

46,562

Solution 1

Starting from the 1.8 version of DoctrineBundle, you can extend your class using Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository instead of Doctrine\ORM\EntityRepository. The result will be the same, but this does support the autowire.

Example:

use App\Entity\Activation;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Common\Persistence\ManagerRegistry;

class ActivationRepository extends ServiceEntityRepository
{
    public function __construct(ManagerRegistry $registry)
    {
        parent::__construct($registry, Activation::class);
    }

    // ...
}

Solution 2

Do you really need to add service definitions to services.yaml for third party vendor classes?

No, don't do that. My personal suggestion is: don't extend EntityRepository. Ever. You don't want your repository's interface to have method like createQuery or flush. At least, you don't want that if you consider a repository just like a collection of objects. If you extend EntityRepository you will have a leaky abstraction.

Instead you can inject the EntityManager inside your repository, and that's it:

use App\Entity\Activation;
use App\Repository\ActivationRepository;
use Doctrine\ORM\EntityManagerInterface;

final class DoctrineActivationRepository implements ActivationRepository
{
    private $entityManager;
    private $repository;

    public function __construct(EntityManagerInterface $entityManager)
    {
        $this->entityManager = $entityManager;
        $this->repository = $this->entityManager->getRepository(Activation::class);
    }

    public function store(Activation $activation): void
    {
        $this->entityManager->persist($activation);
        $this->entityManager->flush();
    }

    public function get($id): ?Activation
    {
        return $this->repository->find($id);
    }

    // other methods, that you defined in your repository's interface.
}

No other steps are required.

Solution 3

My issue was a wrong namespace. File real position was App\Infrastructure\MySQL\Rubric\Specification But namespace was set to App\Infrastructure\Rubric\Specification

Result "[blah blah] but no such service exists".

Share:
46,562
NiqT
Author by

NiqT

Hi! Donal is my name and I've been a Web Developer since 2009. My passion in life is making webpages for millions of people to enjoy and playing with the latest technologies is the best bit! Feel free to get in touch with me, but please, no spam..

Updated on April 15, 2021

Comments

  • NiqT
    NiqT about 3 years

    I'm upgrading a project from Symfony 3 to Symfony 4 (https://github.com/symfony/symfony/blob/master/UPGRADE-4.0.md) and I have many repository/services like this:

    namespace App\Entity;
    
    use App\Entity\Activation;
    use Doctrine\ORM\EntityRepository;
    use Predis\Client;
    
    class ActivationRepository extends EntityRepository
    {
        // ...
    }
    

    And when I try to run the project in the browser like this:

    http://localhost:8000/login
    

    I get this error:

    (1/1) RuntimeException
    Cannot autowire service "App\Entity\ActivationRepository": 
    argument "$class" of method 
    "Doctrine\ORM\EntityRepository::__construct()" 
    references class "Doctrine\ORM\Mapping\ClassMetadata" 
    but no such service exists.
    

    Does this mean you have to create a service for "Doctrine\ORM\Mapping\ClassMetadata" in your services.yaml file?

    Thanks to autowiring my new services.yaml file is fairly small compared to the old one, which had 2000+ lines. The new services.yaml just has several of these (so far):

    App\:
        resource: '../src/*'
    
    # Controllers
    App\Controller\:
        resource: '../src/Controller'
        autowire: true
        public: true
        tags: ['controller.service_arguments']
    
    # Models
    App\Model\:
        resource: '../src/Model/'
        autowire: true
        public: true
    
    // etc
    

    Question: Do you really need to add service definitions to services.yaml for third party vendor classes? And if so, can I get an example of how to do that please? Any advice from anyone who has already upgraded from Symfony 3 to Symfony 4 would be great.

    PHP 7.2.0-2+ubuntu16.04.1+deb.sury.org+2 (cli) (built: Dec 7 2017 20:14:31) ( NTS ) Linux Mint 18, Apache2 Ubuntu.

    EDIT / FYI:

    This is the "Doctrine\ORM\EntityRepository::__construct()" which the ActivationRepository extends:

    /**
         * Initializes a new <tt>EntityRepository</tt>.
         *
         * @param EntityManager         $em    The EntityManager to use.
         * @param Mapping\ClassMetadata $class The class descriptor.
         */
        public function __construct(EntityManagerInterface $em, Mapping\ClassMetadata $class)
        {
            $this->_entityName = $class->name;
            $this->_em         = $em;
            $this->_class      = $class;
        }
    

    which is located here:

    /vendor/doctrine/orm/lib/Doctrine/ORM/EntityRepository.php
    
  • Cerad
    Cerad over 6 years
    Doctrine's repositories are more than a collection of objects. Nor is it clear how have a store method fit's in with a collection of objects. I understand what you are saying but your solution is overkill.
  • Federkun
    Federkun over 6 years
    Yeah, I know, I'm talking about collections and use an example with a store method, that's more persistence-oriented instead of collection-oriented. But if I replace that with an add method the next question is where I commit my changes? and we make the example too complex. Still, I don't think that this is overkill. But I guess it's just my personal opinion. Like everything, there's pro and cons. Using the EntityManager service locator has pro and cons as well.
  • Vadim Ashikhman
    Vadim Ashikhman over 6 years
    Injecting EntityManager for getting repository objects is like injecting Symfony DI container. If there is no way to autowire repository classes the only way is to define them in the services.yml. The subject is not about where to put persistent logic, it's about autowiring Doctrine repository classes.
  • Federkun
    Federkun over 6 years
    @VadimAshikhman, I don't mind provide the answer to that specific question: stackoverflow.com/a/48026278/711206 But you will notice that that solution is even worst than this one.
  • Vadim Ashikhman
    Vadim Ashikhman over 6 years
    @Federkun, wow, nice solution! In my personal opinion i think it is better than injecting the manager and calling it's getRepository() method, so you can only instantiate repository object that corresponds to only 1 entity.
  • Federkun
    Federkun over 6 years
    You will need to overwrite ServiceEntityRepository#__construct for each repository, and under the hood ServiceEntityRepository will do the same thing as what I described here. I guess that, in the end, it's more of a "choose your poison"-kinda of answer. Personally, I'll stick with what I suggested here. But, of course, everybody should choose what works better for them. Cheers!
  • Vadim Ashikhman
    Vadim Ashikhman over 6 years
    @Federkun, I agree with you, when you know much about a library you are using you can make many things which developers didn't think of. Hell, you can always use reflection to make something bad. In case of new developers that are joining a project, they will just use single entity class in the constructor, instead of getting any repository from the manager/registry.
  • WalterEgo
    WalterEgo almost 6 years
    Thank you Federkun and Massimiliano Arione. Not sure what bits you did between you, but you just helped me get one more step towards the end of my Symfony 3.4 to 4 journey.
  • Cristiano Casciotti
    Cristiano Casciotti about 4 years
    Just a note for who are using doctrine/persistence >= 1.3 used by doctrine-bundle 2.x, use Doctrine\Persistence\ManagerRegistry instead of Doctrine\Common\Persistence\ManagerRegistry
  • Zoltán Süle
    Zoltán Süle over 3 years
    I would mention that because it is also important that they have to create a constructor and define the entityClass as you already did in your example.
  • barell
    barell over 3 years
    Beware that presented "store()" method seems convenient it kills the concept of having Doctrine to manage all writes done in a single transaction where possible. Use it only if you know what are you doing. Not sure why people are so hesitant to use entity manager directly in controllers/services and delegate things to repository/model managers etc.