Symfony 1.0, la guía definitiva

10.2. Helpers de formularios para objetos

Cuando se utilizan los elementos de formulario para modificar las propiedades de un objeto, resulta tedioso utilizar los helpers normales. Por ejemplo, para editar el atributo telefono de un objeto Cliente, se podría escribir lo siguiente:

<?php echo input_tag('telefono', $cliente->getTelefono()) ?>
=> <input type="text" name="telefono" id="telefono" value="0123456789" />

Para no tener que repetir continuamente el nombre del atributo, Symfony define un helper de formulario para objetos en cada uno de los helpers de formularios. Los helpers de formularios para objetos deducen el nombre y el valor inicial del elemento a partir de un objeto y del nombre de un método. El anterior input_tag() es equivalente a:

<?php echo object_input_tag($cliente, 'getTelefono') ?>
=> <input type="text" name="telefono" id="telefono" value="0123456789" />

El ahorro de código no es muy significativo para el helper object_input_tag(). No obstante, todos los helpers estándar de formulario disponen del correspondiente helper para objetos y todos comparten la misma sintaxis. Utilizando estos helpers, es muy sencillo crear los formularios. Esta es la razón por la que los helpers de formulario para objetos se utilizan en el scaffolding y en los sistemas de gestión creados de forma automática (en el Capítulo 14 se definen los detalles). El listado 10-10 muestra una lista de todos los helpers de formularios para objetos.

Listado 10-10 - Sintaxis de los helpers de formularios para objetos

<?php echo object_input_tag($objeto, $metodo, $opciones) ?>
<?php echo object_input_date_tag($objeto, $metodo, $opciones) ?>
<?php echo object_input_hidden_tag($objeto, $metodo, $opciones) ?>
<?php echo object_textarea_tag($objeto, $metodo, $opciones) ?>
<?php echo object_checkbox_tag($objeto, $metodo, $opciones) ?>
<?php echo object_select_tag($objeto, $metodo, $opciones) ?>
<?php echo object_select_country_tag($objeto, $metodo, $opciones) ?>
<?php echo object_select_language_tag($objeto, $metodo, $opciones) ?>

No existe un helper llamado object_password_tag(), ya que no es recomendable proporcionar un valor por defecto en un campo de texto de contraseña basado en lo que escribió antes el usuario.

Advertencia Al contrario de lo que sucede con los helpers de formularios, los helpers de formularios para objetos solamente están disponibles si se incluye de forma explícita el grupo de helpers llamado Object en la plantilla, mediante use_helper('Object').

De todos los helpers de formularios para objetos, los más interesantes son objects_for_select() y object_select_tag(), que se emplean para construir listas desplegables.

10.2.1. Llenando listas desplegables con objetos

El helper options_for_select(), descrito anteriormente junto con el resto de helpers estándar, transforma un array asociativo de PHP en una lista de opciones, como se muestra en el listado 10-11.

Listado 10-11 - Creando una lista de opciones a partir de un array con options_for_select()

<?php echo options_for_select(array(
  '1' => 'Steve',
  '2' => 'Bob',
  '3' => 'Albert',
  '4' => 'Ian',
  '5' => 'Buck'
), 4) ?>
 => <option value="1">Steve</option>
    <option value="2">Bob</option>
    <option value="3">Albert</option>
    <option value="4" selected="selected">Ian</option>
    <option value="5">Buck</option>

Imagina que se dispone de un array de objetos de tipo Autor que ha sido obtenido mediante una consulta realizada con Propel. Si se quiere mostrar una lista desplegable cuyas opciones se obtienen de ese array, es necesario recorrer el array para obtener el valor del id y nombre de cada objeto, tal y como muestra el listado 10-12.

Listado 10-12 - Creando una lista de opciones a partir de un array de objetos con options_for_select()

// En la acción
$opciones = array();
foreach ($autores as $autor)
{
  $opciones[$autor->getId()] = $autor->getNombre();
}
$this->opciones = $opciones;

// En la plantilla
<?php echo options_for_select($opciones, 4) ?>

Como esta técnica es muy habitual, Symfony incluye un helper que automatiza todo el proceso llamado objects_for_select() y que crea una lista de opciones directamente a partir de un array de objetos. El helper requiere 2 parámetros adicionales: los nombres de los métodos empleados para obtener el value y el texto de las etiquetas <option> que se generan. De esta forma, el listado 10-12 es equivalente a la siguiente línea de código:

<?php echo objects_for_select($autores, 'getId', 'getNombre', 4) ?>

Aunque esta instrucción es muy rápida e inteligente, Symfony va más allá cuando se emplean claves externas.

10.2.2. Creando una lista desplegable a partir de una columna que es clave externa

