Los formularios de Symfony 1.4

11.3. El generador CRUD

Una vez generadas las clases de los formularios, a continuación se crea un módulo de Symfony que permita trabajar con los objetos a través de un navegador. El objetivo es crear, modificar y borrar objetos de las clases Article, Author, Category y Tag.

En primer lugar se crea el módulo correspondiente a la clase Author. Aunque este módulo se puede crear manualmente, el plugin de Doctrine incluye la tarea doctrine:generate-crud para crear un módulo de tipo CRUD basado en la clase de un objeto del modelo. Si se emplea el mismo formulario que en la sección anterior:

$ ./symfony doctrine:generate-crud frontend author Author

La tarea doctrine:generate-crud requiere tres argumentos:

  • frontend: nombre de la aplicación en la que se crea el módulo
  • author: nombre del módulo creado
  • Author: nombre de la clase del modelo para la que se crea el módulo

Nota CRUD es el acrónimo de las palabras inglesas "Creation / Retrieval / Update / Deletion" (Crear, Obtener, Actualizar y Borrar) que resumen las cuatro operaciones básicas que se realizan con los datos del modelo.

El listado 11-4 muestra que la tarea genera cinco acciones que permiten listar (index), crear (create), modificar (edit), guardar (update) y borrar (delete) los objetos de la clase Author.

Listado 11-4 - La clase authorActions generada por la tarea

// apps/frontend/modules/author/actions/actions.class.php
class authorActions extends sfActions
{
  public function executeIndex()
  {
    $this->authorList = $this->getAuthorTable()->findAll();
  }

  public function executeCreate()
  {
    $this->form = new AuthorForm();

    $this->setTemplate('edit');
  }

  public function executeEdit($request)
  {
    $this->form = $this->getAuthorForm($request->getParameter('id'));
  }

  public function executeUpdate($request)
  {
    $this->forward404Unless($request->isMethod('post'));

    $this->form = $this->getAuthorForm($request->getParameter('id'));

    $this->form->bind($request->getParameter('author'));
    if ($this->form->isValid())
    {
      $author = $this->form->save();

      $this->redirect('author/edit?id='.$author->get('id'));
    }

    $this->setTemplate('edit');
  }

  public function executeDelete($request)
  {
    $this->forward404Unless($author = $this->getAuthorById($request->getParameter('id')));

    $author->delete();

    $this->redirect('author/index');
  }

  private function getAuthorTable()
  {
    return Doctrine::getTable('Author');
  }

  private function getAuthorById($id)
  {
    return $this->getAuthorTable()->find($id);
  }

  private function getAuthorForm($id)
  {
    $author = $this->getAuthorById($id);

    if ($author instanceof Author)
    {
      return new ArticleForm($author);
    }
    else
    {
      return new ArticleForm();
    }
  }
}

El flujo de trabajo del formulario de este módulo se controla mediante los métodos create, edit y update. Por tanto, la tarea doctrine:generate-crud también puede generar un único método que se encargue de estas funcionalidades mediante la opción --non-atomic-actions:

$ ./symfony doctrine:generate-crud frontend author Author --non-atomic-actions

El código generado con la opción --non-atomic-actions (ver listado 11-5) es mucho más conciso.

Listado 11-5 - La clase authorActions generada con la opción --non-atomic-actions

class authorActions extends sfActions
{
  public function executeIndex()
  {
    $this->authorList = $this->getAuthorTable()->findAll();
  }

  public function executeEdit($request)
  {
    $this->form = new AuthorForm(Doctrine::getTable('Author')->find($request->getParameter('id')));

    if ($request->isMethod('post'))
    {
      $this->form->bind($request->getParameter('author'));
      if ($this->form->isValid())
      {
        $author = $this->form->save();

        $this->redirect('author/edit?id='.$author->getId());
      }
    }
  }

  public function executeDelete($request)
  {
    $this->forward404Unless($author = Doctrine::getTable('Author')->find($request->getParameter('id')));

    $author->delete();

    $this->redirect('author/index');
  }
}

