Foros del Web » Programando para Internet » PHP » Zend »

Don´t repeat yourself (DRY). Dudas con la reutilización de controladores y acciones.

Estas en el tema de Don´t repeat yourself (DRY). Dudas con la reutilización de controladores y acciones. en el foro de Zend en Foros del Web. Saludos. Tengo un proyecto desarrollado con Zend en el cual varios controladores comparten en común tanto la vista como las acciones. Es decir, el controlador ...
  #1 (permalink)  
Antiguo 04/06/2011, 05:14
 
Fecha de Ingreso: noviembre-2010
Mensajes: 21
Antigüedad: 14 años
Puntos: 1
Don´t repeat yourself (DRY). Dudas con la reutilización de controladores y acciones.

Saludos.

Tengo un proyecto desarrollado con Zend en el cual varios controladores comparten en común tanto la vista como las acciones. Es decir, el controlador A comparte con B,C y D las mismas acciones. Asímismo, todos comparten el mismo template en la vista.

¿Sería posible crear un controlador común, con una vista común y con un grupo de acciones en común, que mostrara información personalizada según los parámetros que recibiese?

Poniendo un ejemplo: si tengo un menú que muestra 20 modelos de coche, cada modelo tiene las mismas acciones, y cada modelo comparte una vista en común, ¿cómo puedo crear una plantilla controlador/acciones/vista que según el modelo de coche muestre una información u otra?, ¿cómo le paso el/los parámetro/s al controlador?

Nota: la información personalizada se extraerá de una BBDD.

Se me ha ocurrido crear un controlador genérico y luego, mediante herencia, extender el resto, pero sigo necesitando crear un controlador personalizado para cada modelo de coche, lo cual no me resuelve el problema.

Una solución que he desarrollado consiste en, mediante un plug-in, interceptar el nombre del controlador y acción en el preDispatch y pasárselos posteriormente al controlador común. Sin embargo, estoy teniendo problemas al implementarla y quizás haya formas más elegantes de resolver el problema.

En fin, espero haberme explicado bien. Muchas gracias por adelantado.

Última edición por nerthalas; 04/06/2011 a las 07:07
  #2 (permalink)  
Antiguo 04/06/2011, 07:55
Avatar de abimaelrc
Colaborador
 
Fecha de Ingreso: mayo-2009
Ubicación: En el planeta de Puerto Rico
Mensajes: 14.734
Antigüedad: 15 años, 6 meses
Puntos: 1517
Respuesta: Don´t repeat yourself (DRY). Dudas con la reutilización de controladores y

Para eso es zend_layout http://framework.zend.com/manual/en/zend.layout.html y el library, para los modelos que se repiten.
__________________
Verifica antes de preguntar.
Los verdaderos amigos se hieren con la verdad, para no perderlos con la mentira. - Eugenio Maria de Hostos
  #3 (permalink)  
Antiguo 04/06/2011, 08:45
 
Fecha de Ingreso: noviembre-2010
Mensajes: 21
Antigüedad: 14 años
Puntos: 1
Respuesta: Don´t repeat yourself (DRY). Dudas con la reutilización de controladores y

Cita:
Iniciado por abimaelrc Ver Mensaje
Para eso es zend_layout [url]http://framework.zend.com/manual/en/zend.layout.html[/url] y el library, para los modelos que se repiten.
Gracias por tu respuesta pero, desde mi desconocimiento, no acabo de ver la relación entre el problema que planteo y Zend_Layout salvo para crear una vista común en el controlador.

Un saludo.
  #4 (permalink)  
Antiguo 04/06/2011, 08:51
Avatar de masterpuppet
Software Craftsman
 
Fecha de Ingreso: enero-2008
Ubicación: Montevideo, Uruguay
Mensajes: 3.550
Antigüedad: 16 años, 10 meses
Puntos: 845
Respuesta: Don´t repeat yourself (DRY). Dudas con la reutilización de controladores y

Que tal nerthalas,