Los valores que puede tomar una columna que es clave externa de otra son los valores de una clave primaria que corresponden a una tabla externa. Si por ejemplo se dispone de una tabla llamada articulo con una columna autor_id que es una clave externa de la tabla autor, los posibles valores de esta columna son los de la columna id de la tabla autor. Básicamente, una lista desplegable para editar el autor de un artículo debería tener el aspecto del listado 10-13.

Listado 10-13 - Creando una lista de opciones a partir de una clave externa con objects_for_select()

<?php echo select_tag('autor_id', objects_for_select(
  AutorPeer::doSelect(new Criteria()),
  'getId',
  '__toString',
  $articulo->getAutorId()
)) ?>
=> <select name="autor_id" id="autor_id">
      <option value="1">Steve</option>
      <option value="2">Bob</option>
      <option value="3">Albert</option>
      <option value="4" selected="selected">Ian</option>
      <option value="5">Buck</option>
   </select>

El helper object_select_tag() automatiza todo el proceso. En el ejemplo anterior se muestra una lista desplegable con el nombre extraído de las filas de la tabla externa. El helper puede adivinar el nombre de la tabla y de la columna externa a partir del esquema de base de datos, por lo que su sintaxis es muy concisa. El listado 10-13 es equivalente a la siguiente línea de código:

<?php echo object_select_tag($articulo, 'getAutorId') ?>

El helper object_select_tag() adivina el nombre de la clase peer relacionada (AutorPeer en este caso) a partir del nombre del método que se pasa como parámetro. No obstante, también es posible indicar una clase propia mediante la opción related_class pasada como tercer argumento. El texto que se muestra en cada etiqueta <option> es el nombre del registro de base de datos, que es el resultado de aplicar el método __toString() a la clase del objeto (si no está definido el método $autor->__toString(), se utiliza el valor de la clave primaria). Además, la lista de opciones se obtiene mediante un método doSelect() al que se pasa un objeto Criteria vacío, por lo que el método devuelve todas las filas de la tabla ordenadas por fecha de creación. Si se necesita mostrar solamente un subconjunto de filas o se quiere realizar un ordenamiento diferente, se crea un método en la clase peer que devuelve esa selección en forma de array de objetos y se indica como opción peer_method en el helper. Por último, es posible añadir una opción vacía o una opción propia como primera opción de la lista desplegable gracias a las opciones include_blank y include_custom. El listado 10-14 muestra todas estas opciones del helper object_select_tag().

Listado 10-14 - Opciones del helper object_select_tag()

// Sintaxis básica
<?php echo object_select_tag($articulo, 'getAutorId') ?>
// Construye la lista mediante AutorPeer::doSelect(new Criteria())

// Utiliza otra clase peer para obtener los valores
<?php echo object_select_tag($articulo, 'getAutorId', 'related_class=Otraclase') ?>
// Construye la lista mediante OtraclasePeer::doSelect(new Criteria())

// Utiliza otro método de la clase peer para obtener los valores
<?php echo object_select_tag($articulo, 'getAutorId','peer_method=getAutoresMasFamosos') ?>
// Construye la lista mediante AutorPeer::getAutoresMasFamosos(new Criteria())

// Añade una opción <option value="">&nbsp;</option> al principio de la lista
<?php echo object_select_tag($articulo, 'getAutorId', 'include_blank=true') ?>

// Añade una opción <option value="">Seleccione un autor</option> al principio de la lista
<?php echo object_select_tag($articulo, 'getAutorId',
  'include_custom=Seleccione un autor') ?>

10.2.3. Modificando objetos

Las acciones pueden procesar de forma sencilla los formularios que permiten modificar los datos de los objetos utilizando los helpers de formularios para objetos. El listado 10-15 muestra un ejemplo de un objeto de tipo Autor con los atributos nombre, edad y dirección.

Listado 10-15 - Un formulario construido con los helpers de formularios para objetos

<?php echo form_tag('autor/modificar') ?>
  <?php echo object_input_hidden_tag($autor, 'getId') ?>
  Nombre: <?php echo object_input_tag($autor, 'getNombre') ?><br />
  Edad:  <?php echo object_input_tag($autor, 'getEdad') ?><br />
  Dirección: <br />
         <?php echo object_textarea_tag($autor, 'getDireccion') ?>
</form>

La acción modificar del módulo autor se ejecuta cuando se envía el formulario. Esta acción puede modificar los datos del objeto utilizando el modificador fromArray() generado por Propel, tal y como muestra el listado 10-16.

Listado 10-16 - Procesando un formulario realizado con helpers de formularios para objetos

public function executeModificar ()
{
  $autor = AutorPeer::retrieveByPk($this->getRequestParameter('id'));
  $this->forward404Unless($autor);

  $autor->fromArray($this->getRequest()->getParameterHolder()->getAll(), BasePeer::TYPE_FIELDNAME);
  $autor->save();

  return $this->redirect('/autor/ver?id='.$autor->getId());
}