Cómo Programar Con Yii2: ActiveRecord
Spanish (Español) translation by Rafael Chavarría (you can also view the original English article)



Si te estás preguntando: "Qué es Yii?" Revisa mi tutorial anterior Introducción al Framework Yii, que revisa los beneficios de Yii e incluye una vista general de los cambios en Yii 2.0, liberado en Octubre de 2014.
En esta serie Programando Con Yii2, estaré guiando a los lectores en el uso del Framework Yii2 para PHP. En el tutorial de hoy, te guiaré en el uso del mapeo objeto-relacional, conocido como ORM, para trabajar con bases de datos. Es llamado ActiveRecord y es un aspecto clave de programar aplicaciones de base de datos eficientemente en Yii.
Yii ofrece diferentes maneras de trabajar con tu base de datos de manera programática, tales como consultas directas y un constructor de consulta, pero usar ActiveRecord ofrece un conjunto completo de beneficios para programación de base de datos orientada a objetos. Tu trabajo se vuelve más eficiente, más seguro, trabaja dentro de la arquitectura modelo vista controlador de Yii, y es portable si decides cambiar plataformas de base de datos (ej. MySQL a PostgreSQL).
Sigue a la par mientras detallo los básicos de Active Record dentro de Yii.
Solo un recordatorio, participo en la sección de comentarios de abajo. Estoy especialmente interesado si tienes diferentes aproximaciones, ideas adicionales, o quieres sugerir temas para tutoriales futuros. Si tienes una pregunta o sugerencia de tema, por favor publicalo abajo. También puedes contactarme en Twitter @reifman directamente.
¿Qué Es Active Record?
El modelo vista controlador de Yii es uno de sus beneficios clave. Active Record proporciona una solución orientada a objetos para trabajar con tus bases de datos que está integrada cercanamente con modelos Yii. De acuerdo a Wikipedia, el término general Active Record fue "nombrado por Martin Fowler en su libro de 2003, Patterns of Enterprise Application Architecture."
La documentación Yii resume esto concisamente:
Una clase Active Record es asociada con una tabla de base de datos, una instancia Active Record corresponde a una fila de esa tabla, y un atributo de una instancia Active Record representa el valor de una columna particular en esa fila. En vez de escribir declaraciones SQL crudas, podrías acceder atributos Active Record y llamar métodos Active Record para acceder y manipular la información almacenada en tablas de base de datos.
La integración del patrón Active Record en Yii es una gran fortaleza del framework, pero común en la mayoría de frameworks tales como Ruby on Rails.
Esta abstracción de modelos a tablas de base de datos permite al framework realizar la parte pesada de la seguridad en todos lados, ej. desglosar consultas SQL injection.
El soporte Active Record de Yii también proporciona portabilidad a lo largo de un número de bases de datos. Puedes cambiar bases de datos sin necesitar cambiar mucho código:
- MySQL 4.1 o superior
- PostgreSQL 7.3 o superior
- SQLite 2 y 3
- Microsoft SQL Server 2008 o superior
- CUBRID 9.3 o superior
- Oracle
- Sphinx: via yii\sphinx\ActiveRecord, requiere la extensión
yii2-sphinx
- ElasticSearch: via yii\elasticsearch\ActiveRecord, requiere la extensión
yii2-elasticsearch
Y las siguientes bases de datos NoSQL
- Redis 2.6.12 o superior: via yii\redis\ActiveRecord, requiere la extensión
yii2-redis - MongoDB 1.3.0 o superior: via yii\mongodb\ActiveRecord requiere la extensión
yii2-mongodb
Aprendiendo los Básicos
En el episodio anterior, Cómo Programar Con Yii2: Trabajando Con la Base de Datos y Active Record, fui a través de crear tu base de datos, cómo Yii se conecta a esta para cada sesión, usando una migración para crear tablas de base de datos, y usar Gii (el útil generador de scaffolding de Yii) para crear código modelo por defecto. Si no estás familiarizado con nada de esto, por favor revisa ese episodio.
En este episodio, me enfocaré más en aprovechar Active Record en tu código.
Declarando una Clase Active Record en un Modelo
Primero, déjame revisar como transformar un modelo Yii para sacar partido de Active Record. Usaré un modelo de ejemplo que creé en la serie Construyendo tu Startup. Esa serie te guía en cómo estoy construyendo mi startup, Planificador de Reuniones, en Yii2.
Usaré el ejemplo de un modelo simple que creé llamado Launch, que permite a los visitantes de la página principal proporcionar su dirección de correo si quieren ser notificados cuando el producto salga de la vista previa y sea liberado completamente.



