Las novedades de Symfony 1.3 y 1.4

Integración de Doctrine

Doctrine se ha actualizado a su versión 1.2, de la que puedes obtener más información en la documentación de su sitio web: http://www.doctrine-project.org/documentation/1_2/en

Generando las clases de formulario

Los esquemas YAML de Doctrine ahora permite especificar opciones adicionales pra Symfony, entre otras la posibilidad de deshabilitar la generación automática de filtros y formularios.

Si se considera el típico modelo muchos-a-muchos, no necesitas ningún formulario ni filtro para la clase que relaciona los dos modelos, por lo que puedes indicar lo siguiente:

UserGroup:
  options:
    symfony:
      form: false
      filter: false
  columns:
    user_id:
      type: integer
      primary: true
    group_id:
      type: integer
      primary: true

Herencia de las clases de formulario

Cuando se generan los formularios de modelos que contienen herencia, las clases generadas ahora respetan esa herencia y generan formularios que siguen la misma estructura de herencia.

Nuevas tareas

Para facilitar la programación con Doctrine se han creado nuevas tareas.

Creando las tablas del modelo

Ahora se pueden crear individualmente las tablas de los modelos especificados. Las tablas se borran antes de intentar crearlas de nuevo. Su principal utilidad es cuando estás desarrollando nuevos modelos en un proyecto existente y no quieres cargarte toda la base de datos sino que es suficiente reconstruir un pequeño grupo de tablas.

$ php symfony doctrine:create-model-tables Modelo1 Modelo2 Modelo3

Borrar archivos del modelo

Resulta habitual modificar el contenido del esquema YAML renombrando modelos y columnas, creando nuevos modelos, borrando los modelos que ya no se usan, etc. Si borras un modelo existente en el esquema, te encontrarás con clases, formularios y filtros huérfanos que ya no se utilizan. Ahora es posible borrar manualmente todas las clases relacionadas con un modelo que ya no existe mediante la tarea doctrine:delete-model-files.

$ php symfony doctrine:delete-model-files NombreModelo

La tarea anterior busca todos los archivos generados automáticamente y que guardan relación con el modelo indicado. Antes de borrarlos, la tarea muestra todos los archivos encontrados y te pregunta si realmente quieres borrarlos.

Borrado automático de archivos del modelo

Si quieres automatizar el proceso anterior, puedes emplear la tarea doctrine:clean-model-files para buscar los modelos para los que existen archivos generados pero ya no están definidos en el esquema YAML.

$ php symfony doctrine:clean-model-files

El comando anterior compara tus esquemas YAML con los archivos y modelos generados para determinar lo que debe ser borrado. Los modelos marcados para borrar se pasan a la tarea doctrine:delete-model-files, que solicita la confirmación del usuario antes de borrarlos.

Recargar los datos

Eliminar toda la base de datos y volver a cargar la información de los archivos de datos es una operación habitual al desarrollar aplicaciones Symfony. La tarea doctrine:build-all-reload se encarga de ello y de muchas otras cosas como la generación de los modelos, formularios y filtros, por lo que es una tarea cuya ejecución lleva mucho tiempo en los proyectos grandes. Si sólo quieres cargar los datos, ahora puedes utilizar la tarea doctrine:reload-data.

El siguiente comando:

$ php symfony doctrine:reload-data

Es equivalente a ejecutar los siguientes comandos:

$ php symfony doctrine:drop-db
$ php symfony doctrine:build-db
$ php symfony doctrine:insert-sql
$ php symfony doctrine:data-load

Generando de todo

La nueva tarea doctrine:build permite especificar exactamente lo que quieres que generen Symfony y Doctrine. Esta tarea reemplaza a muchas otras tareas combinadas, que han sido declaradas obsoletas en detrimento de esta solución mucho más flexible.

A continuación se muestran algunos ejemplos de uso de doctrine:build:

$ php symfony doctrine:build --db --and-load