Podrías exponer algo de código para visualizar mejor el problema.

Saludos.
__________________
http://es.phptherightway.com/
thats us riders :)
  #5 (permalink)  
Antiguo 04/06/2011, 09:18
 
Fecha de Ingreso: noviembre-2010
Mensajes: 21
Antigüedad: 14 años
Puntos: 1
Respuesta: Don´t repeat yourself (DRY). Dudas con la reutilización de controladores y

Cita:
Iniciado por masterpuppet Ver Mensaje
Que tal nerthalas,

Podrías exponer algo de código para visualizar mejor el problema.

Saludos.
Desde luego. Te pongo un pequeño resumen:

En el index de mi web aparece una barra lateral de menús con distintas opciones (pongo cuatro pero en realidad hay bastantes más convenientemente formateadas con CSS y Javascript):

Código PHP:
<li><a href="<?php echo $this->url(array('controller'=>'opel''action'=>'index'))?>">Opel</a></li>

<li><a href="<?php echo $this->url(array('controller'=>'seat''action'=>'index'))?>">Seat</a></li>

<li><a href="<?php echo $this->url(array('controller'=>'ford''action'=>'index'))?>">Ford</a></li>

<li><a href="<?php echo $this->url(array('controller'=>'bmw''action'=>'index'))?>">BMW</a></li>
La forma "natural" de gestionar cada enlace sería a través de un controlador X con varias acciones. Tales acciones son comunes para cada controlador, es decir, los cuatro tienen "index", "information" y "características" y "galería" pero se han de mostrar personalizadas para cada controlador (las características de Opel son distintas a las de Seat, por poner un ejemplo)

Código PHP:
class OpelController extends Zend_Controller_Action
{    
    
    public function 
init()
    {
        
    }

    public function 
indexAction()
    {
        
    }
    
    public function 
informacionAction()
    {
        
    }

    public function 
caracteristicasAction()
    {
        
    }

    public function 
galeriaAction()
    {
        
    }


A su vez, cada controlador X tendría una vista para el Index así como para el resto de sus acciones.


Dicho todo lo anterior y siempre y cuando sea posible, estoy buscando la manera de evitar repetir el anterior esquema para, pongamos 50 marcas de coches.

Había pensado en la posibilidad de crear un único controlador/acciones que recibiera un parámetro (la marca del coche) y se personalizase para ese parámetro.

Por ejemplo, que MarcaController reciba un parámetro "Opel" y sus acciones index, información, características y galería muestren en la vista la salida específica que devuelve la BBDD para esa marca.

Asímisimo, también busco la forma de tener un template común para la vista de esas acciones y no tener que repetir código en cada controlador.

Espero haberme explicado bien ahora, con estas cosas uno no siempre acierta a la hora de exponer sus problemas

Muchas gracias por anticipado.
  #6 (permalink)  
Antiguo 04/06/2011, 09:32
Avatar de masterpuppet
Software Craftsman
 
Fecha de Ingreso: enero-2008
Ubicación: Montevideo, Uruguay
Mensajes: 3.550
Antigüedad: 16 años, 10 meses
Puntos: 845
Respuesta: Don´t repeat yourself (DRY). Dudas con la reutilización de controladores y

Es que te estas dando la respuesta,

Cita:
Había pensado en la posibilidad de crear un único controlador/acciones que recibiera un parámetro (la marca del coche) y se personalizase para ese parámetro.

Por ejemplo, que MarcaController reciba un parámetro "Opel" y sus acciones index, información, características y galería muestren en la vista la salida específica que devuelve la BBDD para esa marca.
como lo has dicho es como lo deberías hacer, el controller recibe por parámetro la marca, es mas como siempre vas a precisar una marca, te puedes hacer un plugin que al detectar el controller(MarcaController para tomar tu ejemplo) automáticamente busque la marca en la persistencia y la inyecte en el fc o bien en el controller.

Saludos.
__________________
http://es.phptherightway.com/
thats us riders :)
  #7 (permalink)  
