Injeção de Symfony 2 EntityManager em serviço

96

Criei meu próprio serviço e preciso injetar a doutrina EntityManager, mas não vejo que __construct()seja chamado no meu serviço e a injeção não funciona.

Aqui está o código e as configurações:

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

}

Aqui está services.ymlno meu pacote

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

Eu importei esse .yml config.ymlem meu aplicativo assim

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

E quando eu chamo o serviço no controlador

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

Recebo um objeto (não nulo), mas $this->emem UserService é nulo e, como já mencionei, o construtor em UserService nunca foi chamado

Mais uma coisa, Controller e UserService estão em pacotes diferentes (eu realmente preciso disso para manter o projeto organizado), mas ainda assim: tudo o mais funciona bem, posso até chamar

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

no mesmo controlador que eu uso para obter UserService e obter um objeto EntityManager válido (não nulo).

Parece que estou faltando alguma configuração ou algum link entre a configuração do UserService e do Doctrine.

Andrey Zavarin
fonte
Você já experimentou a injeção de setter? Funciona?
gremo
Se por 'injeção setter' você quer dizer adicionar o método setter para EntityManager em meu serviço e chamar o controlador com $ this-> get ('doctrine.orm.entity_manager') como parâmetro, então sim, eu tentei e funciona. Mas eu realmente gosto de usar a injeção adequada via configuração
Andrey Zavarin
2
Quero dizer: symfony.com/doc/current/book/… de qualquer maneira, __constructoré o erro.
gremo
Hum, então eu não tentei injeção setter. __construct corrigiu o problema, mas mesmo assim, obrigado pela ajuda!
Andrey Zavarin

Respostas:

112

O método construtor de sua classe deve ser chamado __construct(), não __constructor():

public function __construct(EntityManager $entityManager)
{
    $this->em = $entityManager;
}
richsage
fonte
2
Olá, neste exemplo, como eu poderia alterar a conexão padrão para qualquer outra?
ptmr.io
Certo, mas seria ainda melhor se você usasse uma interface. public function __construct(EntityManagerInterface $entityManager)
Hugues D
65

Para referência moderna, no Symfony 2.4+, você não pode mais nomear os argumentos para o método de injeção de construtor. De acordo com a documentação que você passaria:

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

E então eles estariam disponíveis na ordem em que foram listados por meio dos argumentos (se houver mais de 1).

public function __construct(EntityManager $entityManager) {
    $this->em = $entityManager;
}
Chadwick Meyer
fonte
8
Você pode fazer um: app / console container: debug E descobrir quais serviços você está executando também.
Hard Fitness
18

Nota a partir do Symfony 3.3 EntityManager está depreciado. Use 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
fonte
1
Para o caso de alguém se deparar com isso e ficar confuso: o EntityManager certamente não foi depreciado. O uso da interface ajuda na fiação automática e é recomendado, mas não é obrigatório. E a interface já existe há muito tempo. Nada realmente novo aqui.
Cerad de
Esta é a resposta. No entanto, consulte: stackoverflow.com/questions/22154558/…
tfont
Atualize para minha própria solução. A maneira correta agora deve ser usar Entidades e Repositórios. O Entity Manager já está naturalmente injetado em um repositório. Você pode ver um exemplo aqui: youtu.be/AHVtOJDTx0M
Robert Saylor
7

Desde 2017 e Symfony 3.3 você pode registrar o Repositório como serviço , com todas as suas vantagens.

Verifique minha postagem Como usar o Repository com Doctrine as Service no Symfony para uma descrição mais geral.


Para seu caso específico, o código original com ajuste seria assim:

1. Use em seus serviços ou controlador

<?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. Crie um novo repositório personalizado

<?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. Registre serviços

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

    Test\CommonBundle\:
       resource: ../../Test/CommonBundle
Tomáš Votruba
fonte