El comando anterior elimina (:drop-db) y vuelve a crear (:build-db) la base de datos, también crea las tablas configuradas en schema.yml (:insert-sql) y carga la información de los archivos de datos (:data-load).

$ php symfony doctrine:build --all-classes --and-migrate

El comando anterior construye el modelo (:build-model), los formularios (:build-forms), los filtros (:build-filters) y ejecuta cualquier migración pendiente (:migrate).

$ php symfony doctrine:build --model --and-migrate --and-append=data/fixtures/categories.yml

El comando anterior construye el modelo (:build-model), migra la base de datos (:migrate) y carga un archivo de datos llamado categories.yml (:data-load --append --dir=data/fixtures/categories.yml).

Si quieres más información sobre esta tarea, consulta la ayuda de doctrine:build.

La nueva opción –migrate

Las siguientes tareas ahora incluyen una opción --migrate que reemplaza la tarea doctrine:insert-sql por doctrine:migrate.

  • doctrine:build-all
  • doctrine:build-all-load
  • doctrine:build-all-reload
  • doctrine:build-all-reload-test-all
  • doctrine:rebuild-db
  • doctrine:reload-data

doctrine:generate-migration –editor-cmd

La tarea doctrine:generate-migration ahora incluye una opción llamada --editor-cmd que hace que se abra el editor de textos indicado justo después de generar la clase de la migración, para facilitar la edición de esa clase.

$ php symfony doctrine:generate-migration AddUserEmailColumn --editor-cmd=mate

En el ejemplo anterior, después de generar la clase de la migración se abrirá el archivo de la clase en el editor de textos TextMate.

doctrine:generate-migrations-diff

Esta nueva tara genera automáticamente clases de migraciones completas comparando tus nuevos y viejos esquemas de datos.

Crear o eliminar conexiones específicas

Cuando se ejecutan las tareas doctrine:build-db y doctrine:drop-db ahora se puede especificar el nombre de la conexión con la base de datos:

$ php symfony doctrine:drop-db master slave1 slave2

Setters y Getters de fechas

Se han añadido dos nuevos métodos para obtener las fechas de Doctrine o los timestamp como objetos de tipo DateTime de PHP.

echo $articulo->getDateTimeObject('created_at')
  ->format('m/d/Y');

También se puede establecer una fecha simplemente invocando el método setDateTimeObject y pasando una instancia válida de DateTime.

$articulo->setDateTimeObject('created_at', new DateTime('09/01/1985'));

doctrine:migrate –down

La tarea doctrine:migrate ahora incluye las opciones up y down para migrar el esquema de datos a una versión anterior o posterior a la actual.

$ php symfony doctrine:migrate --down

doctrine:migrate –dry-run

Si tu base de datos soporta el roll back de sentencias DDL (MySQL no lo soporta), puedes utilizar la nueva opción dry-run que ejecuta la tarea sin realizar ninguna modificación.

$ php symfony doctrine:migrate --dry-run

Mostrar un resultado DQL como una tabla

En las versiones anteriores, el resultado de ejecutar la tarea doctrine:dql se mostraba en formato YAML. Ahora se ha añadido la opción --table para mostrar el resultado como una tabla, similar a como se muestran los resultados en la línea de comandos de MySQL.

$ ./symfony doctrine:dql "FROM Articulo a" --table
>> doctrine  executing dql query
DQL: FROM Articulo a
+----+----------+----------------+---------------------+---------------------+
| id | autor_id | is_on_homepage | created_at          | updated_at          |
+----+----------+----------------+---------------------+---------------------+
| 1  | 1        |                | 2009-07-07 18:02:24 | 2009-07-07 18:02:24 |
| 2  | 2        |                | 2009-07-07 18:02:24 | 2009-07-07 18:02:24 |
+----+----------+----------------+---------------------+---------------------+
(2 results)

Paso de parámetros a doctrine:dql