Antiguo 04/06/2011, 11:55
 
Fecha de Ingreso: noviembre-2010
Mensajes: 21
Antigüedad: 14 años
Puntos: 1
Respuesta: Don´t repeat yourself (DRY). Dudas con la reutilización de controladores y

Cita:
Iniciado por masterpuppet Ver Mensaje
Es que te estas dando la respuesta,



como lo has dicho es como lo deberías hacer, el controller recibe por parámetro la marca, es mas como siempre vas a precisar una marca, te puedes hacer un plugin que al detectar el controller(MarcaController para tomar tu ejemplo) automáticamente busque la marca en la persistencia y la inyecte en el fc o bien en el controller.

Saludos.
Entiendo tu solución pero tengo ciertas lagunas sobre cómo aplicarla. Verás, yo había intentado resolver el problema creando el siguiente plug-in:

Código PHP:
class My_Controller_Plugin_ControllerCheck extends Zend_Controller_Plugin_Abstract
{
    public function 
preDispatch(Zend_Controller_Request_Abstract $request
    {
        
//Intercepta el nombre del controlador y su accion
        //Por ejemplo, al pinchar en el enlace
        // $this->url(array('controller'=>'opel', 'action'=>'index'))
        
        
$controllerName $this->getRequest()->getControllerName();
        
$actionName $this->getRequest()->getActionName();
        
        
//Array donde se almacenan los nombres de los controladores
        
$controladores = array('seat','opel','bmw','ford');
        
        
        
//Compruebo si el nombre del controlador está en el array
        //En caso positivo redirijo la $request al controlador genérico
        //pasándole como parámetro el nombre del controlador inicial,
        //por ejemplo, ('controller'=>'opel', 'action'=>'index')
        
if(in_array($controllerName$controladores)) {
            
$request->setControllerName('marca');
            
$request->setActionName('index');
            
$request->setParam('nombre'$controllerName);
        }
        
        
    }

¿Cuál es el problema? Que tan sólo recibe el parámetro con el nombre de la marca la acción Index, es decir, el resto de acciones no reciben nada. Entonces, dicho lo anterior, no sé muy bien cómo implementar la solución que propones.
  #8 (permalink)  
Antiguo 04/06/2011, 13:47
Avatar de GatorV
$this->role('moderador');
 
Fecha de Ingreso: mayo-2006
Ubicación: /home/ams/
Mensajes: 38.567
Antigüedad: 18 años, 6 meses
Puntos: 2135
Respuesta: Don´t repeat yourself (DRY). Dudas con la reutilización de controladores y

Puedes en ese caso mejor hacer un custom route, donde tengas la ruta http://app.local/marcas/:marca/:action donde vayas al controller marcas y tengas siempre disponible el parámetro de la marca en la url y puedas moverte por diferentes acciones por ejemplo: http://app.local/marcas/opel/listar, http://app.local/marcas/ford/galeria etc.

Saludos.
  #9 (permalink)  
Antiguo 06/06/2011, 05:08
 
Fecha de Ingreso: noviembre-2010
Mensajes: 21
Antigüedad: 14 años
Puntos: 1
Respuesta: Don´t repeat yourself (DRY). Dudas con la reutilización de controladores y

Finalmente he encontrado una solución que me permite reutilizar el mismo controlador y personalizar su salida según el parámetro "Marca" que reciba. De esta manera no tengo que repetir el mismo código para cada marca de coche que gestione en mi web. Masterpuppet me puso sobre la pista, todo hay que decirlo.

Expongo el resultado para compartirlo con el foro:

Comencemos por los enlaces a las distintas marcas que aparecen en el menú del index principal de la web. Cuando alguien pincha en alguno de los enlaces el plug-in ControllerCheck intercepta el $request y lo gestiona según lo expuesto más adelante.

Código PHP:
<li><a href="<?php echo $this->url(array('controller'=>'opel''action'=>'index'))?>">Opel</a></li> 

<li><a href="<?php echo $this->url(array('controller'=>'seat''action'=>'index'))?>">Seat</a></li> 

<li><a href="<?php echo $this->url(array('controller'=>'ford''action'=>'index'))?>">Ford</a></li> 

<li><a href="<?php echo $this->url(array('controller'=>'bmw''action'=>'index'))?>">BMW</a></li>

ControllerCheck incercepta el $request, comprueba si el nombre del controlador está en un array de nombres, y tras ello lo redirige a un controlador genérico "marca" inyectándole en el frontController un parámetro con el nombre de la marca.

Código PHP:
class My_Controller_Plugin_ControllerCheck extends Zend_Controller_Plugin_Abstract
{
    public function 
preDispatch(Zend_Controller_Request_Abstract $request
    {
        
//Guarda el nombre del controlador original en una variable
        
$controllerName $this->getRequest()->getControllerName();
        
        
//Array donde se guardan los nombres a "interceptar"
        
$controladores = array('seat','opel','bmw','ford');
        
        
//Si el nombre del controlador original está en el array....
        
if(in_array($controllerName$controladores))
        {
            
//Redirijo la $request al controlador genérico
            //inyectándole como parámetro al front Controller 
            //el nombre del controlador inicial
            
$request->setControllerName('marca');
            
$nombre Zend_Controller_Front::getInstance();
            
$nombre->setParam('nombre'$controllerName);
        }
        
    }



Controlador genérico "marca" que recibe el parámetro del frontController dentro de sus acciones:

Código PHP:
class MarcaController extends Zend_Controller_Action
{
            
    public function 
init()
    {
        
    }

    public function 
indexAction()
    {
      
//Recoge el nombre del controlador original inyectado en el frontController
        
$nombre $this->getFrontController()->getParam('nombre');
        
//Envía el nombre de la marca a la vista
        
$this->view->nombre $nombre;      
    }
    
    public function 
informacionAction()
    {
        
$nombre $this->getFrontController()->getParam('nombre');
        
//Envía el nombre de la marca a la vista
        
$this->view->nombre $nombre
    }
    
    public function 
galeriaAction()
    {
        
$nombre =$this->getFrontController()->getParam('nombre');
        
//Envía el nombre de la marca a la vista
        
$this->view->nombre $nombre
    }



En la vista Index del controlador genérico Marca muestro los enlaces personalizados de las distintas acciones según el parámetro recibido:

Código PHP:
<a href="<?php echo $this->url(array('controller'=>"$this->nombre"'action'=>'informacion'))?>">Informacion</a>
<br />
<a href="<?php echo $this->url(array('controller'=>"$this->nombre"'action'=>'galeria'))?>">Galeria</a>
Finalmente, en las vistas concretas de las acciones también puedo enviar el nombre de la marca si hiciera falta

Por ejemplo, información.phtml
Código PHP:
echo $this->nombre

¿Qué desventaja veo así a bote pronto? Almacenar los nombres de los controladores a interceptar dentro de un array. Si son varias decenas no hay problema, pero, ¿qué pasaría si fueran 500? Pues probablemente la mejor solución fuese almacenarlos en una BBDD y hacer una consulta. Sin embargo, conviene tener en cuenta la sobrecarga de peticiones que puede producir en el servidor.

No he mirado la sugerencia que me propusiste, GatorV, aunque me gustaría saber qué opináis aquellos que tenéis más experiencia con Zend sobre la solución que he creado para el problema. Sobre todo si le véis algún inconveniente grave.

Un saludo.
  #10 (permalink)  
Antiguo 06/06/2011, 06:52
Avatar de masterpuppet
Software Craftsman
 
Fecha de Ingreso: enero-2008
Ubicación: Montevideo, Uruguay
Mensajes: 3.550
Antigüedad: 16 años, 10 meses
Puntos: 845
Respuesta: Don´t repeat yourself (DRY). Dudas con la reutilización de controladores y

A ver, creo que no me has entendido bien, es 1 controller para manejar las marcas y pasas como parámetro en la uri la marca.

view
Código HTML:
Ver original
  1. <li>
  2.     <a href="<?php echo $this->url(array(
  3.                                         'controller' => 'vendors',
  4.                                         'name' => 'opel')); ?>" >Opel</a>
  5. </li>  
  6. <li>
  7.     <a href="<?php echo $this->url(array(
  8.                                         'controller' => 'vendors',
  9.                                         'name' => 'seat')); ?>" >Seat</a>
  10. </li>

controller

Código PHP:
Ver original
  1. class VendorsController extends Zend_Controller_Action
  2. {
  3.              
  4.     public function init()
  5.     {}
  6.  
  7.     public function indexAction()
  8.     {
  9.         $vendor = $this->_getParam('name');
  10.         $this->view->vendor = $vendor;      
  11.     }
  12.      
  13.     ...
  14.  
  15. }

en tu caso como en todas las acciones tienes que controlar la marca, puedes hacer un plugin que la cargue y sete al FrontController|Request el modelo, es una forma de resolverlo, puedes tambien hacerlo en cada uno de los actions, en el preDispatch, el init, hay muchas formas de resolverlo, si fuera un plugin, podria ser algo asi:

Código PHP:
Ver original
  1. class My_Controller_Plugin_VendorLoader extends Zend_Controller_Plugin_Abstract
  2. {
  3.     public function preDispatch(Zend_Controller_Request_Abstract $request)  
  4.     {          
  5.         $controller = strtolower($request->getControllerName());       
  6.         if('vendor' == $controller) {
  7.             $service = My_Service_Abstract::load('Vendor');
  8.             $vendor  = $service->find($request->getParam('name'));
  9.             if(false === $vendor) {
  10.                 throw new VendorNotFoundException();
  11.             }          
  12.             $fc = Zend_Controller_Front::getInstance();            
  13.             $fc->setParam('vendor', $vendor);
  14.         }                
  15.     }
  16. }

luego en el controller en vez de obtener el name, obtienes directamente el modelo

Código PHP:
Ver original
  1. public function indexAction()
  2. {
  3.     $vendor = $this->_getParam('vendor');
  4.     $this->view->vendor = $vendor;      
  5. }

esto te va a generar url mas largas, algo asi:

project.local/vendors/index/name/seat
project.local/vendors/info/name/seat
project.local/vendors/gallery/name/seat

y ahi es donde entra en juego el router, lo que te comento GatorV, para hacer seo url's, y que queden mas o menos así:

project.local/vendors/seat
project.local/vendors/seat/info
project.local/vendors/seat/gallery

Saludos.
__________________
http://es.phptherightway.com/
thats us riders :)
  #11 (permalink)  
