Necesito inyectar dos objetos en ImageService
. Uno de ellos es una instancia de Repository/ImageRepository
, que obtengo así:
$image_repository = $container->get('doctrine.odm.mongodb')
->getRepository('MycompanyMainBundle:Image');
Entonces, ¿cómo lo declaro en mi services.yml? Aquí está el servicio:
namespace Mycompany\MainBundle\Service\Image;
use Doctrine\ODM\MongoDB\DocumentRepository;
class ImageManager {
private $manipulator;
private $repository;
public function __construct(ImageManipulatorInterface $manipulator, DocumentRepository $repository) {
$this->manipulator = $manipulator;
$this->repository = $repository;
}
public function findAll() {
return $this->repository->findAll();
}
public function createThumbnail(ImageInterface $image) {
return $this->manipulator->resize($image->source(), 300, 200);
}
}
php
symfony
dependency-injection
ChocoDesarrollador
fuente
fuente
Respuestas:
Aquí hay una solución limpia para aquellos que vienen de Google como yo:
Actualización: aquí está la solución Symfony 2.6 (y superior):
services: myrepository: class: Doctrine\ORM\EntityRepository factory: ["@doctrine.orm.entity_manager", getRepository] arguments: - MyBundle\Entity\MyClass myservice: class: MyBundle\Service\MyService arguments: - "@myrepository"
Solución obsoleta (Symfony 2.5 y menos):
services: myrepository: class: Doctrine\ORM\EntityRepository factory_service: doctrine.orm.entity_manager factory_method: getRepository arguments: - MyBundle\Entity\MyClass myservice: class: MyBundle\Service\MyService arguments: - "@myrepository"
fuente
doctrine.odm.mongodb.document_manager
como$this->get('myrepository')
. ¿Hay alguna forma de definir / pasar el repositorio como un argumentomyservice
sin tener que definirlo como un servicio en sí mismo?private
, lo que significa que se pueden inyectar (en la configuración YAML) pero no se pueden recuperar usando->get()
factory_service
yfactory_method
desde Symfony 2.6 . Así es como se debe hacer ahora: stackoverflow.com/a/31807608/828366factory: ["@doctrine.orm.entity_manager", getRepository]
, de lo contrario, será recibido por una bonita ParseException.Encontré este enlace y esto funcionó para mí:
parameters: image_repository.class: Mycompany\MainBundle\Repository\ImageRepository image_repository.factory_argument: 'MycompanyMainBundle:Image' image_manager.class: Mycompany\MainBundle\Service\Image\ImageManager image_manipulator.class: Mycompany\MainBundle\Service\Image\ImageManipulator services: image_manager: class: %image_manager.class% arguments: - @image_manipulator - @image_repository image_repository: class: %image_repository.class% factory_service: doctrine.odm.mongodb factory_method: getRepository arguments: - %image_repository.factory_argument% image_manipulator: class: %image_manipulator.class%
fuente
En caso de que no desee definir cada repositorio como un servicio, a partir de la versión
2.4
puede hacer lo siguiente (default
es un nombre del administrador de la entidad):@=service('doctrine.orm.default_entity_manager').getRepository('MycompanyMainBundle:Image')
fuente
@=service('doctrine').getRepository('AppBundle:EntityX')
<service id="image_manager" class="MyCompany\MainBundle\ImageManager"> <argument type="expression">service('doctrine.orm.default_entity_manager').getRepository('MycompanyMainBundle:Image')</argument> </service>
Symfony 3.3, 4 y 5 lo hace mucho más sencillo.
Revisa mi publicación Cómo usar Repository con Doctrine como servicio en Symfony para obtener una descripción más general.
Para su código, todo lo que necesita hacer es usar la composición sobre la herencia , uno de los patrones SÓLIDOS.
1. Crea tu propio repositorio sin dependencia directa de Doctrine
<?php namespace MycompanyMainBundle\Repository; use Doctrine\ORM\EntityManagerInterface; use MycompanyMainBundle\Entity\Image; class ImageRepository { private $repository; public function __construct(EntityManagerInterface $entityManager) { $this->repository = $entityManager->getRepository(Image::class); } // add desired methods here public function findAll() { return $this->repository->findAll(); } }
2. Agregue el registro de configuración con el registro automático basado en PSR-4
# app/config/services.yml services: _defaults: autowire: true MycompanyMainBundle\: resource: ../../src/MycompanyMainBundle
3. Ahora puede agregar cualquier dependencia en cualquier lugar mediante la inyección del constructor
use MycompanyMainBundle\Repository\ImageRepository; class ImageService { public function __construct(ImageRepository $imageRepository) { $this->imageRepository = $imageRepository; } }
fuente
En mi caso se basa en la respuesta de @ Tomáš Votruba y esta pregunta propongo los siguientes enfoques:
Enfoque del adaptador
Sin herencia
Cree una clase de adaptador genérica:
namespace AppBundle\Services; use Doctrine\ORM\EntityManagerInterface; class RepositoryServiceAdapter { private $repository=null; /** * @param EntityManagerInterface the Doctrine entity Manager * @param String $entityName The name of the entity that we will retrieve the repository */ public function __construct(EntityManagerInterface $entityManager,$entityName) { $this->repository=$entityManager->getRepository($entityName) } public function __call($name,$arguments) { if(empty($arrguments)){ //No arguments has been passed $this->repository->$name(); } else { //@todo: figure out how to pass the parameters $this->repository->$name(...$argument); } } }
Luego, para cada entidad Defina un servicio, por ejemplo, en mi caso para definir un (yo uso php para definir los servicios de Symfony):
$container->register('ellakcy.db.contact_email',AppBundle\Services\Adapters\RepositoryServiceAdapter::class) ->serArguments([new Reference('doctrine'),AppBundle\Entity\ContactEmail::class]);
Con herencia
Mismo paso 1 mencionado anteriormente
Extienda la
RepositoryServiceAdapter
clase por ejemplo:namespace AppBundle\Service\Adapters; use Doctrine\ORM\EntityManagerInterface; use AppBundle\Entity\ContactEmail; class ContactEmailRepositoryServiceAdapter extends RepositoryServiceAdapter { public function __construct(EntityManagerInterface $entityManager) { parent::__construct($entityManager,ContactEmail::class); } }
Servicio de registro:
$container->register('ellakcy.db.contact_email',AppBundle\Services\Adapters\RepositoryServiceAdapter::class) ->serArguments([new Reference('doctrine')]);
O en el caso de que tenga una buena forma comprobable de funcionamiento, prueba el comportamiento de su base de datos y también le ayuda a burlarse en caso de que desee realizar una prueba unitaria de su servicio sin la necesidad de preocuparse demasiado por cómo hacerlo. Por ejemplo, supongamos que tenemos el siguiente servicio:
//Namespace definitions etc etc class MyDummyService { public function __construct(RepositoryServiceAdapter $adapter) { //Do stuff } }
Y RepositoryServiceAdapter adapta el siguiente repositorio:
//Namespace definitions etc etc class SomeRepository extends \Doctrine\ORM\EntityRepository { public function search($params) { //Search Logic } }
Pruebas
Por lo tanto, puede simular / codificar / emular fácilmente el comportamiento del método
search
definido enSomeRepository
burlándose de laRepositoryServiceAdapter
enfoque de no herencia o delContactEmailRepositoryServiceAdapter
herencia.El enfoque de fábrica
Alternativamente, puede definir la siguiente fábrica:
namespace AppBundle\ServiceFactories; use Doctrine\ORM\EntityManagerInterface; class RepositoryFactory { /** * @param EntityManagerInterface $entityManager The doctrine entity Manager * @param String $entityName The name of the entity * @return Class */ public static function repositoryAsAService(EntityManagerInterface $entityManager,$entityName) { return $entityManager->getRepository($entityName); } }
Y luego cambie a la anotación del servicio php haciendo lo siguiente:
Coloque esto en un archivo
./app/config/services.php
(para Symfony v3.4,.
se asume la raíz de su ptoject)use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\DependencyInjection\Reference; $definition = new Definition(); $definition->setAutowired(true)->setAutoconfigured(true)->setPublic(false); // $this is a reference to the current loader $this->registerClasses($definition, 'AppBundle\\', '../../src/AppBundle/*', '../../src/AppBundle/{Entity,Repository,Tests,Interfaces,Services/Adapters/RepositoryServiceAdapter.php}'); $definition->addTag('controller.service_arguments'); $this->registerClasses($definition, 'AppBundle\\Controller\\', '../../src/AppBundle/Controller/*');
Y cambiar el
./app/config/config.yml
(.
se asume la raíz de su ptoject)imports: - { resource: parameters.yml } - { resource: security.yml } #Replace services.yml to services.php - { resource: services.php } #Other Configuration
Luego, puede clasificar el servicio de la siguiente manera (usado en mi ejemplo donde usé una entidad ficticia llamada
Item
):$container->register(ItemRepository::class,ItemRepository::class) ->setFactory([new Reference(RepositoryFactory::class),'repositoryAsAService']) ->setArguments(['$entityManager'=>new Reference('doctrine.orm.entity_manager'),'$entityName'=>Item::class]);
También como un consejo genérico, cambiar a la
php
anotación de servicio le permite realizar una configuración de servicio más avanzada sin problemas. Para los fragmentos de código, use un repositorio especial que hice usando elfactory
método.fuente