Injekcja EntityManager w Symfony 2 w usłudze

96

Utworzyłem własną usługę i muszę wstrzyknąć doktrynę EntityManager, ale nie widzę, aby była __construct()ona wywoływana w mojej usłudze, a wstrzykiwanie nie działa.

Oto kod i konfiguracje:

<?php

namespace Test\CommonBundle\Services;
use Doctrine\ORM\EntityManager;

class UserService {

    /**
     *
     * @var EntityManager 
     */
    protected $em;

    public function __constructor(EntityManager $entityManager)
    {
        var_dump($entityManager);
        exit(); // I've never saw it happen, looks like constructor never called
        $this->em = $entityManager;
    }

    public function getUser($userId){
       var_dump($this->em ); // outputs null  
    }

}

Tutaj jest services.ymlw moim pakiecie

services:
  test.common.userservice:
    class:  Test\CommonBundle\Services\UserService
    arguments: 
        entityManager: "@doctrine.orm.entity_manager"

Zaimportowałem ten plik .yml config.ymldo mojej aplikacji w ten sposób

imports:
    # a few lines skipped, not relevant here, i think
    - { resource: "@TestCommonBundle/Resources/config/services.yml" }

A kiedy dzwonię do serwisu w kontrolerze

    $userservice = $this->get('test.common.userservice');
    $userservice->getUser(123);

Otrzymuję obiekt (nie null), ale $this->emw UserService ma wartość null, a jak już wspomniałem, konstruktor na UserService nigdy nie został wywołany

Jeszcze jedno, Controller i UserService są w różnych pakietach (naprawdę potrzebuję tego, aby utrzymać porządek w projekcie), ale nadal: wszystko inne działa dobrze, mogę nawet zadzwonić

$this->get('doctrine.orm.entity_manager')

w tym samym kontrolerze, którego używam do pobrania UserService i uzyskania prawidłowego (nie zerowego) obiektu EntityManager.

Wygląda na to, że brakuje mi części konfiguracji lub jakiegoś połączenia między konfiguracją UserService i Doctrine.

Andrey Zavarin
źródło
Czy próbowałeś wstrzyknąć setter? To działa?
gremo
Jeśli przez 'setter injection' masz na myśli dodanie metody ustawiającej dla EntityManager w mojej usłudze i wywołanie kontrolera z $ this-> get ('doctrine.orm.entity_manager') jako parametrem, to tak, próbowałem i działa. Ale bardzo lubię używać odpowiedniego wtrysku przez konfigurację
Andrey Zavarin
2
Mam na myśli to: symfony.com/doc/current/book/ ... tak czy owak __constructorto błąd.
gremo
Hm, niż ja nie próbowałem zastrzyku. __construct naprawił problem, ale tak czy inaczej, dziękuję za pomoc!
Andrey Zavarin

Odpowiedzi:

112

Należy wywołać metodę konstruktora Twojej klasy __construct(), a nie __constructor():

public function __construct(EntityManager $entityManager)
{
    $this->em = $entityManager;
}
bogata
źródło
2
Cześć, w tym przykładzie jak mogę zmienić połączenie z domyślnego na inne?
ptmr.io
Zgadza się, ale byłoby jeszcze lepiej, gdybyś użył interfejsu. public function __construct(EntityManagerInterface $entityManager)
Hugues D
65

Dla współczesnego odniesienia, w Symfony 2.4+ nie możesz już nazywać argumentów metody Constructor Injection. Zgodnie z dokumentacją, którą przekażesz:

services:
    test.common.userservice:
        class:  Test\CommonBundle\Services\UserService
        arguments: [ "@doctrine.orm.entity_manager" ]

A potem byłyby dostępne w kolejności, w jakiej zostały wymienione za pomocą argumentów (jeśli jest ich więcej niż 1).

public function __construct(EntityManager $entityManager) {
    $this->em = $entityManager;
}
Chadwick Meyer
źródło
8
Możesz zrobić: kontener aplikacji / konsoli: debugowanie I dowiedzieć się, jakie usługi również używasz.
Hard Fitness
18

Uwaga od Symfony 3.3 EntityManager jest amortyzowany. Zamiast tego użyj EntityManagerInterface.

namespace AppBundle\Service;

use Doctrine\ORM\EntityManagerInterface;

class Someclass {
    protected $em;

    public function __construct(EntityManagerInterface $entityManager)
    {
        $this->em = $entityManager;
    }

    public function somefunction() {
        $em = $this->em;
        ...
    }
}
Robert Saylor
źródło
1
Na wszelki wypadek, gdyby ktoś natknął się na to i był zdezorientowany: EntityManager z pewnością nie został zdeprecjonowany. Korzystanie z interfejsu pomaga w automatycznym okablowaniu i jest zalecane, ale w żadnym wypadku nie jest wymagane. A interfejs istnieje już od dawna. Nie ma tu nic nowego.
Cerad
To jest odpowiedź. Jednakże, proszę o odniesienie: stackoverflow.com/questions/22154558/ ...
tfont
Zaktualizuj do mojego własnego rozwiązania. Właściwym sposobem powinno być teraz użycie jednostek i repozytoriów. Entity Manager jest już naturalnie wstrzykiwany do repozytorium. Przykład możesz zobaczyć tutaj: youtu.be/AHVtOJDTx0M
Robert Saylor
7

Od 2017 i Symfony 3.3 możesz zarejestrować Repozytorium jako usługę , ze wszystkimi jego zaletami.

Sprawdź mój post Jak używać repozytorium z Doctrine as Service w Symfony, aby uzyskać bardziej ogólny opis.


W twoim konkretnym przypadku oryginalny kod z tuningiem wyglądałby tak:

1. Użyj w swoich usługach lub kontrolerze

<?php

namespace Test\CommonBundle\Services;

use Doctrine\ORM\EntityManagerInterface;

class UserService
{
    private $userRepository;

    // use custom repository over direct use of EntityManager
    // see step 2
    public function __constructor(UserRepository $userRepository)
    {
        $this->userRepository = $userRepository;
    }

    public function getUser($userId)
    {
        return $this->userRepository->find($userId);
    }
}

2. Utwórz nowe repozytorium niestandardowe

<?php

namespace Test\CommonBundle\Repository;

use Doctrine\ORM\EntityManagerInterface;

class UserRepository
{
    private $repository;

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

    public function find($userId)
    {
        return  $this->repository->find($userId);
    }
}

3. Zarejestruj usługi

# app/config/services.yml
services:
    _defaults:
        autowire: true

    Test\CommonBundle\:
       resource: ../../Test/CommonBundle
Tomáš Votruba
źródło