Inyección de Symfony 2 EntityManager en servicio

96

He creado mi propio servicio y necesito inyectar la doctrina EntityManager, pero no veo que __construct()se llame a mi servicio y la inyección no funciona.

Aquí está el código y las configuraciones:

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

}

Aquí está services.ymlen mi paquete

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

He importado ese .yml config.ymlen mi aplicación así

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

Y cuando llamo al servicio en el controlador

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

Obtengo un objeto (no nulo), pero $this->emen UserService es nulo, y como ya mencioné, nunca se ha llamado al constructor en UserService

Una cosa más, Controller y UserService están en paquetes diferentes (realmente lo necesito para mantener el proyecto organizado), pero aún así: todo lo demás funciona bien, incluso puedo llamar

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

en el mismo controlador que utilizo para obtener UserService y obtener un objeto EntityManager válido (no nulo).

Parece que me falta una pieza de configuración o algún vínculo entre UserService y la configuración de Doctrine.

Andrey Zavarin
fuente
¿Has probado la inyección de setter? ¿Funciona?
gremo
Si por 'inyección de setter' te refieres a agregar un método de setter para EntityManager en mi servicio y llamar al controlador con $ this-> get ('doctrine.orm.entity_manager') como parámetro, entonces sí, lo he intentado y funciona. Pero realmente me gusta usar la inyección adecuada a través de la configuración
Andrey Zavarin
2
Me refiero a esto: symfony.com/doc/current/book/… de todos modos __constructores el error.
gremo
Um, que no he probado la inyección de setter. __construct solucionó el problema, pero de todos modos, ¡gracias por su ayuda!
Andrey Zavarin

Respuestas:

112

Se debe llamar al método constructor de su clase __construct(), no __constructor():

public function __construct(EntityManager $entityManager)
{
    $this->em = $entityManager;
}
richsage
fuente
2
Hola, en este ejemplo, ¿cómo podría cambiar la conexión predeterminada a cualquier otra?
ptmr.io
Correcto, pero sería aún mejor si usara una interfaz. public function __construct(EntityManagerInterface $entityManager)
Hugues D
65

Para una referencia moderna, en Symfony 2.4+, ya no puede nombrar los argumentos para el método Constructor Injection. Según la documentación pasarías:

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

Y luego estarán disponibles en el orden en que se enumeraron a través de los argumentos (si hay más de 1).

public function __construct(EntityManager $entityManager) {
    $this->em = $entityManager;
}
Chadwick Meyer
fuente
8
Puede hacer un: contenedor de aplicación / consola: depurar y averiguar qué servicios está ejecutando también.
Hard Fitness
18

Tenga en cuenta que Symfony 3.3 EntityManager está depreciado. Utilice EntityManagerInterface en su lugar.

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
fuente
1
En caso de que alguien se encuentre con esto y esté confundido: el EntityManager ciertamente no se ha depreciado. El uso de la interfaz ayuda con el cableado automático y se recomienda, pero no es obligatorio. Y la interfaz existe desde hace mucho tiempo. Nada realmente nuevo aquí.
Cerad
Esta es la respuesta. Sin embargo, sírvase consultar: stackoverflow.com/questions/22154558/…
tfont
Actualización a mi propia solución. La forma correcta ahora debería ser utilizar Entidades y Repositorios. Entity Manager ya se inyecta naturalmente en un repositorio. Puedes ver un ejemplo aquí: youtu.be/AHVtOJDTx0M
Robert Saylor
7

Desde 2017 y Symfony 3.3 puedes registrar Repository como servicio , con todas las ventajas que tiene.

Consulte mi publicación Cómo usar el repositorio con Doctrine como servicio en Symfony para obtener una descripción más general.


Para su caso específico, el código original con ajuste se vería así:

1. Uso en sus servicios o 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. Crea un nuevo repositorio 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. Registrar servicios

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

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