Este foro ya no está activo, así que no puedes publicar nuevas preguntas ni responder a las preguntas existentes.

Cómo crear una respuesta llamando a varios controladores en una aplicación Symfony

11 de diciembre de 2014

Hola! Estoy intentando que a partir de una ruta dada conseguir encadenar la llamada a más de un controlador.action. Dando bandazos sin éxito

Pero, no me refiero a realizar un controller->forward() o llamando a controller->action() desde la plantilla. En el primer caso porque ya nos retorna un objeto Response (no HTML que pueda ser parte del Response), y en el segundo porque pienso que las plantillas no deben realizar lógica de negocio.

El propósito de esto es anidar las llamadas para que una acción padre reciba como parámetro html procesado antes en otro bundle anidado/hijo.

Podría conseguirse si el componente router devolviese una pila de llamadas a controladores en los que tuviera 'match' de la colección de rutas de cada bundle (con la restricción por ejemplo de un Controller.Action por Bundle cómo máximo)

Lo malo es que no sabría cómo asociar/definir que el HTML procesado en una acción para pasar como parámetro de una nueva llamada a otra acción.

(En mi humilde opinión esto creo que es un comportamiento natural a la hora de reutilizar un bundle de una aplicación dentro de otra, pero no soy capaz de representarlo).

Si por favor alguien pudiera aportar su ayuda. Muchas gracias y un saludo.


Respuestas

#1

Si en vez de controladores habláramos de servicios, este problema sería trivial. Gracias al event dispatcher podrías lanzar eventos en el controlador padre y todos los servicios hijo que están escuchando esos eventos podrían modificar la respuesta.

El sistema de eventos es bastante avanzado, por lo que puedes modificar sin límites la respuesta, su contenido, sus cabeceras, etc. También puedes hacer que un hijo detenga la cadena de ejecución y devuelva la respuesta al usuario, sin permitir que otros servicios hijo sigan modificando la respuesta.

Si es obligatorio utilizar los controladores, una posible solución es utilizar la anotación @Template en todos los controladores. De esta manera, tus controladores no renderizan plantillsa ni devuelven objetos de tipo Response. Así que desde un controlador puedes llamar a otro (ejemplo: $this->indexAction()) y como la respuesta del controlador va a ser un array con variables, puedes modificar ese array como quieras antes de renderizar la plantilla. Esta solución la he visto alguna vez en aplicaciones Symfony. Aunque funciona bien, personalmente me parece una solución frágil y poco profesional.

@javiereguiluz

11 diciembre 2014, 21:43
#2

Gracias @javiereguiluz por tu pronta y detallada respuesta.

No es obligatorio utilizar controladores, solamente encontrar la solución más apropiada o lógica.

Voy a estudiar para implementar la solución utilizando servicios, tal y como has sugerido. Seguro que no tardaré mucho en aparecer por aquí con alguna duda...

Muchas gracias y un saludo.

@mabuitragor

12 diciembre 2014, 10:56
#3

Por cierto, mostrando un escenario real de lo anterior añado:

Supongo que a más de uno se le habrá dado el caso de, por ejemplo, tener un Bundle 'Blog', desacoplado, que se reutiliza en un nuevo proyecto WEB 'BundleWEB', dónde por ejemplo toda la funcionalidad de Blog se muestra dentro de la estructura básica de la WEB.

Creo que es una situación bastante habitual, al menos para mí lo era antes de llegar a Symfony2 y ahora tengo dificultades para migrar proyectos, al no saber representar esta anidación de aplicaciones.

@mabuitragor

12 diciembre 2014, 11:09
#4

