Acceder Al Contenedor Servicios Desde Cualquier Lado Symfony2

1,183 views
Skip to first unread message

helysm

unread,
Jun 1, 2012, 4:08:07 PM6/1/12
to symfony-es, symfo...@googlegroups.com, symfony-2...@googlegroups.com, symfony_...@googlegroups.com
Saludos Colegas Symfoneros.

Luego de varias horas de consultas infructuosas en donde buscaba la manera de terner acceso al contenedor de servicios desde cualquier lado diferente del controlador solo encontraba opciones como esta:

Pasar como parametros el servicio o dato que se necesitaba al contructor de la clase ya sea un formulario o una entidad, inicialmente no esta mal solo que cuando va haciendo necesario con mayor uso, esta practica termina siendo molesta => "Opinion Personal". Por lo que vi que hay una forma de poder acceder al contenedor de servicios desde cualquier lado de la aplicacion puede ser una clase de formulario o una clase dentro de una entidad.

Esta busqueda me llevo a la necesidad de que requeria que en una entidad desde el constructor un campo que esta asociado a otra entidad tuviese asociado un objeto especifico que ya existe previamente, pero las unicas opciones que habian era que esto debia hacerlo desde el controlador osea instanciar entidad1 y luego obtener la entidad2 ya sea a traves del entitymanager para luego asociarla a la entidad1; cuando desde el mismo constructor de la entidad1 ya el campo tuviese asociado la entidad2, y esto obedece al concepto de constructor en el paradigma de objetos.

Asi que encontre que la forma de poder acceder al contenedor de servicios desde cualquier lado de la aplicacion seria haciendo esto:

$em = $GLOBALS['kernel']->getContainer()->get('doctrine')->getEntityManager();

Esto lo uso para poder tener acceso al entity manager desde el constructor de una entidad.

y ya con eso puedo realizar la consulta de manera normal a como lo haria desde un controlador; haciendo esto me permite mantener la logica encapsulada dentro de la misma clase de la entidad sin necesidad de pasar parametros adicionales en el constructor.

Ahora el motivo del correo es pedir sus opiniones al respecto, en donde si esta practica es correcta para acceder al entity manager desde mi caso por ejemplo , la clase de una entidad, o si el procedimiento correcto seria que debe ser pasado como parametros al contructor desde la entidad, aunque eso amerite mas trabajo.

Gracias por sus opiniones.

Si encuentras la solución a tu problema no olvides postearla, es por el beneficio de todos en la lista.

Atentamente,

Hely Suarez Marin
Desarrollador PHP Symfony
Miembro del Semillero de Investigación y Desarrollo de Software Libre UFPS
No a la Piratería de Software !Sea Legal con Colombia! Usa Software Libre...
Cúcuta

Ismael Ambrosi

unread,
Jun 1, 2012, 4:36:11 PM6/1/12
to symfo...@googlegroups.com, symfo...@googlegroups.com, symfony-2...@googlegroups.com, symfony_...@googlegroups.com
Hola,

Tengo que pedirte disculpas, pero estoy totalmente en desacuerdo con esta práctica. La lógica de asociación entre entidades no debe ir en una entidad o formulario, deben ser totalmente independientes de esto. Todo esto debe ser manejado desde un controlador, o un servicio especial.

No estoy de acuerdo con tener el DIC disponible en cualquier parte de la aplicación, no es lo que Symfony propone. ¿Cual es el objetivo de utilizar un framework MVC si la lógica va a estar desparramada por todos lados en la aplicación?

Te recomiendo leer la documentación de Symfony y cuales son los Best Practices.

Ismael

--
Has recibido este mensaje porque estás suscrito al grupo "symfony-es" de Google Groups.
Para publicar en este grupo, envía un email a symfo...@googlegroups.com
Para darte de baja, envía un email a symfony-es+...@googlegroups.com
El resto de opciones puedes encontrarlas en http://groups.google.com/group/symfony-es?hl=es


Marcelo Prizmic

