Podemos relacionar unos modelos con otros, de forma similar a como se relacionan las tablas correspondientes en la base de datos. Esto permitirá acceder a información relacionada con un modelo de forma muy sencilla y eficiente, ahorrando el trabajo de escribir las sentencias SQL correspondientes.
Hay cuatro tipo de relaciones:
Para declarar relaciones hay que sobreescribir el método relations() de CActiveRecord, que ha de devolver un array de configuraciones de relaciones, en el que cada elemento es de la forma:
'nombrerelacion'=>array('TipoRelación', 'ClassName', 'ForeignKey', ...opciones adicionales)
Ejemplo:
/** * @return array relational rules. */ public function relations() { return array( 'comentarios' => array(self::HAS_MANY, 'Comentarios', 'entradas_id'), 'usuario' => array(self::BELONGS_TO, 'Usuarios', 'usuarios_id'), 'categoria' => array(self::BELONGS_TO, 'Categorias', 'categorias_id'), 'valoraciones' => array(self::MANY_MANY, 'Etiquetas', 'ent_etiq(entradas_id,etiq_id)'), ); //ent_etiq es la tabla que relaciona entradas y etiquetas (n-m) }
La manera más sencilla es acceder a una propiedad del modelo con el nombre de la relación. Si es la primera vez que se accede, se ejecutará la SQL correspondiente, que devuelve el modelo/modelos relacionados , y se guardará internamente por si se vuelve a solicitar . Esto se conoce como lazy loading, es decir, la consulta relacional es ejecutada sólo cuando los objetos relacionados son accedidos por primera vez. Ejemplo:
// recupera la entrada de id=10 $entrada=Entradas::model()->findByPk(10); // recuperar el autor del post: una consulta relacional se ejecutará aquí $usuario=$Entradas->usuario; // Ya no se vuelve a ejecutar la SQL, porque se ha cargado en la instrucción anterior echo $entrada->usuario->nombre; //Accedemos a los comentarios foreach($entrada->comentarios as $comentario) echo $comentario->text;
El acceso con lazy loading es muy cómodo de usar, pero no es eficiente en algunos escenarios. Por ejemplo, si queremos acceder a la información del autor para N entradas, con este método se ejecutarían N SQL's (una para cada entrada para cargar su autor). En este caso, debemos utilizar el método eager loading, que consiste en forzar a que se carguen inicialmente ya los datos del autor de cada entrada. Para ellom utilizaremos el método with() junto con uno de los métodos find() o findAll() del modelo. Por ejemplo,
$entradas=Entradas::model()->with('usuario')->findAll();
Al hacerlo así, se ejecuta una única consulta SQL que accede a entradas y a autores y carga todos los datos.
Podemos especificar multiples nombres de relación en el método with(). Por ejemplo, el siguiente código cargará las entradas juntos con sus autores y sus categorías:
$entradas=Entradas::model()->with('usuario','categorias')->findAll();
También podemos utilizar este tipo de acceso en los CActiveDataprovider (utilizados en las acciones admin e index). Para ello, definiremos la propiedad criteria→with :
$dataprovider=new CActiveDataprovider('Entradas',array( 'criteria'=>array( 'with'=>array('usuario','categoria') ) ));
Permiten ajustar la forma en que se cargarán los datos, aplicando filtros, ordenación o scopes
class User extends CActiveRecord { public function relations() { return array( 'entradas'=>array(self::HAS_MANY, 'Entradas', 'usuarios_id' 'order'=>'??.fecha_alta DESC', 'with'=>'categoria'), 'perfil'=>array(self::HAS_ONE, 'Perfil', 'usuarios_id'), ); } }
Ahora si accedemos a $usuario→entradas, obtendremos las entradas del usuario ordenadas de acuerdo a su hora de creación en orden descendiente. Cada instancia Entradas también tiene cargadas su categoría.
El ?? se pone cuando puede haber ambigüedad en la condición , al coincidir el nombre de la columna en otras tablas que puedan aparecer en la SQL. Yii sustituirá el ?? por el alias que corresponda.