Inspirado por un buen post de Asier Marqués: Desarrolo ágil con symfony, doctrine y mysql workbench en el que nos habla de como usar Mysql workbench y un plugin que nos permite exportar nuestra base de datos a formato YAML, he decidido contar mi corta experiencia con Doctrine migrations.
Hace poco comencé a usar esta opción que nos brinda a todos el ORM Doctrine. Tras dos o tres task lanzados, ya me enteraba de como funciona, es más, empecé a cogerle el gusto...
La idea parte teniendo un schema.yml inicial, una BBDD en el servidor y la necesidad de incorporar cambios en ambos. El uso es mucho más extenso, pero me voy a centrar en los siguiente:
Teniendo el schema.yml
# config/doctrine/schema.yml
Category:
columns:
id:
type: integer(11)
notnull: true
autoincrement: true
primary: true
unsigned: true
name:
type: string(255)
Y la BBDD en el servidor exactamente tal y como se declara en el schema.
Se necesita aplicar un cambio en la BBDD (añadir/eliminar columna, modificar una relación, etc.).
# config/doctrine/schema.yml
Category:
columns:
id:
type: integer(11)
notnull: true
autoincrement: true
primary: true
unsigned: true
name:
type: string(255)
Item:
columns:
id:
type: integer(11)
notnull: true
autoincrement: true
primary: true
unsigned: true
category_id:
type: integer(11)
notnull: true
unsigned: true
name:
type: string(255)
relations:
Category:
local: category_id
foreign: id
type: one
foreignAlias: Items
Cómo hacemos esto?
1. Modificar el schema.yml para aplicar el cambio.
2. Lanzar el task $ php symfony doctrine:generate-migrations-diff. Esto crea una estructura de ficheros en el directorio lib/ del proyecto:
// lib/migration/doctrine/1283537548_version1.php
class Version1 extends Doctrine_Migration_Base
{
public function up()
{
$this->createTable('item', array(
'id' =>
array(
'type' => 'integer',
'autoincrement' => '1',
'primary' => '1',
'unsigned' => '1',
'length' => '11',
),
'category_id' =>
array(
'type' => 'integer',
'notnull' => '1',
'unsigned' => '1',
'length' => '11',
),
'name' =>
array(
'type' => 'string',
'length' => '255',
),
), array(
'primary' =>
array(
0 => 'id',
),
));
}
public function down()
{
$this->dropTable('item');
}
}
// lib/migration/doctrine/1283537549_version2.phpclass Version2 extends Doctrine_Migration_Base
{
public function up()
{
$this->createForeignKey('item', 'item_category_id_category_id', array(
'name' => 'item_category_id_category_id',
'local' => 'category_id',
'foreign' => 'id',
'foreignTable' => 'category',
));
$this->addIndex('item', 'item_category_id', array(
'fields' =>
array(
0 => 'category_id',
),
));
}
public function down()
{
$this->dropForeignKey('item', 'item_category_id_category_id');
$this->removeIndex('item', 'item_category_id', array(
'fields' =>
array(
0 => 'category_id',
),
));
}
}
3. Lanzar el task $ php symfony doctrine:migrate. Aplica en la BBDD los cambios que se crearon en el paso anterior. Se le pueden pasar parámetros (entorno, up / down, etc.)
4. Por último se reconstruyen las clases del modelo: $ php symfony doctrine:build --all-classes
5. No nos olvidemos de borrar la cache..
Con esto, ya tenemos los cambios que requerimos, aplicados tanto en nuestro proyecto Symfony como en nuestra BBDD. Fácil y rápido.
Enlaces:
Proyecto de prueba creado para el post
Normalmente, cuando hago el análisis de la base de datos para un proyecto nuevo, incorporo una tabla que será la encargada de gestionar las sesiones de los usuarios de la web. Cuando el proyecto va a constar de aplicación frontend y aplicación backend intento separar sus sesiones en tablas diferentes: public_sesion_db y private_sesion_db.
La estructura de estas tablas es la siguiente:
id: varchar(40)
data: text
time: datetime
La clase que uso en los proyectos symfony para la gestión de las sesiones es sfMySQLSessionStorage. Para cerciorarnos de que las tablas de sesiones no almacenan datos de por vida, y así asegurarnos de no tener problemas con las id's (que estas no se pisen, o sean leidas de forma incorrecta), la clase incorpora un método llamado sessionGC que ejecuta la siguiente consulta SQL:
'DELETE FROM '.$db_table.' '.'WHERE '.$db_time_col.' + INTERVAL '.$lifetime.' SECOND < NOW()' donde:
¿ Cuándo se llama a este método ?
Este método es llamado mediante una probabilidad (que se calcula a partir de unos parámetros configurados en el archivo php.ini del servidor) que se aplica a cada visita a la web, es decir, por cada petición que se hace a la web se decide si se llama al recolector de basura (depende de la probabilidad).
Los parámetros encargados de calcular esa probabilidad son:
la probabilidad es session.gc_probability / session.gc_divisor. El valor del divisor ha de ser modificado en función del número de usuarios de la web, para que se llame un par de veces diarias al método sessionGC.