unread,
Jun 3, 2012, 11:20:04 AM6/3/12
to symfo...@googlegroups.com
tuve un problema similar y la documenación no recomendaba acceder al entitymanager desde el contructor de una entity, pero lo cierto es que yo, como en tu caso, necesitaba lógica ahí. No encontré una solución razonable.
La solución que proponés no parece elegante pero me parece que si la lógica la necesitás ahí, tampoco tiene mucho sentido darle vueltas innecesarias. Si tenés que ir de un punto a otro vas por la hipotenusa. No tiene mucho sentido recorrer los 2 catetos sólo porque es más elegante. De cualquier modo, no soy experto en SF2.
Marcelo

helysm

unread,
Jun 4, 2012, 8:32:01 AM6/4/12
to symfo...@googlegroups.com

Saludos companeros lo pude resolver de manera mas elegante y manteniendo las buenas practicas. Lo hice pasando el objeto a enlazar como parametro que se le pasa al objeto y realizando una consulta personalizada en el repositorio del elemento enlazado

El jun 3, 2012 10:20 a.m., "Marcelo Prizmic" <mpri...@gmail.com> escribió:



El 1 de junio de 2012 17:36, Ismael Ambrosi <ismaa...@gmail.com> escribió:


>
> Hola,
>
> Tengo que pedirte disculpas, pero estoy totalmente en desacuerdo con esta práctica. L...


tuve un problema similar y la documenación no recomendaba acceder al entitymanager desde el contructor de una entity, pero lo cierto es que yo, como en tu caso, necesitaba lógica ahí. No encontré una solución razonable.
La solución que proponés no parece elegante pero me parece que si la lógica la necesitás ahí, tampoco tiene mucho sentido darle vueltas innecesarias. Si tenés que ir de un punto a otro vas por la hipotenusa. No tiene mucho sentido recorrer los 2 catetos sólo porque es más elegante. De cualquier modo, no soy experto en SF2.
Marcelo

--
Has recibido este mensaje porque estás suscrito al grupo "symfony-es" de Google Groups.

Para p...

carlos

unread,
Jun 4, 2012, 11:16:02 AM6/4/12
to symfo...@googlegroups.com, symfony-es, symfony-2...@googlegroups.com, symfony_...@googlegroups.com
Hola,
Yo estoy desarrollando un sistema bastante grande y no necesite hacer eso, en principio pensé que era inevitable pero después de entender bien la inyección de dependencia me di cuenta que no es necesario.
Seguro que lo que estas haciendo se puede hacer de otra forma correctamente.
Esa es mi opinión.

Oriol Jiménez

unread,
Jun 5, 2012, 9:37:33 AM6/5/12
to symfony_...@googlegroups.com, symfony-es, symfo...@googlegroups.com, symfony-2...@googlegroups.com
como dice Christian, en resumen: es una mala prática, incumple varios principios, pierdes visibilidad e imposibilitas el unit test.

qué problema hay en trabajar en el Controller y pasar como parámetros al constructor lo necesario, o mucho mejor hacer un XXXManager vía DIC con un método que realice dicha operación, y reutilizable desde otros sitios?

Falta ver el código de todo pero a primera vista acceder al $GLOBALS desde un constructor de una Entity no suena nada bien, no?


Salu2
Oriol
El 4 de junio de 2012 14:50, Francesc Rosàs <francescr...@gmail.com> escribió:
Gran explicación Christian.

Hay un caso bastante claro donde sí se inyecta el contenedor entero: en los controllers. Podrían haber hecho que en el routing se tuvieran que especificar los servicios a utilizar pero supongo que prefirieron simplificar para no espantar demasiado a los que empiezan con el framework ;)


On Friday, June 1, 2012 11:31:35 PM UTC+2, Christian Soronellas wrote:
Hola Hely,

