TigerZF
🌐Español

8.4. Renderizar decoradores individuales

En la sección anterior, vimos cómo se pueden combinar decoradores para crear una salida compleja. Observamos que, si bien este enfoque ofrece mucha flexibilidad, también añade algo de complejidad y sobrecarga. En esta sección, examinaremos cómo renderizar decoradores individualmente con el fin de crear marcado personalizado para formularios y/o elementos individuales.

Una vez que ha registrado sus decoradores, puede recuperarlos posteriormente por nombre desde el elemento. Repasemos el ejemplo anterior:

$element = new Zend_Form_Element('foo', array(
    'label'      => 'Foo',
    'belongsTo'  => 'bar',
    'value'      => 'test',
    'prefixPath' => array('decorator' => array(
        'My_Decorator' => 'path/to/decorators/',
    )),
    'decorators' => array(
        'SimpleInput'
        array('SimpleLabel', array('placement' => 'append')),
    ),
));

Si quisiéramos obtener y renderizar solo el decorador SimpleInput, podemos hacerlo utilizando el método getDecorator():

$decorator = $element->getDecorator('SimpleInput');
echo $decorator->render('');

Esto es bastante sencillo, pero se puede simplificar aún más; hagámoslo en una sola línea:

echo $element->getDecorator('SimpleInput')->render('');

No está mal, pero sigue siendo algo complejo. Para facilitar esto, en la versión 1.7 de Zend_Form se introdujo una notación abreviada: puede renderizar cualquier decorador registrado llamando a un método con el formato renderDecoratorName(). Esto realiza efectivamente lo que se ve arriba, pero hace que el argumento $content sea opcional y simplifica el uso:

echo $element->renderSimpleInput();

Este es un truco interesante, pero ¿cómo y por qué lo usaría?

Muchos desarrolladores y diseñadores tienen necesidades de marcado muy precisas para sus formularios. Prefieren tener control total sobre la salida en lugar de depender de una solución más automatizada que puede o no ajustarse a su diseño. En otros casos, la disposición del formulario puede requerir mucho marcado especializado -- agrupar elementos arbitrarios, ocultar algunos a menos que se seleccione un enlace en particular, etc.

Utilicemos la posibilidad de renderizar decoradores individuales para crear algo de marcado especializado.

Primero, definamos un formulario. Nuestro formulario capturará los datos demográficos de un usuario. El marcado estará altamente personalizado y, en algunos casos, utilizará ayudantes de vista directamente en lugar de elementos de formulario para lograr sus objetivos. Aquí está la definición básica del formulario:

class My_Form_UserDemographics extends Zend_Form
{
    public function init()
    {
        // Add a path for my own decorators
        $this->addElementPrefixPaths(array(
            'decorator' => array('My_Decorator' => 'My/Decorator'),
        ));

        $this->addElement('text', 'firstName', array(
            'label' => 'First name: ',
        ));
        $this->addElement('text', 'lastName', array(
            'label' => 'Last name: ',
        ));
        $this->addElement('text', 'title', array(
            'label' => 'Title: ',
        ));
        $this->addElement('text', 'dateOfBirth', array(
            'label' => 'Date of Birth (DD/MM/YYYY): ',
        ));
        $this->addElement('text', 'email', array(
            'label' => 'Your email address: ',
        ));
        $this->addElement('password', 'password', array(
            'label' => 'Password: ',
        ));
        $this->addElement('password', 'passwordConfirmation', array(
            'label' => 'Confirm Password: ',
        ));
    }
}
[Note] Nota

No estamos definiendo ningún validador ni filtro en este momento, ya que no son relevantes para la discusión sobre la decoración. En un escenario real, debería definirlos.

Con eso resuelto, consideremos cómo podríamos querer mostrar este formulario. Un patrón común con los nombres y apellidos es mostrarlos en una sola línea; cuando se proporciona un título, este suele estar también en la misma línea. Las fechas, cuando no se utiliza un selector de fecha de JavaScript, a menudo se separan en tres campos mostrados uno al lado del otro.

