Symfony 1.0, la guía definitiva

8.6. Extender el modelo

Los métodos del modelo que se generan automáticamente están muy bien, pero no siempre son suficientes. Si se incluye lógica de negocio propia, es necesario extender el modelo añadiendo nuevos métodos o redefiniendo algunos de los existentes.

8.6.1. Añadir nuevos métodos

Los nuevos métodos se pueden añadir en las clases vacías del modelo que se generan en el directorio lib/model/. Se emplea $this para invocar a los métodos del objeto actual y self:: para invocar a los métodos estáticos de la clase actual. No se debe olvidar que las clases personalizadas heredan los métodos de las clases Base del directorio lib/model/om/.

Por ejemplo, en el objeto Article generado en el listado 8-3, se puede añadir un método mágico de PHP llamado __toString() de forma que al mostrar un objeto de la clase Article se muestre su título, tal y como se indica en el listado 8-20.

Listado 8-20 - Personalizar el modelo, en lib/model/Article.php

class Article extends BaseArticle
{
  public function __toString()
  {
    return $this->getTitle();  // getTitle() se hereda de BaseArticle
  }
}

También se pueden extender las clases peer, como por ejemplo para obtener todos los artículos ordenados por fecha de creación, tal y como muestra el listado 8-21.

Listado 8-21 - Personalizando el modelo, en lib/model/ArticlePeer.php

class ArticlePeer extends BaseArticlePeer
{
  public static function getTodosOrdenadosPorFecha()
  {
    $c = new Criteria();
    $c->addAscendingOrderByColumn(self::CREATED_AT);
    return self::doSelect($c);
  }
}

Los nuevos métodos están disponibles de la misma forma que los métodos generados automáticamente, tal y como muestra el listado 8-22.

Listado 8-22 - El uso de métodos personalizados del modelo es idéntico al de los métodos generados automáticamente

foreach (ArticlePeer::getTodosOrdenadosPorFecha() as $articulo)
{
  echo $articulo;   // Se llama al método mágico __toString()
}

8.6.2. Redefinir métodos existentes

Si alguno de los métodos generados automáticamente en las clases Base no satisfacen las necesidades de la aplicación, se pueden redefinir en las clases personalizadas. Solamente es necesario mantener el mismo número de argumentos para cada método.

Por ejemplo, el método $articulo->getComments() devuelve un array de objetos Comment, sin ningún tipo de ordenamiento. Si se necesitan los resultados ordenados por fecha de creación siendo el primero el comentario más reciente, se puede redefinir el método getComments(), como muestra el listado 8-23. Como el método getComments() original (guardado en lib/model/om/BaseArticle.php) requiere como argumentos un objeto de tipo Criteria y una conexión, la nueva función debe contener esos mismos parámetros.

Listado 8-23 - Redefiniendo los métodos existentes en el modelo, en lib/model/Article.php

public function getComments($criteria = null, $con = null)
{
  if (is_null($criteria))
  {
    $criteria = new Criteria();
  }
  else
  {
    // Los objetos se pasan por referencia en PHP5, por lo que se debe clonar
    // el objeto original para no modificarlo
    $criteria = clone $criteria;
  }
  $criteria->addDescendingOrderByColumn(CommentPeer::CREATED_AT);

  return parent::getComments($criteria, $con);
}

El método personalizado acaba llamando a su método padre en la clase Base, lo que se considera una buena práctica. No obstante, es posible saltarse completamente la clase Base y devolver el resultado directamente.

8.6.3. Uso de comportamientos en el modelo

Algunas de las modificaciones que se realizan en el modelo son genéricas y por tanto se pueden reutilizar. Por ejemplo, los métodos que hacen que un objeto del modelo sea reordenable o un bloqueo de tipo optimistic en la base de datos para evitar conflictos cuando se guardan de forma concurrente los objetos se pueden considerar extensiones genéricas que se pueden añadir a muchas clases.

Symfony encapsula estas extensiones en "comportamientos" (del inglés behaviors). Los comportamientos son clases externas que proporcionan métodos extras a las clases del modelo. Las clases del modelo están definidas de forma que se puedan enganchar estas clases externas y Symfony extiende las clases del modelo mediante sfMixer (el Capítulo 17 contiene los detalles).

Para habilitar los comportamientos en las clases del modelo, se debe modificar una opción del archivo config/propel.ini:

propel.builder.AddBehaviors = true     // El valor por defecto es false

Symfony no incluye por defecto ningún comportamiento, pero se pueden instalar mediante plugins. Una vez que el plugin se ha instalado, se puede asignar un comportamiento a una clase mediante una sola línea de código. Si por ejemplo se ha instalado el plugin sfPropelParanoidBehaviorPlugin en la aplicación, se puede extender la clase Article con este comportamiento añadiendo la siguiente línea de código al final del archivo Article.class.php:

sfPropelBehavior::add('Article', array(
  'paranoid' => array('column' => 'deleted_at')
));

Después de volver a generar el modelo, los objetos de tipo Article que se borren permanecerán en la base de datos, aunque será invisibles a las consultas que hacen uso de los métodos del ORM, a no ser que se deshabilite temporalmente el comportamiento mediante sfPropelParanoidBehavior::disable().

La lista de plugins de Symfony disponible en el wiki incluye numerosos comportamientos http://trac.symfony-project.org/wiki/SymfonyPlugins#Behaviors. Cada comportamiento tiene su propia documentación y su propia guía de instalación.