Esta tarea también genera dos plantillas, indexSuccess y editSuccess. La plantilla editSuccess generada no utiliza la instrucción <?php echo $form ?>. Se puede modificar este comportamiento con la opción --non-verbose-templates:

$ ./symfony doctrine:generate-crud frontend author Author --non-verbose-templates

Esta última opción es muy útil cuando se están creando los prototipos, tal y como muestra el listado 11-6.

Listado 11-6 - La plantilla editSuccess

// apps/frontend/modules/author/templates/editSuccess.php
<?php $author = $form->getObject() ?>
<h1><?php echo $author->isNew() ? 'New' : 'Edit' ?> Author</h1>

<form action="<?php echo url_for('author/edit'.(!$author->isNew() ? '?id='.$author->getId() : '')) ?>" method="post" <?php $form->isMultipart() and print 'enctype="multipart/form-data" ' ?>>
  <table>
    <tfoot>
      <tr>
        <td colspan="2">
          &nbsp;<a href="<?php echo url_for('author/index') ?>">Cancel</a>
          <?php if (!$author->isNew()): ?>
            &nbsp;<?php echo link_to('Delete', 'author/delete?id='.$author->getId(), array('post' => true, 'confirm' => 'Are you sure?')) ?>
          <?php endif; ?>
          <input type="submit" value="Save" />
        </td>
      </tr>
    </tfoot>
    <tbody>
      <?php echo $form ?>
    </tbody>
  </table>
</form>

Nota La opción --with-show permite generar una acción y una plantilla específicas para visualizar los datos de un objeto. Esta plantilla solamente permite visualizar los datos, no modificarlos.

Si accedes ahora a la dirección /frontend_dev.php/author con un navegador, puedes visualizar el módulo generado automáticamente (ver figuras 11-1 y 11-2). Prueba a listar los autores, crear nuevos autores, modificar sus datos e incluso borrarlos gracias a la interfaz generada. Como puedes comprobar, las reglas de validación también se tienen en cuenta al crear o modificar los datos.

Listado de autores

Figura 11.1 Listado de autores

Errores de validación al modificar los datos de un autor

Figura 11.2 Errores de validación al modificar los datos de un autor

Si ahora se repite la operación con la clase Article:

$ ./symfony doctrine:generate-crud frontend article Article --non-verbose-templates --non-atomic-actions

El código generado es muy parecido al código de la clase Author. No obstante, si intentas crear un nuevo artículo, la aplicación produce el error fatal que se muestra en la figura 11-3.

Las tablas relacionadas deben definir el método __toString()

Figura 11.3 Las tablas relacionadas deben definir el método __toString()

El formulario ArticleForm utiliza el widget sfWidgetFormDoctrineSelect para representar la relación entre los objetos Article y Author. Este widget crea una lista desplegable con todos los autores disponibles. Cuando la aplicación trata de mostrar los autores, los objetos de tipo Author se convierten en una cadena de texto gracias al método mágico __toString(). Por lo tanto, es obligatorio definir el método __toString() en la clase Author tal y como muestra el listado 11-7.

Listado 11-7 - Definiendo el método __toString() en la clase Author

class Author extends BaseAuthor
{
  public function __toString()
  {
    return $this->getFirstName().' '.$this->getLastName();
  }
}

De la misma forma que en la clase Author, se pueden crear métodos __toString() en las otras clases del modelo: Article, Category y Tag.

Nota sfDoctrineRecord intenta adivinar cuál es el método __toString() adecuado si no lo encuentra. Para ello comprueba si existen columnas llamadas title, name, subject, etc. Si existe alguna de esas columnas, la utiliza como representación del objeto en forma de cadena de texto.

Nota La opción method del widget sfWidgetFormDoctrineSelect establece el método utilizado para obtener la representación del objeto en forma de cadena de texto.

La figura 11-4 muestra cómo crear un artículo después de haber incluído el método __toString().

Creando un artículo

Figura 11.4 Creando un artículo