A free and open-source book on ZF3 for beginners

Translation into this language is not yet finished. You can help this project by translating the chapters and contributing your changes.

6.9. Escribir Nuestros Ayudantes de Vista

Antes en este capítulo hemos creado una maqueta común para todas la páginas del sitio web. Pero aún tenemos un par de cosas que hacer para tener una maqueta totalmente funcional. Si recordamos, la plantilla de maqueta contiene una barra de navegación y migas de pan, sin embargo, ambos componentes de interfaz que provee Twitter Bootstrap son actualmente "estáticos" y necesitan ser más interactivos.

Por ejemplo el elemento activo de la barra de navegación debería depender de la acción del controlador que se está ejecutando en el momento. Las migas de pan deberían mostrar la ruta de la página actual. En esta sección dejaremos estos detalles completamente listos para el sitio web con la ayuda de nuestros propios ayudantes de vista.

Un ayudante de vista típico es una clase PHP derivada de la clase base Zend\View\Helper\AbstractHelper que a su vez implementa la interfaz Zend\View\Helper\HelperInterface (el diagrama de herencia se presenta en la figura 6.9).

Figura 6.9. Diagrama de Clase de un Ayudante de Vista Figura 6.9. Diagrama de Clase de un Ayudante de Vista

6.9.1. Menú

Primero, vamos a implementar la clase para el ayudante de vista Menu que imprimirá el código HTML de la barra de navegación. La clase Menu provee varios métodos que permiten establecer los elementos del menú en forma de arreglo, activar el elemento en el menú e imprimir el menú (ver un resumen de los métodos en la tabla 6.4).

Table 6.4. Métodos del ayudante de vista Menú
Método Descripción
__construct($items) Constructor de la clase.
setItems($items) Método para colocar los elementos del menú.
setActiveItemId($activeItemId) Método para marcar el elemento activo del menú.
render() Imprime el menú.
renderItem($item) Imprime un solo elemento del menú.

La información que describe un solo elemento del menú se representará con un arreglo como el que se muestra abajo (por ejemplo, el elemento Home tendrá un ID, una etiqueta y una URL para el hiperenlace):

[
  'id' => 'home',
  'label' => 'Home',
  'link' => $this->url('home')
]

Además, queremos agregar el soporte para el menú desplegable con elementos navegables. Por ejemplo, en el caso del menú desplegable Soporte que tiene los subelementos Documentación y Ayuda la descripción del elemento tomará la forma siguiente:

[
  'id' => 'support',
  'label' => 'Support',
  'dropdown' => [
    [
      'id' => 'documentation',
      'label' => 'Documentation',
      'link' => $this->url('doc', ['page'=>'contents'])
    ],
    [
      'id' => 'help',
      'label' => 'Help',
      'link' => $this->url('static', ['page'=>'help'])
    ]
  ]
]

Colocaremos la clase Menu dentro del namespace Application\View\Helper. Comenzamos creando el archivo Menu.php dentro de la carpeta View/Helper que está dentro de la carpeta fuente del módulo Application (figura 6.10).

Figura 6.10. Carpeta View helper Figura 6.10. Carpeta View helper

¿Por qué colocamos la clase del ayudante de vista dentro de la carpeta fuente del módulo?

Los ayudantes de vista (a diferencia de las plantillas de vista .phtml) se guardan dentro de la carpeta src/ del módulo porque ellas son clases PHP y necesitan ser resueltas por la clase PHP que implementa la característica de autocargado. Por otro lado, las plantillas de vista se resuelven usando una clase especial de ZF3 llamada view resolver, es por esta razón que las plantillas de vista se almacenan dentro de la carpeta view/ del módulo.

Luego creamos el esbozo de código para la clase Menu:

<?php
namespace Application\View\Helper;

use Zend\View\Helper\AbstractHelper;

// This view helper class displays a menu bar.
class Menu extends AbstractHelper
{
  // Menu items array.
  protected $items = [];

  // Active item's ID.
  protected $activeItemId = '';

  // Constructor.
  public function __construct($items=[])
  {
    $this->items = $items;
  }

  // Sets menu items.
  public function setItems($items)
  {
    $this->items = $items;
  }

  // Sets ID of the active items.
  public function setActiveItemId($activeItemId)
  {
    $this->activeItemId = $activeItemId;
  }
}

En el código de arriba definimos varios campos privados para la clase Menu. El campo $items (línea 10) es un arreglo que almacenará la información con los elementos del menú y el campo $activeItemId (línea 13) tendrá el ID del elemento activo en el menú. El elemento del menú activo se verá resaltado.