Es una mala práctica pasar todo el contenedor de servicios cómo parámetro o en tu caso acceder a él desde el array de globals, no hay que hacerlo. Y mucho menos hacer que la entidades tengan acceso al mismo, pues se incumplen varios principios SOLID y las dependencias entre componentes (aka servicios), ya sean mandatorias o opcionales, dejan de ser explícitas y se pervierte el patrón. Ello comporta que dejes de tener visibilidad sobre qué es lo que necesita el servicio para poder ejecutarse y te sea imposible hacer unit test sobre el mismo. De hecho la recomendación oficial de Symfony2 al respecto del uso del contenedor de servicios, es evitar acoplar nuestro código con el contenedor (de hecho no hay ningún ejemplo, almenos que haya visto yo, en la documentación de Symfony2 que acople el contenedor de servicios)

http://symfony.com/doc/current/components/dependency_injection/introduction.html#avoiding-your-code-becoming-dependent-on-the-container

En general (y no solo para Symfony2), una capa de servicios consta de varias subcapas: lógica de negocio o capa de servicio (en Symfony2 serían services y repositories, por ese orden) => capa entidades del modelo de dominio (en Symfony2 serían las Entities) => capa de acceso a datos (Doctrine 2 & PDO) => Data source (MySQL, Solr, MongoDB y un largo etc). Además debe cumplirse que un servicio debería poderse ejecutar tanto por CLI, como por el front de la aplicación o por dónde se quiera, por lo que no debe estar ligado a componentes intrínsecos a la request HTTP, a la request de CLI, etc.

Adjunto aquí el enlace al patrón Service Layer, inicialmente propuesto por Randy Stafford en el libro de Patterns of Enterprise Application Arquitecture


Y por supuesto, Symfony2 por su arquitectura, te obliga un poco a no usar truquillos piratillas cómo el de la $GLOBALS['kernel'] y a usar algo más acorde con la OOP que ahora por ahora permite PHP en su actual versión. Esto es muy "phpquatro" !! :)

Espero que te sirva!
Un saludo!
Christian.



--
Oriol Jiménez


ironman

unread,
Jun 5, 2012, 10:02:20 AM6/5/12
to symfony-es
Como comentan todos y la propia documentación de symfony afirma, no es
una práctica recomendable.

Tuve esa misma necesidad hace unas semanas y lo pude resolver con un
servicio que contenga el entity manager y haga los malabares
pertinentes con las entidades, algo como un entityWrapper, o un
pseudoRepository para que te hagas una idea.

JERONIMO SALAZAR RAMIREZ

unread,
Jun 5, 2012, 4:32:21 PM6/5/12
to symfo...@googlegroups.com
Pues varios dicen que esta mal, pero creo que cuando uno ya esta desesperado y no sabe por donde, lo que le funcione esta bien mientras te resuelva el problema.

--
Has recibido este mensaje porque estás suscrito al grupo "symfony-es" de Google Groups.
Para publicar en este grupo, envía un email a symfo...@googlegroups.com
Para darte de baja, envía un email a symfony-es+...@googlegroups.com
El resto de opciones puedes encontrarlas en http://groups.google.com/group/symfony-es?hl=es



--
-------------------------------------------------------
Si programas  en PHP, usa Symfony
-------------------------------------------------------
Navega mas seguro con FireFox
------------------------------------------------------


Sebastian Riquelme

unread,
Oct 7, 2015, 1:37:49 PM10/7/15
to symfony-es, symfo...@googlegroups.com, symfony-2...@googlegroups.com, symfony_...@googlegroups.com
Estimados, 

Disculpen por revivir un tema tan antiguo, pero tengo una consulta al respecto. Estoy trabajando con doctrine pero DBAL, no ORM y tengo que acceder al servicio de DBAL (database_connecion) para poder crear una entidad.

Tengo que pasarlo por obligación por el constructor para devolver el objeto?

Por ejemplo:

namespace AppBundle\Entity;

