3.7.6 Asociaciones: Enlazando Modelos
Una de las características más potentes de CakePHP es la habilidad para enlazar el mapeado relacional proporcionado por el modelo. En CakePHP, los enlaces entre modelos son manejados mediante asociaciones.
Definir relaciones entre diferentes objetos en tu aplicación debería ser un proceso natural. Por ejemplo, en una base de datos de recetas, una receta puede tener varias revisiones, las revisiones tienen un único autor, y los autores pueden tener varias recetas. El definir la manera en que funcionan estas relaciones te permite acceder a tus datos de manera intuitiva y potente.
El propósito de esta sección es mostrarte cómo diseñar, definir y utilizar asociaciones entre modelos en CakePHP.
Mientras que los datos pueden provenir de una variedad de orígenes, la formá más común de almacenamiento en aplicaciones web es una base de datos relacional. La mayoría de cosas que cubre esta sección estará en ese contexto.
3.7.6.1 Tipos de Relaciones
Los cuatro tipos de relaciones en CakePHP son: hasOne, hasMany, belongsTo y hasAndBelongsToMany (HABTM), "tiene un", "tiene muchos", "pertenece a" y "tiene y pertenece a muchos", respectivamente.
| Relación | Tipo de Asociación | Ejemplo |
|---|---|---|
| uno a uno | hasOne ("tiene un") | Un usuario tiene un perfil. |
| uno a muchos | hasMany ("tiene muchos") | Los usuarios en un sistema pueden tener múltiples recetas. |
| muchos a uno | belongsTo ("pertenece a") | Una receta pertenece a un usuario. |
| muchos a muchos | hasAndBelongsToMany ("tiene y pertenece a muchos") | Las recetas tienen, y pertenecen, a muchas etiquetas. |
Las asociaciones son definidas creando una variable de clase nombrada tras la asociación que estás definiendo. La variable de clase puede, a veces, ser tan simple como una cadena de caracteres, pero puede ser tan completa como un array multidimensional usado para definir asociaciones concretas.
<?php
class Usuario extends AppModel {
var $name = 'Usuario';
var $hasOne = 'Pefil';
var $hasMany = array(
'Receta' => array(
'className' => 'Receta',
'conditions' => array('Receta.aprobada' => '1'),
'order' => 'Receta.created DESC'
)
);
}
?>
<?phpclass Usuario extends AppModel {var $name = 'Usuario';var $hasOne = 'Pefil';var $hasMany = array('Receta' => array('className' => 'Receta','conditions' => array('Receta.aprobada' => '1'),'order' => 'Receta.created DESC'));}?>
En el ejemplo de arriba, la primera instancia de la palabra 'Receta' es lo que se llama un 'Alias'. Este es un identificador para la relación y puede ser cualquier cosa que escojas. Normalmente, escogerás el mismo nombre que la clase que referencia. De todos modos, los alias han de ser únicos dentro de un modelo dado y en ambas partes de una relación belongsTo/hasMany o belongsTo/hasOne. Escoger nombres no únicos para alias puede causar comportamiento inesperados.
3.7.6.2 hasOne
Configuremos un modelo Usuario con una relación hasOne con un modelo Perfil.
Primero, necesitas establecer las claves de tus tablas de base de datos correctamente. Para que funcione una relación hasOne correctamente, una tabla ha de contener una clave foránea que apunte a un registro en la otra. En este caso, la tabla 'perfiles' contendrá un campo llamado usuario_id. El patrón básico es:
| Relación | Esquema |
|---|---|
| Manzana hasOne Plátano | plananos.manzana_id |
| Usuario hasOne Perfil | perfiles.usuario_id |
| Doctor hasOne Mentor | mentores.doctor_id |
El archivo del modelo Usuario será grabado en /app/models/usuario.php. Para definir la asociación 'Usuario hasOne Perfil', añade la propiedad $hasOne a la clase del modelo. Recuerda tener un modelo Perfil en /app/models/perfil.php, o la asociación no funcionará.
<?php
class Usuario extends AppModel {
var $name = 'Usuario';
var $hasOne = 'Perfil';
}
?>
<?phpclass Usuario extends AppModel {var $name = 'Usuario';var $hasOne = 'Perfil';}?>
Hay dos manera de describir esta relación en tus archivos del modelo. La manera más simple es establecer el atributo $hasOne a una cadena de caracteres conteniendo el nombre de la clase del modelo asociado, como hemos hecho arriba.
Si necesitas más control, puedes definir tus asociaciones utilizando sintaxis de arrays. Por ejemplo, podrías desear limitar la asociación para incluir sólo ciertos registros.
<?php
class Usuario extends AppModel {
var $name = 'Usuario';
var $hasOne = array(
'Perfil' => array(
'className' => 'Perfil',
'conditions' => array('Perfil.publicado' => '1'),
'dependent' => true
)
);
}
?>
<?phpclass Usuario extends AppModel {var $name = 'Usuario';var $hasOne = array('Perfil' => array('className' => 'Perfil','conditions' => array('Perfil.publicado' => '1'),'dependent' => true));}?>
Las claves posibles para los arrays de asociaciones hasOne incluyen:
- className: el nombre de la clase del modelo que está siendo asociado al modelo actual. si estás definiendo una relación 'Usuario hasOne Perfil', la clave
classNamedebería ser igual a 'Perfil'. - foreignKey: el nombre de la clave foránea que se encuentra en el otro modelo. Esto es especialmente útil si necesitas definir múltiples relaciones hasOne. El valor por defecto para esta clave es el nombre en singular del modelo actual, seguido del sufijo '_id'. En el ejemplo de arriba, debería ser por defecto 'usuario_id'.
- conditions: Un fragmento SQL usado para filtrar registros del modelo relacionado. Es buena práctica usar nombres de modelos en los fragmentos SQL: 'Perfil.aprobado = 1' siempre es mejor que simplemente 'aprobado = 1'.
- fields: Una lista de campos a ser devueltos cuando se traen los datos del modelo asociado. Por defecto devuelve todos los campos.
- dependent: Cuando la clave
dependentse establece atrue, y el métododelete()del modelo es llamado con el parámetro$cascadacon valortrue, los registros del modelo asociado también son borrados. En este caso lo ponemos atruede manera que borrando un Usuario también borrará su Perfil asociado.
Una vez que esta asociación ha sido definida, las operaciones de búsqueda en el modelo usuario traerán también el registro Perfil relacionado si existe:
// Resultados de ejemplo de una llamada a $this->Usuario->find()
Array
(
[Usuario] => Array
(
[id] => 121
[name] => Gwoo the Kungwoo
[created] => 2007-05-01 10:31:01
)
[Perfil] => Array
(
[id] => 12
[user_id] => 121
[habilidad] => Hornear Pasteles
[created] => 2007-05-01 10:31:01
)
)
3.7.6.3 belongsTo
Ahora que tenemos acceso a los datos de Perfil desde el modelo Usuario, definamos la asociación belongsTo (perteneceA) en el modelo Pefil para tener acceso a los datos de Usario relacionados. La asociación belongsTo es un complemento natural a las asociaciones hasOne (tieneUn) y hasMany (tieneMuchos): nos permite ver los datos de la otra dirección.
A la hora de establecer las claves de las tablas de tu base de datos para una relación belongsTo, sigue estas convenciones:
| Relación | Esquema |
|---|---|
| Platano belongsTo Manzana | platanos.manzana_id |
| Perfil belongsTo Usuario | perfiles.usuarios_id |
| Mentor belongsTo Doctor | mentores.doctores_id |
Si un modelo (tabla) contiene una clave foránea, "perteneceA" (belongsTo) el otro modelo (tabla).
Podemos definir la asociación belongsTo en nuestro modelo Perfil en /app/models/perfil.php usando la sintaxis de cadena de caracteres así:
<?php
class Perfil extends AppModel {
var $name = 'Perfil';
var $belongsTo = 'Usuario';
}
?>
<?phpclass Perfil extends AppModel {var $name = 'Perfil';var $belongsTo = 'Usuario';}?>
También podemos definir una relación más específica usando sintaxis de arrays:
<?php
class Perfil extends AppModel {
var $name = 'Perfil';
var $belongsTo = array(
'Usuario' => array(
'className' => 'Usuario',
'foreignKey' => 'usuario_id'
)
);
}
?>
<?phpclass Perfil extends AppModel {var $name = 'Perfil';var $belongsTo = array('Usuario' => array('className' => 'Usuario','foreignKey' => 'usuario_id'));}?>
Claves posibles para los arrays de la asociación belongsTo son:
- className: el nombre de la clase del modelo que se está asociando al modelo actual. Si estás definiendo una relación 'Perfil belongsTo Usuario', la clave
classNameha de tener el valor 'Usuario'. - foreignKey: el nombre de la clave foránea que se encuentra en el modelo actual. Esto es especialmente útil si necesitas definir múltiples relaciones belongsTo. El valor por defecto de esta clave es el nombre en singular del otro modelo (separado por guiones de subrayado) con el sufijo '_id'.
- conditions: el fragmento SQL filtra los registros del modelo relacionado. Es buena práctica usar el nombre de los modelos en los fragmentos SQL:
'Usuario.activo = 1'siempre es mejor que simplemente'activo = 1'. - fields: lista de campos a ser recuperados cuando los datos del modelo asociado se traen de la base de datos. Por defecto devuelve todos los campos.
- counterCache: (booleano) si se establece a
true, el modelo asociado automáticamente incrementará o decrementará el campo'[nombre_modelo_en_singular]_count'de la tabla foránea siempre que hagas unsave()odelete()(ver counterCache). El valor en el campo contador representa el número de filas relacionadas.
Una vez que esta asociación ha sido definida, las operaciones de búsqueda en el modelo Perfil también traerán el registro de Usuario relacionado si existe:
// Resultados de ejemplo de la llamada a $this->Perfil->find().
Array
(
[Perfil] => Array
(
[id] => 12
[usuario_id] => 121
[habilidad] => Baking Cakes
[created] => 2007-05-01 10:31:01
)
[Usuario] => Array
(
[id] => 121
[name] => Gwoo the Kungwoo
[created] => 2007-05-01 10:31:01
)
)
3.7.6.4 hasMany
Siguiente paso: definiendo una asociación "Usuario hasMany Comentario". Una asociación hasMany (tieneMuchos) nos permitirá traer los comentarios del usuario cuando se trae un registro Usuario.<7p>
A la hora de stablecer las claves de las tablas de tu base de datos para una relación hasMany, sigue estas convenciones:
| Relación | Esquema |
|---|---|
| Usuario hasMany Comentario | comentarios.usuario_id |
| Cake hasMany Virtud | virtudes.cake_id |
| Producto hasMany Opcion | opciones.producto_id |
Podemos definir la asociación hasMany en nuestro modelo Usuario en /app/models/usuario.php usando la sintaxis de cadena de caracteres así:
<?php
class Usuario extends AppModel {
var $name = 'Usuario';
var $hasMany = 'Comentario';
}
?>
<?phpclass Usuario extends AppModel {var $name = 'Usuario';var $hasMany = 'Comentario';}?>
También podemos definir una relación más específica usando sintaxis de arrays:
<?php
class Usuario extends AppModel {
var $name = 'Usuario';
var $hasMany = array(
'Comentario' => array(
'className' => 'Comentario',
'foreignKey' => 'usuario_id',
'conditions' => array('Comentario.estado' => '1'),
'order' => 'Comentario.created DESC',
'limit' => '5',
'dependent'=> true
)
);
}
?>
<?phpclass Usuario extends AppModel {var $name = 'Usuario';var $hasMany = array('Comentario' => array('className' => 'Comentario','foreignKey' => 'usuario_id','conditions' => array('Comentario.estado' => '1'),'order' => 'Comentario.created DESC','limit' => '5','dependent'=> true));}?>
Las claves posibles para los arrays de la asociación hasMany son:
- className: el nombre de la clase del modelo que está siendo relacionado con el modelo actual. Si estás definiendo una relación 'Usuario hasMany Comentario', el valor de
clasNameha de ser 'Comentario'. - foreignKey: el nombre de la clave foránea en el otro modelo. Esto es especialmente útil si necesitas definir múltiples relaciones hasMany. El valor por defecto para esta clave es el nombre en singular del otro modelo (separado por guiones de subrayado), con el sufijo '_id'.
- conditions: un fragmento SQL filtra los registros del modelo relacionado. Es buena práctica usar el nombre de los modelos en los fragmentos SQL:
'Usuario.activo = 1'siempre es mejor que simplemente'activo = 1'. - fields: lista de campos a ser recuperados cuando los datos del modelo asociado se traen de la base de datos. Por defecto devuelve todos los campos.
- order: un fragmento SQL que define el orden de las filas asociadas devueltas.
- limit: el número máximo de filas asociadas que quieres que devuelva.
- offset: el número de filas asociadas que quieres saltarte (dadas las condiciones y orden actuales) antes de traer las filas y asociarlas.
- dependent: Cuando
dependentse establece atrue, es posible el borrado recursivo del modelo. En este ejemplo, los registros Comentario serán borrados cuando sus registros Usuario asociados han sido borrados.El segundo parámentro del método
Modelo->delete()ha de establecerse atruepara que ocurra un borrado recursivo. - finderQuery: Una consulta SQL completa que CakePHP puede usar para traer los registros del modelo asociado. Esto debería ser usado en situaciones que requieren unos resulados muy personalizados.
Una vez que esta asociación ha sido definida, las operaciones de búsqueda en el modelo Usuario también traerán los registros Comentario relacionados si existen:
// Resultados de ejemplo de llamada a $this->Usuario->find().
Array
(
[Usuario] => Array
(
[id] => 121
[name] => Gwoo the Kungwoo
[created] => 2007-05-01 10:31:01
)
[Comentario] => Array
(
[0] => Array
(
[id] => 123
[usuario_id] => 121
[title] => On Gwoo the Kungwoo
[cuerpo] => The Kungwooness is not so Gwooish
[created] => 2006-05-01 10:31:01
)
[1] => Array
(
[id] => 123
[usuario_id] => 121
[title] => More on Gwoo
[cuerpo] => But what of the ‘Nut?
[created] => 2006-05-01 10:41:01
)
)
)
Algo a recordar es que necesitarás la asociación complementaria 'Comentario belongsTo Usuario' para obtener los datos en ambas direcciones. Lo que hemos esbozado en esta sección te permite obtener datos de Comentario desde Usuario. Añadir la asociación 'Comentario belongsTo Usuario' en el modelo comentario te permite obtener los datos de Usuario desde el modelo Comentario, completando la conexión y permitiendo el flujo de la información desde ambas perspectivas del modelo.
3.7.6.5 hasAndBelongsToMany (HABTM)
Perfecto. En este punto puedes llamarte "profesional de asociaciones del modelo de CakePHP". Ya estás versado en tres de las asociaciones que tratan la mayoría de las relaciones de objetos.
Tratemos el último tipo de relación: hasAndBelongsToMany (tieneYPerteneceAMuchos), o HABTM. Esta asociación es usada cuando tienes dos modelos que necesitas unir, repetidamente, muchas veces, de muchas maneras distintas.
La principal diferencia entre hasMany y HABTM es que un enlace entre modelos en HABTM no es exclusivo. Por ejemplo, vamos a unir nuestro modelo Receta con un modelo Etiqueta usando HABTM. Atando la etiqueta 'Italiano' a la receta 'Gnocci' de mi abuela no 'acapara' la etiqueta; también puedo etiquetar con 'Italiano' mis 'Espaguettis a la barbacoa con miel glaseada".
Los enlaces entre objetos asociados mediante hasMany son exclusivos. Si mi 'Usuario hasMany Comentarios', un comentario está sólo enlazado a un usuario específico. Deja de estar disponible para todos.
Andando. Necesitaremos establecer una tabla extra en la base de datos para manejar las asociaciones HABTM. El nombre de esta nueva tabla de unión necesita incluir los nombres de ambos modelos involucrados en plural, en orden alfabético, y separados por un guión de subrayado ( _ ). El esquema de la tabla debería contener como mínimo dos campos, cada uno clave foránea (que deberían ser enteros) apuntando a ambas claves primarias de los modelos involucrados.
HABTM necesita una tabla de unión separada que incluya los nombres de ambos modelos.
| Relación | Esquema |
|---|---|
| Receta HABTM Etiqueta | id, etiquetas_recetas.receta_id, etiquetas_recetas.etiqueta_id |
| Cake HABTM Fan | id, cakes_fans.cake_id, cakes_fans.fan_id |
| Foo HABTM Bar | id, bars_foos.foo_id, bars_foos.bar_id |
Los nombres de las tablas están, por convención, en orden alfabético.
Una vez que esta nueva tabla ha sido creada, podemos definir las asociaciones HABTM en los ficheros del modelo. Vamos a saltar directamente a la sintaxis de arrays esta vez:
<?php
class Receta extends AppModel {
var $name = 'Receta';
var $hasAndBelongsToMany = array(
'Etiqueta' =>
array('className' => 'Etiqueta',
'joinTable' => 'etiquetas_recetas',
'foreignKey' => 'receta_id',
'associationForeignKey' => 'etiqueta_id',
'with' => '',
'conditions' => '',
'order' => '',
'limit' => '',
'unique' => true,
'finderQuery' => '',
'deleteQuery' => '',
'insertQuery' => ''
)
);
}
?>
<?phpclass Receta extends AppModel {var $name = 'Receta';var $hasAndBelongsToMany = array('Etiqueta' =>array('className' => 'Etiqueta','joinTable' => 'etiquetas_recetas','foreignKey' => 'receta_id','associationForeignKey' => 'etiqueta_id','with' => '','conditions' => '','order' => '','limit' => '','unique' => true,'finderQuery' => '','deleteQuery' => '','insertQuery' => ''));}?>
Claves posibles para los arrays de asociaciones HABTM son:
- className: el nombre de la clase del modelo que se está asociando al modelo actual. Si estás definiendo una relación 'Usuario hasAndBelongsToMany Comentarios',
classNamedebería ser igual a 'Comentario'. - joinTable: el nombre de la tabla de unión usuada en esta asociación (si si la tabla actual no se adhiere a la convención de nombrado para tablas de unión HABTM).
- foreignKey: el nombre de la clave foránea que se encuentra en el modelo actual. Esto es especialmente útil si necesitas definir múltiples relaciones HABTM. El valor por defecto para esta clave es el nombre en singular, separado por guiones de subrayado (_), del modelo actual con el sufijo '_id'.
- associationForeignKey: el nombre de la clave foránea que se encuentra en el otro modelo. Esto es especialmente útil si necesitas definir múltiples relaciones HABTM. El valor por defecto para esta clave es el nombre en singulas, separado por guiones de subrayado (_), del modelo actual con el sufijo '_id'.
- with: define el nombre del modelo para la tabla de unión. Por defecto, CakePHP autocreará un modelo por ti. Usando el ejemplo de arriba, se llamaría EtiquetaReceta. Usando esta clave puedes sustituir este nombre por defecto. El modelo de la tabla de unión puede ser usado como cualquier modelo 'regular' para acceder a la tabla de unión directamente
- conditions: fragmento SQL usado para filtrar registros del modelo relacionado. Es buena práctica usar nombres de modelos en los fragmentos SQL: 'Comentario.estado = 1' siempre es preferible a simplemente 'estado = 1'.
- fields: lista de campos a ser devueltos cuando los datos del modelo asociado son traídos. Devuelve todos los campos por defecto.
- order: fragmento SQL que define el orden de las filas asociadas devueltas.
- limit: el número máximo de filas asociadas que deseas que sean devueltas.
- unique: si tiene el valor
true(valor por defecto) Cake borrará primero los registros de relación existentes en la tabla de claves foráneas antes de insertar nuevas filas, cuando se actualiza un registro. Así, las asociaciones existentes deberán ser pasadas de nuevo durante las actualizaciones. - offset: el número de filas asociadas que omitir (dadas las condiciones actuales y orden) antes de buscar y asociar.
- finderQuery, deleteQuery, insertQuery: una consulta SQL completa que CakePHP puede usar para buscar, borrar o crear nuevos registros del modelo asociado. Esto debería ser usado en situaciones que requieren resultados muy personalizados.
Una vez que esta asociación ha sido definida, las operaciones de búsqueda en el modelo Receta también devolverán los registros Etiqueta relacionados si existen:
// Resultados de ejemplo de una llamada a $this->Receta->find().
Array
(
[Receta] => Array
(
[id] => 2745
[name] => Bombas de Cholocate con Azúcar Glaseada
[created] => 2007-05-01 10:31:01
[usuario_id] => 2346
)
[Etiqueta] => Array
(
[0] => Array
(
[id] => 123
[name] => Desayuno
)
[1] => Array
(
[id] => 124
[name] => Postre
)
[2] => Array
(
[id] => 125
[name] => Enfermedad del Corazón
)
)
)
Recuerda definir una asociación HABTM en el modelo Etiqueta si quieres traer datos de Receta cuando uses el modelo Etiqueta.
También es posible ejecutar consultas de búsqueda personalizadas basadas en relaciones HABTM. Considera los ejemplos siguientes:
Asumiendo la misma estructura en el ejemplo de arriba (Receta HABTM Etiqueta), digamos que queremos obtener todas las Recetas con la etiqueta 'Postre', una manera potencial (pero errónea) de conseguirlo sería aplicar una condición a la misma asociación:
$this->Receta->bindModel(array(
'hasAndBelongsToMany' => array(
'Etiqueta' => array(
'conditions'=>array('Etiqueta.name'=>'Postre') )
)
)
);
$this->Receta->find('all');
$this->Receta->bindModel(array('hasAndBelongsToMany' => array('Etiqueta' => array('conditions'=>array('Etiqueta.name'=>'Postre') ))));$this->Receta->find('all');
// Datos devueltos
Array
(
0 => Array
{
[Receta] => Array
(
[id] => 2745
[name] => Bombas de Cholocate con Azúcar Glaseada
[created] => 2007-05-01 10:31:01
[usuario_id] => 2346
)
[Etiqueta] => Array
(
[0] => Array
(
[id] => 124
[name] => Postre
)
)
)
1 => Array
{
[Receta] => Array
(
[id] => 2745
[name] => Pasteles de Cangrejo
[created] => 2008-05-01 10:31:01
[usuario_id] => 2349
)
[Etiqueta] => Array
(
}
}
}
Notar que este ejemplo devuelve TODAS las recetas pero sólo la etiqueta 'Postre'. Para conseguir nuestro objetivo adecuadamente, hay diversas maneras de hacerlo. Una opción es buscar en el modelo Etiqueta (en vez de Receta), lo que nos dará también todas las Recetas asociadas.
$this->Receta->Tag->find('all', array('conditions'=>array('Etiqueta.name'=>'Postre')));
$this->Receta->Tag->find('all', array('conditions'=>array('Etiqueta.name'=>'Postre')));
Podríamos también usar el modelo de tabla de unión (que CakePHP nos provee), para buscar por un ID dado.
$this->Receta->bindModel(array('hasOne' => array('EtiquetaReceta')));
$this->Receta->find('all', array(
'fields' => array('Receta.*'),
'conditions'=>array('EtiquetaReceta.etiqueta_id'=>124) // id de Postre
));
$this->Receta->bindModel(array('hasOne' => array('EtiquetaReceta')));$this->Receta->find('all', array('fields' => array('Receta.*'),'conditions'=>array('EtiquetaReceta.etiqueta_id'=>124) // id de Postre));
También es posible crear una asociación exótica con el propósito de crear tantas uniones como necesarias para permitir el filtrado, por ejemplo:
$this->Receta->bindModel(
array(
'hasOne' => array(
'EtiquetaReceta',
'EtiquetaFiltro' => array(
'className' => 'Tag',
'foreignKey' => false,
'conditions' => array('EtiquetaFiltro.id = EtiquetaReceta.id')
)
)
)
);
$this->Receta->find('all', array(
'fields' => array('Receta.*'),
'conditions'=>array('EtiquetaReceta.name'=>'Postre')
));
$this->Receta->bindModel(array('hasOne' => array('EtiquetaReceta','EtiquetaFiltro' => array('className' => 'Tag','foreignKey' => false,'conditions' => array('EtiquetaFiltro.id = EtiquetaReceta.id')))));$this->Receta->find('all', array('fields' => array('Receta.*'),'conditions'=>array('EtiquetaReceta.name'=>'Postre')));
Ambos devolverán los siguientes datos:
// Datos devueltos
Array
(
0 => Array
{
[Receta] => Array
(
[id] => 2745
[name] => Bombas de Cholocate con Azúcar Glaseada
[created] => 2007-05-01 10:31:01
[usuario_id] => 2346
)
[Etiqueta] => Array
(
[0] => Array
(
[id] => 123
[name] => Desayuno
)
[1] => Array
(
[id] => 124
[name] => Postre
)
[2] => Array
(
[id] => 125
[name] => Enfermedad del corazón
)
)
}
Para más información sobre asociaciones de modelo ligadas al vuelo mira Creando y Destruyendo Asociaciones al Vuelo
Mezcla y encaja técnicas para conseguir tu objetivo específico.
3.7.6.6 Creando y Destruyendo Asociaciones al Vuelo
Algunas veces es necesario crear y destruir asociaciones del modelo al vuelo. Esto puede ser por varias razones:
- Quieres reducir la cantidad de datos asociados buscados, pero todas tus asociaciones están en el primer nivel de recursión.
- Deseas cambiar la manera en que la asociación está definida para ordenar o filtar los datos asociados.
Esta creación y destrucción de asociaciones se realiza usando los métodos del modelo de CakePHP bindModel() y unbindModel(). También hay un comportamiento muy útil llamado 'Containable', mirar la sección del manual sobre comportamientos empotrados para más información. Establezcamos unos pocos modelos para que podamos ver cómo funcionan bindModel() y unbindModel(). Empezaremos con dos modelos:
<?php
class Lider extends AppModel {
var $name = 'Lider';
var $hasMany = array(
'Seguidor' => array(
'className' => 'Seguidor',
'order' => 'Seguidor.rango'
)
);
}
?>
<?php
class Seguidor extends AppModel {
var $name = 'Seguidor';
}
?>
<?phpclass Lider extends AppModel {var $name = 'Lider';var $hasMany = array('Seguidor' => array('className' => 'Seguidor','order' => 'Seguidor.rango'));}?><?phpclass Seguidor extends AppModel {var $name = 'Seguidor';}?>
Ahora, en el LideresController podemos usar el método find() en el modelo Lider para obtener un lider y sus seguidores asociados. Como puedes ver arriba, el array de asociación en el modelo Lider define una relación 'Lider hasMany Seguidores'. Por motivos demostrativos, usemos unbindModel() para eliminar esa asociación en una acción de un controlador
function algunaAccion() {
// Esto obtiene Lideres, y sus Seguidores asociados
$this->Lider->findAll();
// Eliminemos el hasMany...
$this->Lider->unbindModel(
array('hasMany' => array('Seguidor'))
);
// Ahora usar una funcion find devolverá
// Lideres, sin Seguidores
$this->Lider->findAll();
// NOTE: unbindModel sólo afecta la siguiente función
// function. Una llamada adicional a find usará la
// información de la asociación configurada.
// Hemos uado findAll() tras unbindModel(),
// así que esto obtendrá Lideres con Seguidores asociados
// una vez más...
$this->Lider->findAll();
}
function algunaAccion() {// Esto obtiene Lideres, y sus Seguidores asociados$this->Lider->findAll();// Eliminemos el hasMany...$this->Lider->unbindModel(array('hasMany' => array('Seguidor')));// Ahora usar una funcion find devolverá// Lideres, sin Seguidores$this->Lider->findAll();// NOTE: unbindModel sólo afecta la siguiente función// function. Una llamada adicional a find usará la// información de la asociación configurada.// Hemos uado findAll() tras unbindModel(),// así que esto obtendrá Lideres con Seguidores asociados// una vez más...$this->Lider->findAll();}
Eliminar o añadir asociaciones usando bind- y unbindModel() sólo funciona para la operación del modelo next() a menos que el segundo parámetro haya sido establecido a false. Si el segundo parámetro ha sido establecido a false, la unión se mantiene para el resto de la petición.
Aquí está el patrón básico de uso para unbindModel():
$this->Modelo->unbindModel(
array('tipoAsociacion' => array('nombreDeClaseDelModeloAsociado'))
);
$this->Modelo->unbindModel(array('tipoAsociacion' => array('nombreDeClaseDelModeloAsociado')));
Ahora que hemos eliminado satisfactoriamente una asociación al vuelo, añadamos otra. Nuestro Lider 'sin todavía' principios necesita algunos Principios asociados. El fichero del modelo para nuestro modelo Principio está vacío, excepto por la declaración var $name. Asociemos algunos Principios a nuestro Lider al vuelo (pero recuerda, sólo para la siguiente operación de búsqueda). Esta función aparece en LiderController:
function otraAccion() {
// No hay 'Lider hasMany Principio' en
// el fichero de modelo lider.php, asi que una búsqueda
// aquí sólo obtiene Lideres.
$this->Lider->findAll();
// Usemod bindModel() para añadir una nueva asociación
// al modelo Lider:
$this->Lider->bindModel(
array('hasMany' => array(
'Principio' => array(
'className' => 'Principio'
)
)
)
);
// Ahora que hemos asociado correctamente,
// podemos usar una función de búsqueda para obtener
// Lideres con sus principios asociados:
$this->Lider->findAll();
}
function otraAccion() {// No hay 'Lider hasMany Principio' en// el fichero de modelo lider.php, asi que una búsqueda// aquí sólo obtiene Lideres.$this->Lider->findAll();// Usemod bindModel() para añadir una nueva asociación// al modelo Lider:$this->Lider->bindModel(array('hasMany' => array('Principio' => array('className' => 'Principio'))));// Ahora que hemos asociado correctamente,// podemos usar una función de búsqueda para obtener// Lideres con sus principios asociados:$this->Lider->findAll();}
Ahí lo tienes. El uso básico para bindModel() es la encapsulación de un array normal de asociación dentro de un array cuya clave es nombrada tras el tipo de asociación que estás tratando de crear:
$this->Modelo->bindModel(
array('nombreAsociacion' => array(
'nombreDeClaseDelModeloAsociado' => array(
// claves de asociacion normales van aquí...
)
)
)
);
$this->Modelo->bindModel(array('nombreAsociacion' => array('nombreDeClaseDelModeloAsociado' => array(// claves de asociacion normales van aquí...))));
A pesar de que el nuevo modelo unido no necesita ningún tipo de asociación en la definición de su fichero de modelo, todavía necesitará tener la clave correcta para que la nueva asociación funcione correctamente.
