web-dev-qa-db-fra.com

Service autowire impossible: l'argument fait référence à la classe mais ce service n'existe pas

Je suis en train de mettre à niveau un projet de Symfony 3 à Symfony 4 (- https://github.com/symfony/symfony/blob/master/UPGRADE-4.0.md ) et j'ai de nombreux référentiels/services comme celui-ci:

namespace App\Entity;

use App\Entity\Activation;
use Doctrine\ORM\EntityRepository;
use Predis\Client;

class ActivationRepository extends EntityRepository
{
    // ...
}

Et quand j'essaye de lancer le projet dans le navigateur comme ceci:

http://localhost:8000/login

Je reçois cette erreur:

(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.

Cela signifie-t-il que vous devez créer un service pour "Doctrine\ORM\Mapping\ClassMetadata" dans votre fichier services.yaml?

Merci à autowiring mon nouveau fichier services.yaml est assez petit comparé à l'ancien, qui avait plus de 2000 lignes. Le nouveau services.yaml en a plusieurs (jusqu'à présent):

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: Avez-vous vraiment besoin d’ajouter des définitions de service à services.yaml pour les classes de fournisseurs tiers? Et si oui, puis-je avoir un exemple de la façon de le faire s'il vous plaît? Tout conseil de toute personne ayant déjà mis à niveau de Symfony 3 à Symfony 4 serait génial.

PHP 7.2.0-2 + ​​ubuntu16.04.1 + deb.sury.org + 2 (cli) (build: 7 déc. 2017 20:14:31) (NTS) Linux Mint 18, Apache2 Ubuntu.

EDIT/FYI:

Ceci est le "Doctrine\ORM\EntityRepository :: __ construct ()" que ActivationRepository étend:

/**
     * 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;
    }

qui se trouve ici:

/vendor/doctrine/orm/lib/Doctrine/ORM/EntityRepository.php
17
Donal.Lynch.Msc

À partir de la version 1.8 de DoctrineBundle, vous pouvez étendre votre classe en utilisant Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository au lieu de Doctrine\ORM\EntityRepository. Le résultat sera le même, mais cela prend en charge le fil automatique.

Exemple:

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);
    }

    // ...
}
39
Federkun

Avez-vous vraiment besoin d'ajouter des définitions de service à services.yaml pour les classes de fournisseurs tiers?

Non, ne fais pas ça. Ma suggestion personnelle est la suivante: ne prolongez pas EntityRepository. Déjà. Vous ne voulez pas que l'interface de votre référentiel ait une méthode comme createQuery ou flush. Au moins, vous ne voulez pas cela si vous considérez un référentiel comme une collection d'objets. Si vous prolongez EntityRepository, vous aurez une abstraction qui fuit.

Au lieu de cela, vous pouvez injecter le EntityManager à l'intérieur de votre référentiel, et c'est tout:

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.
}

Aucune autre étape n'est requise.

1
Federkun