Usando Active Record con un modelo es bastante simple; nota la class Launch extends \yii\db\ActiveRecord:
1 |
<?php
|
2 |
|
3 |
namespace frontend\models; |
4 |
|
5 |
use Yii; |
6 |
use yii\db\ActiveRecord; |
7 |
|
8 |
|
9 |
/**
|
10 |
* This is the model class for table "launch".
|
11 |
*
|
12 |
* @property integer $id
|
13 |
* @property string $email
|
14 |
* @property string $ip_addr
|
15 |
* @property integer $status
|
16 |
* @property integer $created_at
|
17 |
* @property integer $updated_at
|
18 |
*/
|
19 |
class Launch extends \yii\db\ActiveRecord |
20 |
{
|
21 |
const STATUS_REQUEST =0; |
22 |
/**
|
23 |
* @inheritdoc
|
24 |
*/
|
25 |
public static function tableName() |
26 |
{
|
27 |
return 'launch'; |
28 |
}
|
Eso es todo.
Construyendo Consultas
Veamos algunas consultas Active Record comunes.
Registros Individuales
Si tienes un ID de registro, frecuentemente un parámetro de consulta de un controlador, es fácil encontrar el registro que quieres:
1 |
public function actionSomething($id) |
2 |
{
|
3 |
$model = Launch::findOne($id); |
Esto es idéntico a:
1 |
$model = Launch::find() |
2 |
->where(['id' => $id]) |
3 |
->one(); |
También puedes extender el arreglo ->where con más campos o condiciones booleanas:
1 |
$model = Launch::find() |
2 |
->where(['id' => $id,'status'=>Launch::ACTIVE_REQUEST]) |
3 |
...
|
4 |
|
5 |
//equivalent to
|
6 |
$model = Launch::find() |
7 |
->where(['id' => $id) |
8 |
->andWhere(['status'=>Launch::ACTIVE_REQUEST]) |
9 |
->orWhere(['status'=>Launch::FUTURE_REQUEST]) |
10 |
...
|
11 |
Registros Múltiples
Aquí hay un ejemplo de encontrar todos los registros que empaten un status específico ordenado por $id:
1 |
$people = Launch::find() |
2 |
->where(['status' => Launch::STATUS_REQUEST]) |
3 |
->orderBy('id') |
4 |
->all(); |
El ->all(); encuentra todos los registros en lugar de solo uno. La variable $people es devuelta como un arreglo de objetos de modelo. De manera alternativa, cuando no hay condiciones, puedes acceder a todos los registros con ->findAll();
Devolviendo un Arreglo
Usar indexBy devuelve un arreglo de elementos indexados por su id:
1 |
$people = Launch::find() |
2 |
->indexBy('id') |
3 |
->all(); |
De manera alternativa, puedes devolver un arreglo asociativo con ->asArray():
1 |
$people = Launch::find() |
2 |
->asArray() |
3 |
->all(); |
Nota: La documentación de Yii dice, "Mientras este método guarda memoria y mejora desempeño, es más cercano a la capa de abstracción DB más baja y perderás la mayoría de características Active Record."
Contando Registros
También puedes devolver solo un count desde una consulta:
1 |
$count = Launch::find() |
2 |
->where(['status' => Launch::STATUS_REQUEST]) |
3 |
->count(); |
Uso contadores mucho en Planificador de Reunión para estadísticas por ejemplo; aprende más en nuestro episodio Dashboard:
1 |
// calculate $count_meetings_completed
|
2 |
$hd->count_meetings_completed = Meeting::find()->where(['status'=>Meeting::STATUS_COMPLETED])->andWhere('created_at<'.$since)->count();; |
3 |
// calculate $count_meetings_expired
|
4 |
$hd->count_meetings_expired = Meeting::find()->where(['status'=>Meeting::STATUS_EXPIRED])->andWhere('created_at<'.$since)->count();; |
5 |
// calculate $count_meetings_planning
|
6 |
$hd->count_meetings_planning = Meeting::find()->where('status<'.Meeting::STATUS_COMPLETED)->andWhere('created_at<'.$since)->count();; |
7 |
// calculate $count_places
|
8 |
$hd->count_places = Place::find()->where('created_at>'.$after)->andWhere('created_at<'.$since)->count(); |
Accediendo a los Datos
Una vez que has solicitado los datos, como un modelo individual, es sencillo acceder a los datos como un objeto modelo:
1 |
$model = Launch::findOne($id); |
2 |
$id = $model->id; |
3 |
$email = $model->email; |
Frecuentemente proceso arreglos de esta manera:
1 |
$users = User::findAll(); |
2 |
foreach ($users as $u) { |
3 |
$id = $u->id; |
4 |
$email = $u->email; |
Asignación Masiva
También puedes asignar rápidamente un arreglo a un registro modelo vía ActiveRecord:
1 |
$values = [ |
2 |
'name' => 'James', |
3 |
'email' => 'james@example.com', |
4 |
];
|
5 |
$customer = new Customer(); |
6 |
$customer->attributes = $values; |
7 |
$customer->save(); |
Esto es frecuentemente usado para poblar datos de modelo después de un envío de formulario:
1 |
if (isset($_POST['FormName'])) { |
2 |
$model->attributes = $_POST['FormName']; |
3 |
if ($model->save()) { |
4 |
// handle success
|
5 |
}
|
6 |
}
|
O puedes usar ->load() para esto:
1 |
if ($model->load(Yii::$app->request->post()) && $model->save()) { |
2 |
...
|
3 |
}
|
El generador de scaffolding Gii de Yii es grandioso generando modelos usando ActiveRecord que hace mucho de esto por ti, ej. modelos, controladores, formularios, vistas, etc.
Guardando Datos
Como puedes ver arriba, guardar datos con Active Record es sencillo también. En este ejemplo de la documentación Yii, un nuevo registro es creado y guardado---y después el registro es cargado por id, y las actualizaciones son guardadas:
1 |
// insert a new row of data
|
2 |
$customer = new Customer(); |
3 |
$customer->name = 'James'; |
4 |
$customer->email = 'james@example.com'; |
5 |
$customer->save(); |
6 |
|
7 |
// update an existing row of data
|
8 |
$customer = Customer::findOne(123); |
9 |
$customer->email = 'james@newexample.com'; |
10 |
$customer->save(); |
Borrando Registros
Borrando un registro es incluso más fácil:
1 |
$u = User::findOne(99); |
2 |
$u->delete(); |
Actualizando Contadores
Yii también ofrece incrementos de contadores. Digamos que un usuario programa otra reunión, y estoy dando seguimiento a cuantos en la tabla usuario:
1 |
$u = User::findOne(99); |
2 |
$u->updateCounters(['meeting_count'=>1]); |
3 |
// equivalent to
|
4 |
// UPDATE `User` SET `meeting_count` = `meeting_count` + 1 WHERE `id` = 99
|
Relaciones
Conectando tablas a lo largos de índices es una de las capacidades más poderosas de Active Record. Por ejemplo, en Planificador de Reuniones, cada reunión podría tener 0 o más MeetingPlaces. El modelo Meeting.php define un ActiveQuery relacional para esto:
1 |
* @property MeetingPlace[] $meetingPlaces |
2 |
/**
|
3 |
* @return \yii\db\ActiveQuery
|
4 |
*/
|
5 |
public function getMeetingPlaces() |
6 |
{
|
7 |
return $this->hasMany(MeetingPlace::className(), ['meeting_id' => 'id']); |
8 |
}
|
Después, puedo acceder a todos los lugares de reunión con la propiedad $meetingPlaces. Abajo, cargo una reunión e itero sobre todos sus meetingPlaces bastante sencillo ya que fue un arreglo de sub-objetos integrado.
1 |
$mtg=Meeting::find()->where(['id'=>$meeting_id])->one(); |
2 |
foreach ($mtg->meetingPlaces as $mp) { |
3 |
...
|
4 |
}
|
Por supuesto, esto depende en crear una llave exterior cuando creas la tabla en su migración:
1 |
$this->createTable('{{%meeting_place}}', [ |
2 |
'id' => Schema::TYPE_PK, |
3 |
'meeting_id' => Schema::TYPE_INTEGER.' NOT NULL', |
4 |
'place_id' => Schema::TYPE_INTEGER.' NOT NULL', |
5 |
'suggested_by' => Schema::TYPE_BIGINT.' NOT NULL', |
6 |
'status' => Schema::TYPE_SMALLINT . ' NOT NULL DEFAULT 0', |
7 |
'created_at' => Schema::TYPE_INTEGER . ' NOT NULL', |
8 |
'updated_at' => Schema::TYPE_INTEGER . ' NOT NULL', |
9 |
], $tableOptions); |
10 |
$this->addForeignKey('fk_meeting_place_meeting', |
11 |
'{{%meeting_place}}', 'meeting_id', '{{%meeting}}', |
12 |
'id', 'CASCADE', 'CASCADE'); |
13 |
Qué Sigue
Espero que esto te haya proporcionado una introducción sencilla de algunas de las genialidades de Active Record. También incluye Ciclos de Vida, Transacciones, y Bloqueo, sobre lo que podría escribir en el futuro. Si quieres continuar, Yii2 ofrece dos grandes áreas para aprender más en su documentaicón: Guía Yii2 para Active Record y Especificaciones funcionales Yii2 Active Record. Estas son introducciones bien escritas.
Espera futuros tutoriales en la serie Programando Con Yii2 mientras continuamos sumergiéndonos en diferentes aspectos del framework. Podrías también querer revisar la antes mencionada serie Construyendo Tu Startup Con PHP.
Si quisieras saber cuando llega el siguiente tutorial Yii2, sígueme en Twitter @reifman o revisa mi página de instructor.



