En esta sección se explica cómo crear un nuevo comportamiento haciendo uso de Doctrine 1.2. El ejemplo utilizado permitirá mantener una cache del número de relaciones de un registro para no tener que hacer esa consulta todo el rato.
La funcionalidad es realmente simple: en todas las relaciones en las que quieras controlar su número, el comportamiento añade una columna a su modelo para almacenar un contador.
8.1.1. El esquema
Inicialmente se va a utilizar el siguiente esquema. Más adelante se modifica
para añadir la definición actAs
del comportamiento que se va a crear:
# config/doctrine/schema.yml
Thread:
columns:
title:
type: string(255)
notnull: true
Post:
columns:
thread_id:
type: integer
notnull: true
body:
type: clob
notnull: true
relations:
Thread:
onDelete: CASCADE
foreignAlias: Posts
Seguidamente se construyen todas las clases del esquema:
$ php symfony doctrine:build --all
8.1.2. La plantilla
En primer lugar se crea la clase básica de tipo Doctrine_Template
que será
la responsable de añadir las columnas al modelo que guardará los contadores.
Añade la siguiente clase dentro de cualquier directorio lib/
del proyecto
para que symfony pueda cargarla de forma automática:
// lib/count_cache/CountCache.class.php
class CountCache extends Doctrine_Template
{
public function setTableDefinition()
{
}
public function setUp()
{
}
}
A continuación se modifica el modelo Post
para añadir el comportamiento
CountCache
mediante actAs
:
# config/doctrine/schema.yml
Post:
actAs:
CountCache: ~
# ...
Ahora que el modelo Post
hace uso del comportamiento CountCache
, su
funcionamiento es el siguiente: cuando se instancia la información de mapeo de
un modelo, se invocan los métodos setTableDefinition()
y setUp()
de todos
sus comportamientos asociados. Esto es lo mismo que sucede con la clase
BasePost
en lib/model/doctrine/base/BasePost.class.php
. Esta característica
permite añadir elementos de todo tipo a un modelo, como columnas, relaciones,
eventos, etc.
Ahora que está más claro su funcionamiento interno, se añade toda la lógica
interna del comportamiento CountCache
:
class CountCache extends Doctrine_Template
{
protected $_options = array(
'relations' => array()
);
public function setTableDefinition()
{
foreach ($this->_options['relations'] as $relation => $options)
{
// si no se dispone del nombre de la columna, se crea
if (!isset($options['columnName']))
{
$this->_options['relations'][$relation]['columnName'] = 'num_'.Doctrine_Inflector::tableize($relation);
}
// añadir la columna al modelo relacionado
$columnName = $this->_options['relations'][$relation]['columnName'];
$relatedTable = $this->_table->getRelation($relation)->getTable();
$this->_options['relations'][$relation]['className'] = $relatedTable->getOption('name');
$relatedTable->setColumn($columnName, 'integer', null, array('default' => 0));
}
}
}
El código superior añade columnas para mantener los contadores de los modelos
relacionados. Por tanto, en este caso se añade el comportamiento en el modelo
Post
para su relación Thread
. De esta forma, el número de posts de cualquier
Thread
se almacena en una columna llamada num_posts
. A continuación,
modifica el esquema YAML para definir las opciones adicionales del comportamiento:
code.34720752b17772471994a609c6d180fdee6cac0ephp class CountCache extends Doctrine_Template { // ...
public function setTableDefinition() { // ...
$this->addListener(new CountCacheListener($this->_options));
} } code.65708ada59fe69c44c4d5970022173ac2ab752c3php // lib/model/count_cache/CountCacheListener.class.php
class CountCacheListener extends Doctrine_Record_Listener { protected $_options;
public function __construct(array $options) { $this->_options = $options; } } code.3d188004b308550888dad35cc71f43fd229fec21php class CountCacheListener extends Doctrine_Record_Listener { // ...
public function postInsert(Doctrine_Event $event) { $invoker = $event->getInvoker(); foreach ($this->_options['relations'] as $relation => $options) { $table = Doctrine::getTable($options['className']); $relation = $table->getRelation($options['foreignAlias']);
$table ->createQuery() ->update() ->set($options['columnName'], $options['columnName'].' + 1') ->where($relation['local'].' = ?', $invoker->$relation['foreign']) ->execute(); }
} } code.3ca928d5842179c661f78197b2ea3756f236c7fbphp $post = new Post(); $post->thread_id = 1; $post->body = 'contenido del post'; $post->save(); code.8780233e6f2f4bd59b24324b15564f7baa688109php class CountCacheListener extends Doctrine_Record_Listener { // ...
public function postDelete(Doctrine_Event $event) { $invoker = $event->getInvoker(); foreach ($this->_options['relations'] as $relation => $options) { $table = Doctrine::getTable($options['className']); $relation = $table->getRelation($options['foreignAlias']);
$table ->createQuery() ->update() ->set($options['columnName'], $options['columnName'].' - 1') ->where($relation['local'].' = ?', $invoker->$relation['foreign']) ->execute(); }
} } code.9362e9b88a78ce6bafd4423ff1ad7bc10a38f97aphp $post->delete(); code.7cfb988a77916fd5f3af6d545fb066c95d5ec9baphp class CountCacheListener extends Doctrine_Record_Listener { // ...
public function preDqlDelete(Doctrine_Event $event) { foreach ($this->_options['relations'] as $relation => $options) { $table = Doctrine::getTable($options['className']); $relation = $table->getRelation($options['foreignAlias']);
$q = clone $event->getQuery(); $q->select($relation['foreign']); $ids = $q->execute(array(), Doctrine::HYDRATE_NONE);
foreach ($ids as $id) { $id = $id[0];
$table ->createQuery() ->update() ->set($options['columnName'], $options['columnName'].' - 1') ->where($relation['local'].' = ?', $id) ->execute(); } }
} } code.a59c4b0fb327a7fd9c298f470c62ee63e412bafephp Doctrine::getTable('Post') ->createQuery() ->delete() ->where('id = ?', 1) ->execute(); code.760e6bb96862904c7fc6468079f5b0a6c1bf36ffphp Doctrine::getTable('Post') ->createQuery() ->delete() ->where('body LIKE ?', '%cool%') ->execute(); code.a06d067f5308d876f4c3d158d15d89105604ab81php $manager->setAttribute(Doctrine_Core::ATTR_USE_DQL_CALLBACKS, true); code.b076a6222ed1da33edf284412214e878219d9a31yaml