La tarea doctrine:dql también se ha mejorado para aceptar como argumentos los parámetros de tipo “query string”:

$ php symfony doctrine:dql "FROM Articulo a WHERE nombre LIKE ?" Jose%

Depurando consultas en las pruebas funcionales

La clase sfTesterDoctrine ahora incluye un método llamado ->debug(), que muestra toda la información sobre las consultas que se han ejecutado en el contexto actual.

$browser->
  get('/articles')->
  with('doctrine')->debug()
;

Si se pasa un número entero al método, sólo se muestran las N últimas consultas ejecutadas. Si se pasa una regular expresión como cadena de texto, sólo se muestran las consultas cuyo contenido cumpla co la expresión regular indicada.

$browser->
  get('/articles')->
  with('doctrine')->debug('/from articles/i')
;

sfFormFilterDoctrine

A la clase sfFormFilterDoctrine ahora se le puede pasar un objeto de tipo Doctrine_Query mediante la opción query:

$filter = new ArticleFormFilter(array(), array(
  'query' => $table->createQuery()->select('title, body'),
));

El método de la tabla especificado mediante ->setTableMethod() (ahora mediante la opción table_method) ya no es necesario para devolver un objeto de tipo consulta. Cualquiera de los siguientes son métodos de tabla válidos para sfFormFilterDoctrine:

// funciona en Symfony >= 1.2
public function getQuery()
{
  return $this->createQuery()->select('title, body');
}
 
//funciona en Symfony >= 1.2
public function filterQuery(Doctrine_Query $query)
{
  return $query->select('title, body');
}
 
// funciona en Symfony >= 1.3
public function modifyQuery(Doctrine_Query $query)
{
  $query->select('title, body');
}

Personalizar un filtro de formulario ahora es mucho más fácil. Si quieres añadir un nuevo campo de filtrado, sólo tienes que añadir un widget y el método que lo procesa.

class UserFormFilter extends BaseUserFormFilter
{
  public function configure()
  {
    $this->widgetSchema['name'] = new sfWidgetFormInputText();
    $this->validatorSchema['name'] = new sfValidatorString(array('required' => false));
  }
 
  public function addNameColumnQuery($query, $field, $value)
  {
    if (!empty($value))
    {
      $query->andWhere(sprintf('CONCAT(%s.f_name, %1$s.l_name) LIKE ?', $query->getRootAlias()), $value);
    }
  }
}

En las versiones anteriores era necesario extender el método getFields() además de crear el widget y el método.

Configurando Doctrine

Ahora se puede configurar Doctrine mediante los eventos doctrine.configure y doctrine.configure_connection. Esto significa que incluso un plugin puede modificar fácilmente la configuración de Doctrine, siempre que el plugin se habilite antes que sfDoctrinePlugin.

doctrine:generate-module, doctrine:generate-admin y doctrine:generate-admin-for-route

Las tareas doctrine:generate-module, doctrine:generate-admin y doctrine:generate-admin-for-route ahora incluyen una opción llamada --actions-base-class que permite configurar la clase base de las acciones de los módulos generados automáticamente.

Métodos mágicos y etiquetas PHPdoc

Los métodos mágicos de tipo getter y setter que genera Symfony para los modelos de Doctrine ahora se incluyen dentro del PHPdoc de cada clase base generada. Si tu IDE soporta el autocompletado de código, ahora verás los métodos getLoQueSea() y setLoQueSea() en los objetos del modelo, donde LoQueSea es la versión CamelCase del nombre del campo.

Utilizando una versión diferente de Doctrine

Utilizar una versión diferente de Doctrine es tan sencillo como utilizar la variable de configuración sf_doctrine_dir en la clase ProjectConfiguration:

// config/ProjectConfiguration.class.php
public function setup()
{
  $this->enablePlugins('sfDoctrinePlugin');
 
  sfConfig::set('sf_doctrine_dir', '/ruta/hasta/doctrine/lib');
}