En las líneas 16-19 definimos el método constructor de la clase que opcionalmente toma un arreglo de elementos para iniciar el menú. Una manera alternativo para iniciar el menú es mediante el método setItems() (líneas 22-25). Y el método setActiveItemId() (líneas 28-31) coloca el ID del elemento activo actual.

Luego vamos a agregar el método render() que generará el código HTML para la barra de navegación y la regresará como una cadena de texto:

// Renders the menu.
public function render()
{
  if (count($this->items)==0)
    return ''; // Do nothing if there are no items.

  $result = '<nav class="navbar navbar-default" role="navigation">';
  $result .= '<div class="navbar-header">';
  $result .= '<button type="button" class="navbar-toggle" ';
  $result .= 'data-toggle="collapse" data-target=".navbar-ex1-collapse">';
  $result .= '<span class="sr-only">Toggle navigation</span>';
  $result .= '<span class="icon-bar"></span>';
  $result .= '<span class="icon-bar"></span>';
  $result .= '<span class="icon-bar"></span>';
  $result .= '</button>';
  $result .= '</div>';

  $result .= '<div class="collapse navbar-collapse navbar-ex1-collapse">';
  $result .= '<ul class="nav navbar-nav">';

  // Render items
  foreach ($this->items as $item) {
    $result .= $this->renderItem($item);
  }

  $result .= '</ul>';
  $result .= '</div>';
  $result .= '</nav>';

  return $result;
}

En el código de arriba producimos el código HTML para el componente que genera la barra de navegación de Bootstrap. La barra de navegación usará el tema por defecto y será desplegable (adaptable a los diferentes tamaños de las pantallas). La barra de navegación no tendrá el texto en la cabecera. En las líneas 22-24 recorremos los elementos del menú e imprimiremos cada uno de ellos con el método renderItem(). Finalmente, el método render() regresa el código HTML resultante como una cadena de texto.

Para terminar la creación de la clase Menu vamos a implementar el método renderItem(). Este método producirá el código HTML para un solo elemento del menú:

// Renders an item.
protected function renderItem($item)
{
  $id = isset($item['id']) ? $item['id'] : '';
  $isActive = ($id==$this->activeItemId);
  $label = isset($item['label']) ? $item['label'] : '';

  $result = '';

  if(isset($item['dropdown'])) {

    $dropdownItems = $item['dropdown'];

    $result .= '<li class="dropdown ' . ($isActive?'active':'') . '">';
    $result .= '<a href="#" class="dropdown-toggle" data-toggle="dropdown">';
    $result .= $label . ' <b class="caret"></b>';
    $result .= '</a>';

    $result .= '<ul class="dropdown-menu">';

    foreach ($dropdownItems as $item) {
      $link = isset($item['link']) ? $item['link'] : '#';
      $label = isset($item['label']) ? $item['label'] : '';

      $result .= '<li>';
      $result .= '<a href="'.$link.'">'.$label.'</a>';
      $result .= '</li>';
    }

    $result .= '</ul>';
    $result .= '</a>';
    $result .= '</li>';

  } else {
    $link = isset($item['link']) ? $item['link'] : '#';

    $result .= $isActive?'<li class="active">':'<li>';
    $result .= '<a href="'.$link.'">'.$label.'</a>';
    $result .= '</li>';
  }

  return $result;
}

En el código del método renderItem() de arriba hicimos lo siguiente: primero revisamos si el elemento es un menú desplegable o un elemento simple (línea 10). Si el elemento es un menú desplegable recorremos los elementos que constituyen el menú desplegable e imprimimos cada uno de ellos (líneas 21-28). Las líneas 35-39 contienen el código HTML para el caso de un elemento simple.

Para poder usar el ayudante de vista Menu en la plantilla de vista necesitamos registrarlo en la configuración. Para hacerlo agregamos la llave view_helpers al archivo module.config.php:

<?php
return [

    // ...

    // The following registers our custom view
    // helper classes in view plugin manager.
    'view_helpers' => [
        'factories' => [
            View\Helper\Menu::class => InvokableFactory::class,
        ],
       'aliases' => [
            'mainMenu' => View\Helper\Menu::class
       ]
    ],
];

En el código de arriba registramos nuestra clase Menu como el ayudante de vista mainMenu con lo que seremos capaces de acceder a él desde cualquier template de vista.

Como planeamos usar el ayudante de vista Menu en la plantilla de maqueta de vista reemplazamos el código para el menú de navegación que está en el archivo layout.phtml con el siguiente código:

<!-- Navigation bar -->
<?php
  $this->mainMenu()->setItems([
    [
      'id' => 'home',
      'label' => 'Home',
      'link' => $this->url('home')
    ],
    [
      'id' => 'downloads',
      'label' => 'Downloads',
      'link' => $this->url("application", ['action'=>'downloads'])
    ],
    [
      'id' => 'support',
      'label' => 'Support',
      'dropdown' => [
        [
          'id' => 'documentation',
          'label' => 'Documentation',
          'link' => $this->url('doc', ['page'=>'contents'])
        ],
        [
          'id' => 'help',
          'label' => 'Help',
          'link' => $this->url('static', ['page'=>'help'])
        ]
      ]
    ],
    [
      'id' => 'about',
      'label' => 'About',
      'link' => $this->url('about')
    ],
  ]);

  echo $this->mainMenu()->render();
?>

En el código de arriba accedemos al ayudante de vista registrado mainMenu y colocamos los elementos de la barra de navegación con la ayuda del método setItems() (línea 3). Como parámetro para el método pasamos un arreglo de elementos. Luego imprimimos la barra de navegación con el método render().

Para colocar el elemento activo en la barra de navegación llamamos al método setActiveItemId() desde cualquier plantilla de vista. Por ejemplo, agregamos el siguiente código al comienzo de la plantilla de vista para la página Acerca de (application/index/about.phtml) de la siguiente manera:

<?php
$this->mainMenu()->setActiveItemId('about');
?>

Ahora si abrimos la página Acerca de en nuestro navegador web deberíamos ver que el elemento Acerca de (About) del menú de navegación está sombreado con un color diferente. Para mostrar el elemento activo apropiadamente necesitamos llamar al método setActivoItemId() en cada página que este en la barra de navegación (Home, Descargas, Documentación, etc.) Podemos ver como se hace esto en la aplicación de ejemplo Hello World.

6.9.2. Migas de Pan

Ahora que sabemos como implementar un ayudante de vista vamos a crear un segundo ayudante para imprimir las migas de pan. Es completamente análogo al ayudante de vista Menu por esta razón solo mostramos el código completo de la clase Breadcrumbs:

<?php
namespace Application\View\Helper;

use Zend\View\Helper\AbstractHelper;

// This view helper class displays breadcrumbs.
class Breadcrumbs extends AbstractHelper
{
  // Array of items.
  private $items = [];

  // Constructor.
  public function __construct($items=[])
  {
    $this->items = $items;
  }

  // Sets the items.
  public function setItems($items)
  {
    $this->items = $items;
  }

  // Renders the breadcrumbs.
  public function render()
  {
    if(count($this->items)==0)
      return ''; // Do nothing if there are no items.

    // Resulting HTML code will be stored in this var
    $result = '<ol class="breadcrumb">';

    // Get item count
    $itemCount = count($this->items);

    $itemNum = 1; // item counter

    // Walk through items
    foreach ($this->items as $label=>$link) {

      // Make the last item inactive
      $isActive = ($itemNum==$itemCount?true:false);

      // Render current item
      $result .= $this->renderItem($label, $link, $isActive);

      // Increment item counter
      $itemNum++;
    }

    $result .= '</ol>';

    return $result;
  }

  // Renders an item.
  protected function renderItem($label, $link, $isActive)
  {
   $result = $isActive?'<li class="active">':'<li>';

    if(!$isActive)
      $result .= '<a href="'.$link.'">'.$label.'</a>';
    else
      $result .= $label;

    $result .= '</li>';

    return $result;
  }
}

Para poder usar el ayudante de vista Breadcrumbs tenemos que registrarlo en el archivo module.config.php:

<?php
return [

  //...

  // The following registers our custom view helper classes.
  'view_helpers' => [
    'factories' => [
      View\Helper\Breadcrumbs::class => InvokableFactory::class,
    ],
    'aliases' => [
        'pageBreadcrumbs' => View\Helper\Breadcrumbs::class,
    ]
  ],
];

Como planeamos usar el ayudante de vista Breadcrumbs en la plantilla de maqueta de vista reemplazamos el código HTML que está en el archivo layout.phtml por el siguiente código:

<!-- Breadcrumbs -->
<?= $this->pageBreadcrumbs()->render(); ?>

En el código de arriba accedemos al ayudante de vista pageBreadcrumbs() y lo llamamos con el método render(). Luego, el operador echo imprime el código HTML de las migas de pan.

Finalmente necesitamos pasar los elementos para las migas de pan en cada plantilla de vista. Es decir, agregamos las siguientes líneas en la plantilla de vista de la página About:

<?php
$this->pageBreadcrumbs()->setItems([
            'Home'=>$this->url('home'),
            'About'=>$this->url('about'),
            ]);
?>

Ahora si abrimos la página Acerca de deberíamos ver las migas de como como en la figura 6.11 que está más abajo. Los usuarios del sitio verán fácilmente la página que están visitando y no estarán perdidos.

Figura 6.11. Migas de Pan para la página Acerca de Figura 6.11. Migas de Pan para la página Acerca de


Top