Usemos la capacidad de renderizar los decoradores de un elemento uno por uno para lograrlo. Primero, notemos que no se definieron decoradores explícitos para los elementos dados. Como recordatorio, los decoradores por defecto para (la mayoría de) los elementos son:

  • ViewHelper: utiliza un ayudante de vista para renderizar una entrada de formulario

  • Errors: utiliza el ayudante de vista FormErrors para renderizar los errores de validación

  • Description: utiliza el ayudante de vista FormNote para renderizar la descripción del elemento (si existe)

  • HtmlTag: envuelve los tres elementos anteriores en una etiqueta <dd>

  • Label: renderiza la etiqueta del elemento utilizando el ayudante de vista FormLabel (y la envuelve en una etiqueta <dt>)

Además, como recordatorio, puede acceder a cualquier elemento de un formulario como si fuera una propiedad de clase; simplemente haga referencia al elemento por el nombre que le asignó.

Nuestro script de vista podría entonces verse así:

<?php
$form = $this->form;
// Remove <dt> from label generation
foreach ($form->getElements() as $element) {
    $element->getDecorator('label')->setOption('tag', null);
}
?>
<?php echo $form->renderForm(false); ?>
    <div class="element">
        <?php echo $form->title->renderLabel()
              . $form->title->renderViewHelper() ?>
        <?php echo $form->firstName->renderLabel()
              . $form->firstName->renderViewHelper() ?>
        <?php echo $form->lastName->renderLabel()
              . $form->lastName->renderViewHelper() ?>
    </div>
    <div class="element">
        <?php echo $form->dateOfBirth->renderLabel() ?>
        <?php echo $this->formText('dateOfBirth[day]', '', array(
            'size' => 2, 'maxlength' => 2)) ?>
        /
        <?php echo $this->formText('dateOfBirth[month]', '', array(
            'size' => 2, 'maxlength' => 2)) ?>
        /
        <?php echo $this->formText('dateOfBirth[year]', '', array(
            'size' => 4, 'maxlength' => 4)) ?>
    </div>
    <div class="element">
        <?php echo $form->password->renderLabel()
              . $form->password->renderViewHelper() ?>
    </div>
    <div class="element">
        <?php echo $form->passwordConfirmation->renderLabel()
              . $form->passwordConfirmation->renderViewHelper() ?>
    </div>
    <?php echo $this->formSubmit('submit', 'Save') ?>
</form>

Si utiliza el script de vista anterior, obtendrá aproximadamente el siguiente HTML (aproximado, ya que el HTML siguiente está formateado):

<form method="post" action="">
    <div class="element">
        <label for="title" tag="" class="optional">Title:</label>
        <input type="text" name="title" id="title" value=""/>

        <label for="firstName" tag="" class="optional">First name:</label>
        <input type="text" name="firstName" id="firstName" value=""/>

        <label for="lastName" tag="" class="optional">Last name:</label>
        <input type="text" name="lastName" id="lastName" value=""/>
    </div>

    <div class="element">
        <label for="dateOfBirth" tag="" class="optional">Date of Birth
            (DD/MM/YYYY):</label>
        <input type="text" name="dateOfBirth[day]" id="dateOfBirth-day"
            value="" size="2" maxlength="2"/>
        /
        <input type="text" name="dateOfBirth[month]" id="dateOfBirth-month"
            value="" size="2" maxlength="2"/>
        /
        <input type="text" name="dateOfBirth[year]" id="dateOfBirth-year"
            value="" size="4" maxlength="4"/>
    </div>

    <div class="element">
        <label for="password" tag="" class="optional">Password:</label>
        <input type="password" name="password" id="password" value=""/>
    </div>

    <div class="element">
        <label for="passwordConfirmation" tag="" class="" id="submit"
            value="Save"/>
</form>

Puede que no quede realmente bonito, pero con algo de CSS, podría hacerse que luciera exactamente como usted desee. El punto principal, sin embargo, es que este formulario se generó utilizando casi por completo marcado personalizado, mientras que se sigue aprovechando los decoradores para el marcado más común (y para garantizar que ocurran cosas como el escapado con htmlentities y la inyección de valores).

A estas alturas del tutorial, debería sentirse bastante cómodo con las posibilidades de marcado que ofrecen los decoradores de Zend_Form. En la siguiente sección, retomaremos el elemento de fecha visto anteriormente y demostraremos cómo crear un elemento y un decorador personalizados para elementos compuestos.