class Funcionario
{
   
private $conn;

   
public function __construct($idFuncionario)
   
{
       
// Aqui debo acceder al contenedor de servicio para crear la conexion $conn
        $this
->conn = $conn
        $stmt
= $conn->query("SELECT...");
        //... crear el objeto funcionario
   
}

Asi desde un controlador poder crear el objeto de la siguiente forma:

$id = 1;

$funcionario
= new Funcionario($id);


Que me recomienda? estoy obligado a pasar la conexion por el constructor?

De antemano muchas gracias.
Sebastian Riquelme

Sebastian Thomson

unread,
Oct 8, 2015, 6:18:48 PM10/8/15
to symfony-es, symfo...@googlegroups.com, symfony-2...@googlegroups.com, symfony_...@googlegroups.com
Sebastian, 

Al inyector de dependencias (container) puedes acceder desde cualquier clase utilizando la variable global $kernel.

Aquí puedes ver un pequeño ejemplo:


Saludos!

Jeronimo SALAZAR RAMIREZ

unread,
Oct 11, 2015, 10:27:22 PM10/11/15
to symfo...@googlegroups.com
Porque forzozamente en una entidad, si no estas utilizando orm, mejor en un servicio. y asi le inyectas lo que necesitas. Ademas se supone que con new funcionario ya estas creando una instancia funcionario, no tiene sentido devolver otra instancia funcionario.

--
--
Has recibido este mensaje porque estás suscrito al grupo "symfony-es" de Google Groups.
Para publicar en este grupo, envía un email a symfo...@googlegroups.com
Para darte de baja, envía un email a symfony-es+...@googlegroups.com
El resto de opciones puedes encontrarlas en http://groups.google.com/group/symfony-es?hl=es

---
Has recibido este mensaje porque estás suscrito al grupo "symfony-es" de Grupos de Google.
Para anular la suscripción a este grupo y dejar de recibir sus mensajes, envía un correo electrónico a symfony-es+...@googlegroups.com.
Para acceder a más opciones, visita https://groups.google.com/d/optout.

Manuel Aguirre

unread,
Oct 13, 2015, 8:12:32 AM10/13/15
to symfo...@googlegroups.com
Es una muy mala practica acceder directamente al contenedor de servicios en cualquier clase que creemos, una clase solo debería depender de las clases/servicios que realmente necesita, no de todo el contenedor para poder funcionar.

Acá explican muy bien el contenedor de servicios, que es, y como se usa: http://librosweb.es/libro/symfony_2_4/capitulo_16.html

Lo ideal es que toda lógica de alguna rutina exista dentro de servicios y no en los controladores, estos servicios se registran en el contenedor de servicios, y se les pasan las dependencias necesarias en cada caso, doctrine, event_dispatcher, logger, doctrine_connection, etc...

Manuel Aguirre.


manuel_j555 | Desarrollador de Software | Optime Consulting | twitter | github | Sitio web


A menudo unas pocas horas de "Prueba y error" podrán ahorrarte minutos de leer manuales.

oyepez003

unread,
Oct 13, 2015, 8:47:21 AM10/13/15
to symfony-es
+1 Manuel Aguirre.

Rafael Estalayo Mateo

unread,
Jan 10, 2017, 1:17:27 PM1/10/17
to symfony-es, symfo...@googlegroups.com, symfony-2...@googlegroups.com, symfony_...@googlegroups.com
La línea de código del siglo. Será una mala práctica, pero después de horas mareándome con servicios e historias, esto me ha salvado la vida. Mil gracias por compartirla.

Diego Mesa

unread,
Jan 31, 2017, 12:12:43 PM1/31/17
to symfony-es, symfo...@googlegroups.com, symfony-2...@googlegroups.com, symfony_...@googlegroups.com
Es una mala linea de codigo ,porque esta detro del modelo, pero asi lo solucione. ( me dice mi cliente entidad2 sea un valor por defecto, en las reglas del negocio ) eso quiere decir que el SICLO DE VIDA(lifecycle)  de entidad1 es nacer y crea una entidad2,  (el encargado de esto es Doctrine)

Para ello se debe estar pendientes (listener) cuando la entidad1 nasca (post-load) y crearle una entidad2.

#service.yml
miapp_listener_entityone_postload
:
 
class: Miapp\EventListener\PostLoadEntityOne
 arguments
: ['@service_container'] # opcional
 tags
:
 
- { name: doctrine.event_listener, event: postLoad }

como argumento le paso contenedor de servicios, para poder llamar mas servicios detro si deseo.
cree la clase 

namespace ....
use ....

class PostLoadEntityOne
{
 
private $container;

 
public function __construct(Container $container) {
    $this
->container = $container;
 
}


 
public function getSubscribedEvents()
 
{
   
return array(
   
'prePersist',
   
'preUpdate',
   
);
 
}

 
public function preUpdate(LifecycleEventArgs $args)
 
{
 $this
->miMetodo($args);
 
}

 
public function prePersist(LifecycleEventArgs $args)
 
{
 $this
->miMetodo($args);
 
}

 
....
 
ya con recibir a $args, puedo acceder a la entidad, (aunque debo validar que tipo de objeto me esta enviado)

public function miMetodo(LifecycleEventArgs $args)
{
   $entity1
= $args->getEntity();
   
if ($entity1 instanceof ClassEntityOne)
   
{
   
# entidad 2 por defecto
    $entity1
->setEntidad2( $this->getEntidad2Default($args));
   
# o si en mi db asocio una entidad por defecto
    $entity1
->setEntidad2($this->getEntidad2DefaultFor($entity1,$args));
   
}
   
...
}

....

private function getEntidad2DefaultFor(ClassEntityOne $entity,
LifecycleEventArgs $args)
{
    $em
= $args->getEntityManager();
    $repository
= $em->getRepository('MiAppBundle:ClassEntityTow');
   
# puedo llamar otro evento
   
$this->container->get('event_dispatcher')->dispatch...  
   
return $entity->getEntidad2();
}



Asi fue como solucione el problema, el codigo es desacoplado, y pued

Jose R. Prieto

unread,
Feb 1, 2017, 5:44:50 AM2/1/17
to symfo...@googlegroups.com
Hombre....

En tu propio código estás poniendo lo que necesitas...


namespace AppBundle\Entity;

class Funcionario
{
    
private $conn;

    
public function __construct($idFuncionario)
    
{
        
// Aqui debo acceder al contenedor de servicio para crear la conexion $conn
        $this
->conn = $conn
        $stmt 
= $conn->query("SELECT...");
        //... crear el objeto funcionario
    
}


Yo veo ahí un private $conn; necesitas NO el EntityManager, sino el Connection de Doctrine; así pues, es lo que debes pasarle al constructor de tu objeto (y yo pensaría si realmente necesitas el Connection...).

Respecto a lo de pasarle un id de entidad para construir el objeto... Como te comentan por ahí, no es una opción muy limpia.

Lo lógico es que tendrías un método loadFuncionario, o similar; o bien, tener un Manager que se encargue de crear los objetos que toquen.

Es decir...

FuncionarioManager -> encargado de crear, consultar, etc....

Funcionario -> (modelo) representa a un funcionario único.

Date cuenta que si lo hicieses de la forma tradicional, tienes, por un lado, la Entidad (Entity), que representa al Modelo puro (Funcionario), y el Repositorio, que es el que usas para hacer las consultas, etc.

Mejor lo vemos en el propio libro de Sf:



// src/AppBundle/Controller/DefaultController.php

// ...
use AppBundle\Entity\Product;
use Symfony\Component\HttpFoundation\Response;

// ...
public function createAction()
{
    $product = new Product();
    $product->setName('Keyboard');
    $product->setPrice(19.99);
    $product->setDescription('Ergonomic and stylish!');

    $em = $this->getDoctrine()->getManager();

    // tells Doctrine you want to (eventually) save the Product (no queries yet)
    $em->persist($product);

    // actually executes the queries (i.e. the INSERT query)
    $em->flush();

    return new Response('Saved new product with id '.$product->getId());
}


Lo interesante de esto, es que, si te fijas, y miras el código de Product, tienes:

// src/AppBundle/Entity/Product.php
namespace AppBundle\Entity;

use Doctrine\ORM\Mapping as ORM;

/**
 * @ORM\Entity
 * @ORM\Table(name="product")
 */
class Product
{
    /**
     * @ORM\Column(type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    private $id;

    /**
     * @ORM\Column(type="string", length=100)
     */
    private $name;