Tienes razón en que el caso que comentas es habitual en el mundo web. De hecho, la clave de los bundles de Symfony es precisamente ese concepto. ¿Cómo se traduce esto en una aplicación real?

  1. Instalas un bundle de terceros para que te añada la funcionalidad del blog en la aplicación (aquí tienes todos los bundles de tipo blog para Symfony)
  2. Si quieres personalizar alguna parte de ese bundle (plantillas, controladores, etc.) puedes hacerlo de dos maneras:
    • Creando un nuevo bundles propio que extienda el anterior, con lo que puedes machacar cualquiera de sus funcionalidades (en este artículo se explica cómo hacerlo).
    • Si sólo quieres redefinir las plantillas para que se adapten a tu diseño, es todavía más fácil. No tienes que crear ningún bundle ni complicarte con herencia de bundles. Simplemente crea en el directorio app/Resources/views/ una plantilla con el mismo nombre y la misma ruta que la que quieres cambiar y Symfony usará la tuya en vez de la del bundle (en este artículo se explica cómo hacerlo).

Todo lo anterior también funciona para tus propios bundles, así que no siempre es necesario utilizar bundles de terceros para conseguir esto. Puedes crear tus propios bundles para compartirlos entre varias de tus aplicaciones.

@javiereguiluz

12 diciembre 2014, 11:17
#5

¡Hola de nuevo! Tras estudiar todavía un poco más todo.

En conclusión, con Symfony no puedo representar el escenario de la manera que consideraba más 'lógica' SIN construir un mecanismo 'extra' que añada la funcionalidad comentada.

Diría que el problema radica en la restricción de asociar UNA ruta a UNA sola acción por parte del componente Router de Symfony. La implementación de ese mecanismo 'extra' podría ser un ¿servicio? que apile una secuencia de llamadas 'controller.action'. Esto podría conseguirse desde la definición de rutas, por ejemplo si pudiéramos decir que un parámetro de entrada del controlador se obtiene en el proceso de otro Bundle.

El servicio con esa premisa debería obtener la secuencia en orden descendente al analizar la ruta y si hay algún parámetro que se obtenga como llamada a otro Bundle realizar la misma tarea de routing dentro del nuevo bundle. Sucesivamente hasta que no haya más ocurrencias de este tipo. Luego el servicio realizaría un recorrido en orden ascendente (sacando elementos de la pila) realizando las llamadas para procesarlas y almacenar la respuesta 'String' y no objeto Response debidamente asociadas a los parámetros de otras llamadas. La última ejecución a un controlador sí devolvería el objeto response.

Creo que de manera parecida surgió PHPstack (posiblemente lo conozcas). Pero esto creo que NO puede incorporarse a Symfony (sólo a Silex). Y sobre todo, podemos verlo en el componente url-map que incorpora: https://github.com/stackphp/url-map

Cuando vemos la reutilización de Bundles, cualquiera por ejemplo del enlace que has aportado, el mecanismo para incorporarlos pasa (resumiendo) por registrarlos en la correspondiente aplicación, incorporar sus definiciones de rutas etc, y que las plantillas del Bundle hereden de la plantilla base de la aplicación que lo 'incorpora'.

¿Qué ocurre cuando no se trata sólo de decorar con un layout, si quiero incorporarlo para que sea una parte de lo que se produce en una acción del Bundle 'principal'? Pues no podría, tendría que programar la plantilla base para que sepa obtener 'sus partes' típicamente con llamadas a otras acciones o añadir más herencia de plantillas. Desde el principio que comencé con Symfony pensaba que esto se podría representar como yo venía haciendo con mi viejo framework desde hace años, y me ha lastrado a dar vueltas y vueltas porque daba por hecho que esto también tenía sentido en Symfony.

Bueno, a lo mejor piensas que estoy obcecado con esta idea, pero tras pensarlo mucho creo que Symfony sería más correcto si incluyera de manera natural este comportamiento.

Un saludo y gracias por tus aportes.

@mabuitragor

16 diciembre 2014, 14:10
#6

Como tú mismo dices, creo que el verdadero problema es que intentas aplicar la filosofía del anterior framework en Symfony. No digo que el anterior esté mal y Symfony esté bien. Digo que los dos son diferentes y que cuando usas un framework debes adaptarte a el. Por suerte, Symfony es uno de los frameworks que menos te obligan a adaptarte, ya que es realmente flexible.

En cualquier caso, sigo pensando que, aunque no conozco bien tu proyecto, la forma "correcta" de resolver este problema en Symfony sería el uso de eventos.

@javiereguiluz

18 diciembre 2014, 7:43