Внедрение Symfony 2 EntityManager в сервисе

96

Я создал свою собственную службу, и мне нужно внедрить доктрину EntityManager, но я не вижу, что она __construct()вызывается в моей службе, и внедрение не работает.

Вот код и конфиги:

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

}

Вот services.ymlв моей связке

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

Я импортировал этот .yml config.ymlв свое приложение вот так

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

И когда я вызываю сервис в контроллере

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

Я получаю объект (не null), но $this->emв UserService значение null, и, как я уже упоминал, конструктор UserService никогда не вызывался

Еще одна вещь, Controller и UserService находятся в разных пакетах (мне действительно нужно, чтобы проект был организован), но все же: все остальное работает нормально, я даже могу позвонить

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

в том же контроллере, который я использую для получения UserService и получения действительного (не нулевого) объекта EntityManager.

Похоже, мне не хватает части конфигурации или какой-то связи между UserService и конфигурацией Doctrine.

Андрей Заварин
источник
Вы пробовали вводить сеттер? Оно работает?
gremo
Если под «установкой инъекции» вы имеете в виду добавление метода установки для EntityManager в мою службу и вызов контроллера с параметром $ this-> get ('doctrine.orm.entity_manager') в качестве параметра, тогда да, я пробовал, и он работает. Но мне очень нравится использовать правильную инъекцию через конфиг
Андрей Заварин
2
Я имею в виду следующее: symfony.com/doc/current/book/… в любом случае __constructorэто ошибка.
gremo
Гм, чем я не пробовал вводить сеттер. __construct устранил проблему, но в любом случае спасибо за вашу помощь!
Андрей Заварин

Ответы:

112

Следует вызывать метод конструктора вашего класса __construct(), а не __constructor():

public function __construct(EntityManager $entityManager)
{
    $this->em = $entityManager;
}
Richsage
источник
2
Привет, как в этом примере я могу изменить подключение по умолчанию на любое другое?
ptmr.io
Верно, но было бы еще лучше, если бы вы использовали интерфейс. public function __construct(EntityManagerInterface $entityManager)
Hugues D
65

Для современной справки, в Symfony 2.4+ вы больше не можете называть аргументы для метода Constructor Injection. Согласно документации, вы должны пройти:

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

И тогда они будут доступны в том порядке, в котором они были перечислены через аргументы (если их больше 1).

public function __construct(EntityManager $entityManager) {
    $this->em = $entityManager;
}
Чедвик Мейер
источник
8
Вы можете сделать: app / console container: debug И узнать, какие службы у вас запущены.
Hard Fitness
18

Обратите внимание, что в Symfony 3.3 EntityManager обесценился. Вместо этого используйте 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;
        ...
    }
}
Роберт Сэйлор
источник
1
На всякий случай кто-нибудь наткнется на это и запутается: EntityManager определенно не обесценился. Использование интерфейса помогает при автоматическом подключении и рекомендуется, но ни в коем случае не требуется. И интерфейс существует уже давно. Ничего особенного здесь нет.
Cerad
Это ответ. Однако, пожалуйста, сделайте ссылку: stackoverflow.com/questions/22154558/…
tfont
Обновите свое решение. Теперь правильным способом должно быть использование сущностей и репозиториев. Entity Manager уже естественным образом внедрен в репозиторий. Вы можете увидеть пример здесь: youtu.be/AHVtOJDTx0M
Роберт
7

Начиная с 2017 года и с Symfony 3.3 вы можете зарегистрировать репозиторий как сервис со всеми его преимуществами.

Проверьте мой пост Как использовать репозиторий с доктриной как сервис в Symfony для более общего описания.


В вашем конкретном случае исходный код с настройкой будет выглядеть так:

1. Используйте в своих сервисах или Контроллере

<?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. Создайте новый настраиваемый репозиторий.

<?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. Зарегистрируйте услуги

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

    Test\CommonBundle\:
       resource: ../../Test/CommonBundle
Томаш Вотруба
источник