    /**
     * @ORM\Column(type="decimal", scale=2)
     */
    private $price;

    /**
     * @ORM\Column(type="text")
     */
    private $description;
}

Si NO usas el ORM, resulta que no tienes dependencias....

Y para la consulta de objetos...

public function showAction($productId)
{
    $product = $this->getDoctrine()
        ->getRepository('AppBundle:Product')
        ->find($productId);

    if (!$product) {
        throw $this->createNotFoundException(
            'No product found for id '.$productId
        );
    }

    // ... do something, like pass the $product object into a template
}


¿Dependencias de ese código?, evidentemente, Product, y, realmente, el RepositoryManager de Doctrine (ahí lo está haciendo como un controller, pero eso eso otro tema).

Aquí, como crear Repositories custom:



Lo interesante es que, si lo haces separado, la entidad no tendría por que depender ni siquiera de Doctrine; quien dependería de Doctrine, sería el Manager -en mi approach, claro- (Manager también necesitaría a Funcionario, claro...)

Personalmente, yo suelo tener un repository, la entity, y un manager para gestionar todo esto; es decir, el manager se encarga de, en cierta forma, encapsular la lógica que vaya más allá de las condiciones que ha de cumplir el modelo en sí; por ejemplo, que tengo que crear una entidad a partir de un objeto serializado... O tengo que devolver la entidad en un objeto serializado... La propia consulta / carga del objeto, suelo tenerla en un manager; y, si puedo, devolver al controlador -> plantilla, no el objeto, sino un array, para evitar sobrecargar la capa de vista (depende del caso y de la complejidad del modelo en cuestión).

Hecho así, a mayores, puedes plantearte tener una API de tu lógica de negocio, que va a ser "independiente" del resto de tu aplicación, y a la que solamente le vas a tener que pasar una conexión a base de datos (o varias, vamos...); y que vas a poder reutilizar en otros aplicaciones y / o partes de la misma...
(Vamos, la puedes meter como dependencia de composer y cargar como un vendor)

De la otra forma, queda inevitablemente ligada a la aplicación, y "sacarla" de ahí...

Hay una charla muy buena en uno de los deSymfony, sobre todo esto (bueno, hay varias, pero vamos, donde tratan el tema de SOLID, etc):


Esta otra, en la misma línea:


Otras que te pueden venir muy bien:



Todas las de deSymfony:




--
--
Has recibido este mensaje porque estás suscrito al grupo "symfony-es" de Google Groups.
Para publicar en este grupo, envía un email a symfo...@googlegroups.com
Para darte de baja, envía un email a symfony-es+unsubscribe@googlegroups.com

El resto de opciones puedes encontrarlas en http://groups.google.com/group/symfony-es?hl=es

---
Has recibido este mensaje porque estás suscrito al grupo "symfony-es" de Grupos de Google.
Para cancelar la suscripción a este grupo y dejar de recibir sus mensajes, envía un correo electrónico a symfony-es+unsubscribe@googlegroups.com.

Jose R. Prieto

unread,
Feb 1, 2017, 5:46:51 AM2/1/17
to symfo...@googlegroups.com
Como comentario; veo que muchos usáis "demasiado" Symfony, como Framework (no por este hilo, sino por muchos que leo en la lista); y no vais demasiado a la "chicha" del FW, y a la posibilidad de usar los propios componentes de Sf, a vuestro gusto...

Echadle un ojo a esta guía (a mi me fue muy útil en su momento):

Reply all
Reply to author
Forward
0 new messages