Antiguo 06/06/2011, 07:56
Avatar de GatorV
$this->role('moderador');
 
Fecha de Ingreso: mayo-2006
Ubicación: /home/ams/
Mensajes: 38.567
Antigüedad: 18 años, 6 meses
Puntos: 2135
Respuesta: Don´t repeat yourself (DRY). Dudas con la reutilización de controladores y

Muy buena solución la que expone Masterpuppet, con eso lo que haces es aplicar el principio de DI (Dependency Injection) así tu controller marcas no le importa con que marca va a trabajar, el chiste es que tenga un modelo adecuado para trabajar que se adhiera a una interfaz en concreto para que funcione
  #12 (permalink)  
Antiguo 06/06/2011, 08:27
 
Fecha de Ingreso: noviembre-2010
Mensajes: 21
Antigüedad: 14 años
Puntos: 1
Respuesta: Don´t repeat yourself (DRY). Dudas con la reutilización de controladores y

Cita:
Iniciado por masterpuppet Ver Mensaje
A ver, creo que no me has entendido bien, es 1 controller para manejar las marcas y pasas como parámetro en la uri la marca.

....................
En cierta forma tampoco me he alejado tanto de tu solución, pienso yo. Veamos, mi solución pasa por comprobar si el controlador pertenece a un grupo y en caso positivo redirijo el $request al controlador genérico pasando como parámetro el nombre del controlador original. En tu caso compruebas si el el controlador es "Vendor" y en caso positivo chequéas, intuyo mediante un "servicio" externo, que el parámetro asociado "name" existe en un almacenamiento y posteriormente lo inyectas en el frontController.

