Extendiendo Flash Messenger Action Helper

Por zsamer en Julio 22, 2008

En este articulo vamos a ver una herramienta bastante común en el desarrollo de aplicaciones web, llamados mensajes entre request (Petición).

Comúnmente nuestros desarrollos tienen la necesidad de notificar al usuario sobre éxitos, aviso, advertencia y/o errores de un determinado proceso de nuestra lógica de negocio, es aquí donde entra en acción el Action Helper FlashMessenger, sin embargo los mensajes no los trata por separados, es decir no los clasifica según su origen (éxitos, aviso, advertencia y/o errores), si aún sigues interesado en como dar una mayor funcionalidad y potencialidad a esta herramienta éste es el articulo que estabas buscando, continua leyendo.

Un ejemplo clásico sería el siguiente:

PHP:
  1. try {
  2.             $user = $userModel->find($userId)->current();
  3.            
  4.             if($user === null){
  5.                 $this->_flashMessenger->addError('Usuario no encontrado, por favor seleccione un Id existente en la base de datos.')
  6.                 $this->_redirect('/usuarios/index/list/');
  7.             }
  8.            
  9.             $user->invalid_credential = ($user->invalid_credential == 0)? 1: 0;
  10.             $user->save();
  11.             $this->_flashMessenger->addSuccess('Actualización completada con éxito');
  12.         } catch (Exception $e) {
  13.             $this->_flashMessenger->addError('Error en la actualización. Por favor insista más tarde.')
  14.                                   ->addError($e->getMessage());
  15.         }
  16.  
  17.         $this->_redirect('/usuarios/index/list/');

Nos damos cuenta que el manejo de error esta bien controlado y clasificado, si ocurre algún problema ya sea que el id del usuario no exista en la base de datos o lanzó una excepción al tratar de actualizar el registro por A,B o C motivo entonces el mensaje sera clasificado como error y agregado mediante el método addError() de nuestra clase Helper Action FlashMessenger, muy por el contrario si todo salio como debiera ser entonces el mensaje será un éxito mediante el método addSuccess().

Simple y elegante, bueno la idea que propongo es extender FlashMessenger de Zend Framework para dar esta característica y además de entregarnos un output XHTML con los estilos css correspondiente al mensaje, el output sera manejado por la vista, si señor como debiera ser, a través de un Helper View.

El Helper View al cúal llamaremos FlashMessenger (obvio) tendrá la misión de imprimir la salida (render) de los mensajes con sus respectivos XHTML/CSS en nuestro(s) layout (Zend_Layout), cómo lo hará, muy sencillo llamando la instancias de nuestra clase Helper Action FlashMessenger de manera estática obtenemos los mensajes del request anterior y etc. ver más abajo.

A continuación están disponible las clases mencionadas:

Zsamer_Controller_Action_Helper_ZsamerFlashMessenger:

PHP:
  1. require_once 'Zend/Controller/Action/Helper/FlashMessenger.php';
  2.  
  3. class Zsamer_Controller_Action_Helper_ZsamerFlashMessenger extends Zend_Controller_Action_Helper_FlashMessenger
  4. {
  5.     const ERROR     = 'error';
  6.     const WARNING   = 'warning';
  7.     const NOTICE    = 'notice';
  8.     const SUCCESS   = 'success';
  9.    
  10.     /**
  11.      * $_namespace - Instance namespace, default is 'default'
  12.      *
  13.      * @var string
  14.      */
  15.     protected $_namespace = 'default';
  16.  
  17.  
  18.     public function addError($message, $class = null, $method = null)
  19.     {
  20.         return $this->_addMessage($message, self::ERROR, $class, $method);
  21.     }
  22.  
  23.     public function addSuccess($message, $class = null, $method = null)
  24.     {
  25.         return $this->_addMessage($message, self::SUCCESS, $class, $method);;
  26.     }
  27.    
  28.     public function addWarning($message, $class = null, $method = null)
  29.     {
  30.         return $this->_addMessage($message, self::WARNING, $class, $method);;
  31.     }
  32.  
  33.     public function addNotice($message, $class = null, $method = null)
  34.     {
  35.         return $this->_addMessage($message, self::NOTICE, $class, $method);;
  36.     }
  37.    
  38.     protected function _addMessage($message, $type, $class = null, $method = null)
  39.     {
  40.         if (self::$_messageAdded === false) {
  41.             self::$_session->setExpirationHops(1, null, true);
  42.         }
  43.  
  44.         if (!is_array(self::$_session->{$this->_namespace})) {
  45.             self::$_session->{$this->_namespace}[$type] = array();
  46.         }
  47.  
  48.         self::$_session->{$this->_namespace}[$type][] = $this->_factory($message, $type, $class, $method);
  49.  
  50.         return $this;
  51.     }
  52.    
  53.     protected function _factory($message, $type, $class = null, $method = null)
  54.     {
  55.         $messg = new stdClass();
  56.         $messg->message = $message;
  57.         $messg->type = $type;
  58.         $messg->class = $class;
  59.         $messg->method = $method;
  60.         return $messg;
  61.     }
  62.    
  63.     /**
  64.      * getMessages() - Get messages from a specific namespace
  65.      *
  66.      * @param unknown_type $namespace
  67.      * @return array
  68.      */
  69.     public function getMessages($type = null)
  70.     {
  71.         if($type === null){
  72.             return parent::getMessages();
  73.         }
  74.        
  75.         if (isset(self::$_messages[$this->_namespace][$type])) {
  76.             return self::$_messages[$this->_namespace][$type];
  77.         }
  78.  
  79.         return array();
  80.     }
  81.  
  82.     /**
  83.      * getCurrentMessages() - get messages that have been added to the current
  84.      * namespace within this request
  85.      *
  86.      * @return array
  87.      */
  88.     public function getCurrentMessages($type = null)
  89.     {
  90.         if($type === null){
  91.             return parent::getCurrentMessages();
  92.         }
  93.  
  94.         if (isset(self::$_session->{$this->_namespace}[$type])) {
  95.             return self::$_session->{$this->_namespace}[$type];
  96.         }
  97.  
  98.         return array();
  99.     }
  100. }

Zsamer_View_Helper_FlashMessenger:

PHP:
  1. class Zsamer_View_Helper_FlashMessenger extends Zend_View_Helper_FormElement
  2. {
  3.     private $_types = array(
  4.     Zsamer_Controller_Action_Helper_ZsamerFlashMessenger::ERROR,
  5.     Zsamer_Controller_Action_Helper_ZsamerFlashMessenger::WARNING,
  6.     Zsamer_Controller_Action_Helper_ZsamerFlashMessenger::NOTICE,
  7.     Zsamer_Controller_Action_Helper_ZsamerFlashMessenger::SUCCESS
  8.     );
  9.  
  10.     public function flashMessenger()
  11.     {
  12.         $flashMessenger = Zend_Controller_Action_HelperBroker::getStaticHelper('ZsamerFlashMessenger');
  13.  
  14.         $html = '';
  15.  
  16.         foreach ($this->_types as $type) {
  17.             $messages = $flashMessenger->getMessages($type);
  18.  
  19.             if (!$messages){
  20.                 $messages = $flashMessenger->getCurrentMessages($type);
  21.             }
  22.  
  23.             if ($messages) {
  24.                 if ( !$html ) {
  25.                     $html .= '<ul class="messages">';
  26.                 }
  27.                 $html .= '<li class="' . $type . '-msg">';
  28.                 $html .= '<ul>';
  29.  
  30.                 foreach ( $messages as $message ) {
  31.                     $html.= '<li>';
  32.                     $html.= $message->message;
  33.                     $html.= '</li>';
  34.                 }
  35.                 $html .= '</ul>';
  36.                 $html .= '</li>';
  37.             }
  38.         }
  39.         if ( $html) {
  40.             $html .= '</ul>';
  41.         }
  42.         return $html;
  43.     }
  44. }

Ejemplo de uso:

PHP:
  1. class Usuarios_IndexController extends Zend_Controller_Action
  2. {
  3.     /**
  4.      * FlashMessenger
  5.      *
  6.      * @var Zend_Controller_Action_Helper_FlashMessenger
  7.      */
  8.     protected $_flashMessenger = null;
  9.    
  10.     public function init(){
  11.         parent::init();
  12.                 //Crumbs Pendiente próximo Artículo
  13.         //$this->createBreadCrumbs();
  14.         $this->_flashMessenger = $this->_helper->getHelper('ZsamerFlashMessenger');
  15.     }
  16.  
  17.     public function invalidCredentialAction()
  18.     {
  19.         if( !$userId = $this->getRequest()->getParam('id'))
  20.         {
  21.             $this->_flashMessenger->addError('Debe ingresar un ID de usuario v&aacute;lido');
  22.             $this->_redirect('/usuarios/index/list/');
  23.         }
  24.        
  25.         $userModel = new User();
  26.        
  27.         try {
  28.             $user = $userModel->find($userId)->current();
  29.            
  30.             if($user === null){
  31.                 $this->_flashMessenger->addError('Usuario no encontrado, por favor seleccione un Id existente en la base de datos.')
  32.                 $this->_redirect('/usuarios/index/list/');
  33.             }
  34.            
  35.             $user->invalid_credential = ($user->invalid_credential == 0)? 1: 0;
  36.             $user->save();
  37.             $this->_flashMessenger->addSuccess('Actualizaci&oacute;n completada con &eacute;xito');
  38.         } catch (Exception $e) {
  39.             $this->_flashMessenger->addError('Error en la actualización. Por favor insista m&aacute;s tarde.')
  40.                                   ->addError($e->getMessage());
  41.         }
  42.  
  43.         $this->_redirect('/usuarios/index/list/');
  44.     }
  45.  
  46. }

En nuestro layout agregamos la salida de los mensajes sobre el contenido de las acciones:

PHP:
  1. <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
  2. <html xmlns="http://www.w3.org/1999/xhtml">
  3. <head>
  4. <?php echo $this->headTitle($this->sitename) ?>
  5. <link rel="StyleSheet" href="/public/css/style.css" type="text/css" />
  6. </head>
  7. <body>
  8. <div id="contenedor">
  9. <div id="messages"><?php echo $this->flashMessenger(); ?></div>
  10. <?php echo $this->layout()->content; ?>
  11. </div>
  12. </body>
  13. </html>

Finalmente el css:

CSS:
  1. /* MESSAGES
  2. *******************************************************************************/
  3.  
  4. .error,
  5. a.error span,
  6. .required,
  7. .validation-advice      { color:#D40707 !important; font-style:bold !important; }
  8. .notice                 { color:#ea7601}
  9. .messages ul            { border:0 !important; }
  10. .messages li {
  11.     min-height:23px !important;
  12.     margin-bottom:11px !important;
  13.     padding:8px 8px 2px 32px !important;
  14.     font-size:.95em !important;
  15.     font-weight:bold !important;
  16.     list-style:none;
  17.     }
  18. .messages ul li {
  19.     margin:0 0 3px 0 !important;
  20.     border:0 !important;
  21.     padding:0 !important;
  22.     }
  23. .error-msg {
  24.     border:1px solid #f16048 !important;
  25.     color:#df280a !important;
  26.     background:#faebe7 url(../images/error_msg_icon.gif) no-repeat 10px 10px !important;
  27.     }
  28. .success-msg {
  29.     border:1px solid #95a486 !important;
  30.     color:#3d6611 !important;
  31.     background:#eff5ea url(../images/success_msg_icon.gif) no-repeat 10px 10px !important;
  32.     }
  33. .notice-msg {
  34.     border:1px solid #ffd967 !important;
  35.     background:#fffbf0 url(../images/note_msg_icon.gif) no-repeat 10px 10px !important;
  36.     color:#3d6611 !important;
  37.     }
  38. .warning-msg {
  39.     border:1px solid #666e73 !important;
  40.     background:#e6e6e6 url(../images/warning_msg_icon.gif) no-repeat 10px 10px !important;
  41.     color:#000000 !important;
  42.     }

Y por supuesto, nos faltaba agregar las imágenes o iconos representantes de los mensajes:


  

  

  

Imagen de ejemplo de éxito

Imagen de ejemplo de error

Espero que sea de utilidad, personalmente lo encuentro muy funcional y ágil para el manejo de errores de nuestra aplicación, bueno y también de éxitos, advertencias, avisos.

Lamentablemente hemos llegado al fin de nuestro articulo pero nos vemos en el próximo igual que siempre :-) .

Comentarios

33 Responses to “Extendiendo Flash Messenger Action Helper”

  1. Enrique Place on Julio 22nd, 2008 4:29 am

    Muy buen artículo, como siempre.

    Tendremos que agregarlo a los proyectos “surforce” ;-)

    PD: ya te iba a mandar un email ya que hacía demasiado tiempo que no leía un post nuevo de tu autoría ;-)

  2. zsamer on Julio 22nd, 2008 1:30 pm

    Gracias Enrique, como siempre es un gusto compartir conocimiento.

  3. langos on Agosto 11th, 2008 7:36 pm

    Hola,

    me da el siguiente error:

    Fatal error: Uncaught exception ‘Zend_View_Exception’ with message ‘helper ‘FlashMessenger’ not found in path’ in C:\xampp\htdocs\aptana\inmo\library\Zend\View\Abstract.php:1004 Stack trace: #0 C:\xampp\htdocs\aptana\inmo\library\Zend\View\Abstract.php(497): Zend_View_Abstract->_loadClass(’helper’, ‘FlashMessenger’) #1 C:\xampp\htdocs\aptana\inmo\library\Zend\View\Abstract.php(294): Zend_View_Abstract->getHelper(’flashMessenger’) #2 [internal function]: Zend_View_Abstract->__call(’flashMessenger’, Array) #3 C:\xampp\htdocs\aptana\inmo\application\views\scripts\admin\properties.phtml(12): Zend_View->flashMessenger() #4 C:\xampp\htdocs\aptana\inmo\library\Zend\View.php(46): include(’C:\xampp\htdocs…’) #5 C:\xampp\htdocs\aptana\inmo\library\Zend\View\Abstract.php(769): Zend_View->_run(’.\application\v…’) #6 C:\xampp\htdocs\aptana\inmo\library\Zend\Controller\Action\Helper\ViewRenderer.php(907): Zend_View_Abstract->render(’admin/propertie…’) #7 C:\xampp\htdocs\aptana\inmo\library\Zend\Controller\Action\Helper\ViewRender in C:\xampp\htdocs\aptana\inmo\library\Zend\View\Abstract.php on line 1004

    Al parecer no encuentra el método flashMeseenger(), alguna solución?

    Saludos.

  4. zsamer on Agosto 11th, 2008 8:02 pm

    Hola Langos,

    Tienes que crear un Namespace(directorio) en la carpeta library, puedes llamarlo como quieras luego ahí mantienes la misma estructura de Zend, es decir en el caso de agregar un helper view tendrías que dentro de tu namespace crear un sub directorio View y luego Helper.

    Ejemplo: library\Zsamer\View\Helper (Zsamer es mi Namespace donde guardo las clases que heredo de Zend Framework y además para agregar los helpers action y/o helpers view, plugins etc…).

    Bueno, en la carpeta Helper guardas todos los Helper View tales como Zend_View_Helper_FlashMessenger.

    Finalmente y lo más importante tienes que decirle a Zend_View que tenga en cuenta el nuevo Namspace de Helpers:

    $this->view->addHelperPath(Core::getBaseDir() . DIRECTORY_SEPARATOR . ‘library/Zsamer/View/Helper’, ‘Zsamer_View_Helper_’);

    eso sería en todo caso en el manual de Zend Framework explica más detallado esto.

    saludos.

  5. langos on Agosto 12th, 2008 7:38 am

    Hola,

    muchas gracias, ya lo he solucionado.

    Saludos.

  6. lisandro on Septiembre 26th, 2008 3:09 pm

    Hola Andrés !

    Para que funcionara tuve que agregar Zend_Controller_Action_HelperBroker::addHelper( new Zsamer_Controller_Action_Helper_ZsamerFlashMessenger() ); en el bootstrap, creo q es correcto asi que lo dejo aca por si le sirve a alguien mas.

    Saludos

  7. zsamer on Septiembre 26th, 2008 3:31 pm

    lisandro, es muy valido tu aporte, ahora también hay otra manera, es la que uso yo: se defina en el bootstrap:

    Zend_Controller_Action_HelperBroker::addPrefix(’Zsamer_Controller_Action_Helper’);

    En el fondo le dice a Zend que debe de tomar en cuenta mi namespace donde voy a guardar mis propios Action Helper, en este caso el namespace o carpeta sería “Zsamer/Controller/Action/Helper”.

    Luego en el controlador haces esto:

    $helperFlash = $this->_helper->getHelper(’ZsamerFlashMessenger’);

    Inmediatamente chequea si existe la instancias, si no existe busca la clase en el namespace por defecto de zend y si no esta la busca en el namespace que definimos en el paso anterior y la crea, luego retorna la nueva instancia.

  8. lisandro on Septiembre 26th, 2008 3:56 pm

    Ok, entonces de esta forma no tengo registrar uno a uno todos mis (tus ;)) helpers …mucho mejor.

    Sospechaba que habia una mejor manera de hacerlo por eso la inquietud.

    Gracias otra vez, saludos !

  9. zsamer on Septiembre 26th, 2008 4:22 pm

    jeje, claro justamente eso es, se registra sólo una vez.

    saludos.

  10. francisco on Octubre 27th, 2008 3:00 pm

    Te animas a subir el codigo??

  11. zsamer on Octubre 27th, 2008 3:05 pm

    ¿subir el código? es un articulo, tienes que leerlo y ahí están las clases y ejemplos que necesitas, no necesitas que te suba ningún código.

  12. framcisco on Octubre 27th, 2008 11:31 pm

    Realmente te felicito por tus articulos, estan super interesantes. Pero tengo serios problemas con las estructuras y los nombres de las clases y sus ubicaciones, por eso te comentaba, la idea de que puedas subirlas.

    saludos. gracias

  13. Pablo Morales on Diciembre 9th, 2008 6:21 pm

    @francisco: Fijate que el nombre de las clases generalmente te dice la ruta, reemplaza los underscore (_) por / o \ y te va a dar la ruta ;)

  14. Pablo Morales on Diciembre 10th, 2008 2:46 pm

    Zsamer, muy bueno, ya lo hice andar en mi sistema, si agrego algo te paso la actualizacion

  15. Creando un blog con Zend Framework - Parte 4 | Blog personal de Pablo Morales on Diciembre 11th, 2008 4:18 am

    [...] no va a ser el definitivo, pero necesitaba una estructura para trabajar. También incorpore el plugin de Zsamer para el manejo de errores. El login ya tomo un poco de forma, y solucionamos algunos problemas con las [...]

  16. Alberto on Diciembre 22nd, 2008 11:34 am

    Hola,

    Lo primero enohrabuena por tu web y tus artículos que son de gran ayuda.

    Tengo un pequeño porblema con el flasMessenger. Cuando muestro un msg, al pinchar en un enlace e ir a otra parte de la web me mantiene el msg en pantalla (sólo en el primer clic después de mostrar un msg en los sucesivos ya no, hasta que vuelvo a mostrar otro msg que vuelve a pasar) es como si quedase cacheado el msg y no lo borrase después de mostrarlo ¿os pasa a vosotros también?

  17. zsamer on Diciembre 22nd, 2008 12:16 pm

    No hay que pinchar ningún enlace, agregas los mensajes pertinentes y luego haces un:

    $this->_redirect(’/usuarios/index/list/’);

  18. Alberto on Diciembre 22nd, 2008 2:06 pm

    No no, si el problema es que una vez mostrado el msg en pantalla, por ejemplo:

    “Registro de cuenta de usuario realizado.”

    Al pinchar en el menú login para iniciar sesión, me mantiene en pantalla el msg, es decir, una vez mostrado el msg en pantalla, vaya donde vaya después (dentro de mi aplicación claro) me vuelve a mostrar el msg sin haberlo generado de nuevo.

    No se si me explico…

  19. Alberto on Diciembre 22nd, 2008 6:04 pm

    Bueno, creo que lo he “solucionado”, el problema era que en el indexController habia mostrado los distintos tipos mensaje para ver su aspecto y por algún motivo me los dejaba cacheados de algún modo y por eso se mostraban mal.

    Un saludo.

  20. Top Posts 2008 | Zend Framework: Estado del Arte on Enero 11th, 2009 4:26 am

    [...] Extendiendo Flash Messenger Action Helper [...]

  21. Alexandra on Febrero 17th, 2009 5:07 pm

    Hola,

    Muchas gracias, justo lo que necesitaba. Buen trabajo! :)

  22. lisandro on Marzo 5th, 2009 7:03 pm

    En su momento también tuve problemas con los menajes, estos no se borraban y se mostraban el la accion siguiente, lo solucioné usando $this->_helper->redirector->goto(); en vez de $this->_redirect(), no tengo claro el motivo todavia pero lo dejo por si le sirve a alguien.

  23. Iñaki on Abril 9th, 2009 4:30 pm

    Tengo el problema de duración de los mensajes en pantalla, la cuestión es que lo estoy usando en la validación de formularios, pero no quiero perder los datos (cosa que sucedería si hago el redirect), cómo lo podría solucionar? me podéis ayudar?

  24. zsamer on Abril 10th, 2009 7:57 pm

    puedes utilizar un $this->_forward(),

  25. Claudio on Mayo 7th, 2009 12:43 pm

    Estoy siguiendo todos los tutoriales que publicas y parecen geniales. Con respecto a este quisiera saber que lineas de codigo, exactamente, van en el bootstrap, y como definir ese namespace ? Muchas gracias.

  26. Mutadjuff on Mayo 8th, 2009 1:36 am

    download mp3 music unashamed, mp3 treble charger dowload the road to sourceville mp3 switchfoot mp3 download free still lives perry blake mp3 songs sukhbir mp3 free downlaod scott bradley tom and jerry

  27. Matias on Mayo 9th, 2009 6:29 pm

    hola, tengo el bootstrap en el archivo Core.php, pero noc en que parte tengo que poner:

    Zend_Controller_Action_HelperBroker::addPrefix(’Zsamer_Controller_Action_Helper’);

    ya que lo he puesto en varios lugares pero sigue saliendo el error:

    Message: Action Helper by name ZsamerFlashMessenger not found

    espero q me puedan ayudar,, graciaass

  28. David on Mayo 21st, 2009 6:02 am

    Holas.. estuve siguiendo todo el tuto y hasta ahora me ha ido todo bien, pero en este tuto, me he perdido.. no se como probar el ejemplo..

    trate de hacer algo asi http://localhost/quickstart/public/usuarios

    pero me sale este mensaje

    http://localhost/quickstart/public/usuarios

    no se si habria la posibilidad de que publiques tus archivos de este ejemplo solamente porfa..

    gracias de antemano..

  29. David on Mayo 21st, 2009 6:03 am

    Edito. mi comentario anterior, el mensaje que me muestra es este :

    Message: Action Helper by name ZsamerFlashMessenger not found

    disculpen la confusion..

    pero podrian ayudarme!!

  30. sistema2009 on Junio 17th, 2009 3:06 pm

    si coloco esto en el boostrap

    $this->view->addHelperPath(Core::getBaseDir() . DIRECTORY_SEPARATOR . ‘library/Zsamer/View/Helper’, ‘Zsamer_View_Helper_’);

    me da el siguiente error:Fatal error: Using $this when not in object context in /var/www/sis/index.php on line 36

    Finalmente y lo más importante tienes que decirle a Zend_View que tenga en cuenta el nuevo Namspace de Helpers: como realizo esto y donde lo coloco

  31. Dan on Agosto 4th, 2009 2:28 pm

    Hola una consulta, como puedo trabajar una applicacion con acceso a 2 bases de datos distintas, postgresql y mysql.

    Thanks

  32. Ganry88 on Octubre 23rd, 2009 12:20 am

    Such individuals can be produced when two equal recessive mutants happen to be crossed with each other or when a mutant is crossed with itself; this is possible in hermaphroditic plants and even happens spontaneously. ,

  33. Nejo on Mayo 11th, 2010 1:32 pm

    Hola,

    muy buena la extensión. Lo que no acabo de entender es la existencia y la creación de una clase stdClass del método _factory.

    ¿ Me lo podría alguien explicar para qué se usa ?

    Gracias

Deja tu comentario




XHTML: puedes usar estas etiquetas: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>