Cita:
Iniciado por GatorV Ver Mensaje
Muy buena solución la que expone Masterpuppet, con eso lo que haces es aplicar el principio de DI (Dependency Injection) así tu controller marcas no le importa con que marca va a trabajar, el chiste es que tenga un modelo adecuado para trabajar que se adhiera a una interfaz en concreto para que funcione
Entiendo, pero, dicho con la máxima humildad, en el controller que yo he propuesto pasa lo mismo, ¿no? Quiero decir, es un controller genérico con unas acciones genéricas que reciben como parámetro una marca y devuelven un resultado personalizado para esa marca. Al controlador no le importa que la marca sea X, Y o Z, puesto que responde según lo que reciba.

Un saludo y muchas gracias a los dos por vuestro tiempo.
  #13 (permalink)  
Antiguo 06/06/2011, 08:44
Avatar de GatorV
$this->role('moderador');
 
Fecha de Ingreso: mayo-2006
Ubicación: /home/ams/
Mensajes: 38.567
Antigüedad: 18 años, 6 meses
Puntos: 2135
Respuesta: Don´t repeat yourself (DRY). Dudas con la reutilización de controladores y

Es una forma simple de hacerlo, ya que tu estas definiendo un array de marcas, y comprobando, y si bien funciona, hay formas mas "elegantes" de hacerlo.

La solución que expone masterpuppet, usa un servicio de intermedio donde compruebas si la marca es válida o no, y luego se la inyectas a tu controller para que haga el trabajo (directamente con el modelo).

Con lo que te expuse yo, puedes hacerlo de una forma aún más sencilla sin el plugin ya que con la pura ruta puedes persistir el valor de la marca, aunque me gusta más la idea de Masterpuppet de usar un service intermedio para verificar antes y posteriormente usar la ruta solo para maquetar la URL.

Saludos.
  #14 (permalink)  
Antiguo 06/06/2011, 08:50
 
Fecha de Ingreso: noviembre-2010
Mensajes: 21
Antigüedad: 14 años
Puntos: 1
Respuesta: Don´t repeat yourself (DRY). Dudas con la reutilización de controladores y

Cita:
Iniciado por GatorV Ver Mensaje
Es una forma simple de hacerlo, ya que tu estas definiendo un array de marcas, y comprobando, y si bien funciona, hay formas mas "elegantes" de hacerlo.

La solución que expone masterpuppet, usa un servicio de intermedio donde compruebas si la marca es válida o no, y luego se la inyectas a tu controller para que haga el trabajo (directamente con el modelo).

Con lo que te expuse yo, puedes hacerlo de una forma aún más sencilla sin el plugin ya que con la pura ruta puedes persistir el valor de la marca, aunque me gusta más la idea de Masterpuppet de usar un service intermedio para verificar antes y posteriormente usar la ruta solo para maquetar la URL.

Saludos.
Desde luego el array no es la manera más elegante de hacerlo pero no veas el suspiro de alivio que solté esta mañana cuando vi que funcionaba tras varios días comiéndome la cabeza con ello .

Investigaré más sobre el uso de Zend_Route. Es una parte del framework que desconozco por completo.

Un saludo.

Etiquetas: acciones, controladores, dudas, frameworks-y-php-orientado-a-objetos, repeat
Atención: Estás leyendo un tema que no tiene actividad desde hace más de 6 MESES, te recomendamos abrir un Nuevo tema en lugar de responder al actual.
Respuesta

SíEste tema le ha gustado a 2 personas




La zona horaria es GMT -6. Ahora son las 11:26.