viernes, 24 de abril de 2015

Acceso a datos

Como se había mencionado anteriormente, los datos traídos de la base de datos se alamcenan en instancias Active Record, y cada fila del resultado de la consulta corresponde a una sola instancia Active Record. Se puede acceder a los valores de columna mediante el acceso a los atributos de las instancias Active Record, por ejemplo,

// "id" y "correo" son los nombres de columna de la tabla "cliente"
$cliente Cliente::findOne(123);$id $cliente->id;$correo $cliente->correo;

Es importante tomar en cuenta que no deberíamos re-declarar ninguno de los atributos de Active Record ya que los define Yii de manera automática.

Transformación de datos

Sucede a menudo que los datos que se ingresan y/o se muestran están en un formato diferente del utilizado en el almacenamiento de los datos en una base de datos. Por ejemplo, supongamos que la fecha de nacimiento de los clientes está almacenados como marcas de tiempo UNIX (aunque no es un buen diseño), mientras que en la mayoría de los casos lo ideal sería manipular las fechas en un formato más legible como 'yyyy/mm/dd' . Para lograr este objetivo, se pueden definir métodos de transformación de datos en la clase Active Record del cliente como se muestra a continuación:

class Cliente extends ActiveRecord{
    // ...

    public function getBirthdayText()
    {
        return date('Y/m/d'$this->birthday);
    }
    
    public function setBirthdayText($value)
    {
        $this->birthday strtotime($value);
    }
}

Ahora, en lugar de acceder a $customer->birthday (que nos mostraría una marca de tiempo), accedemos a $customer->birthdayText (que nos presentará la fecha en formato yyyy/mm/dd)

Recuperación de datos en matrices

Mientras que la recuperación de datos en términos de objetos Active Record es conveniente y flexible, no siempre es deseable cuando se tiene que traer una gran cantidad de datos debido al uso grande de memoria. En este caso, podemos recuperar datos utilizando arreglos PHP llamando asArray() antes de ejecutar un método de consulta:

// retorna todos los clientes
// cada cliente es retornado como un arreglo asociativo
$clientes Cliente::find()
    ->asArray()
    ->all();

Nota: Si bien este método ahorra memoria y mejora el rendimiento, está más cerca de la capa de abstracción de la base de datos y por ende se perderá la mayor parte de las características de Active Record. Una distinción muy importante radica en el tipo de datos de los valores de columna. Cuando retornan los datos en los casos de Active Record, los valores de columna serán asignados automáticamente en función de los tipos de columna reales; por otra parte, cuando retornan datos en arreglos, los valores de columna serán cadenas (ya que son el resultado de PDO sin ningún procesamiento), independientemente de sus tipos de columna reales.

Recuperación de datos en lotes

En la entrada Generador de Consultas (Query Builder), hemos visto que podemos utilizar la consulta por lotes para minimizar el uso de memoria al consultar una gran cantidad de datos de la base de datos. Podemos utilizar la misma técnica en Active Record. Por ejemplo,

// extrae 10 cliente de una sola vez
foreach (Cliente::find()->batch(10) as $clientes) {
    // $clientes es un arreglo de 10 o menos objetos Cliente
}

// extrae 10 clientes a la vez e itera uno por uno
foreach (Cliente::find()->each(10) as $cliente) {
    // $cliente es un objeto Cliente
}

// consulta en lotes con carga lenta
foreach (Cliente::find()->with('ordenes')->each() as $cliente) {
    // $cliente es un objeto Cliente
}



También te puede interesar:
Consulta de Datos
Almacenamiento de Datos
Active Record (Registro Activo)

miércoles, 22 de abril de 2015

Consulta de Datos

Después de haber declarado una clase Active Record,

namespace app\models;

use yii\db\ActiveRecord;

class Cliente extends ActiveRecord{
    const ESTADO_INACTIVO 0;
    const ESTADO_ACTIVO 1;
    
    /**
     * @Retorna una cadena con el nombre de la tabla asociada a ésta clase ActiveRecord.
     */
    public static function tableName()
    {
        return 'cliente';
    }
}

podemos utilizarla para consultar datos de la tabla de base de datos correspondiente. El proceso se lo realiza normalmente en los tres pasos siguientes:

  1. Crear un nuevo objeto de consulta llamando al método yii\db\ActiveRecord::find();
  2. Construir el objeto de consulta llamando a cualquiera de los métodos de construcción de consultas;
  3. Llamar a un método de consulta para recuperar los datos en términos de instancias de Active Record.

A continuación algunos ejemplos:

// retorna un único cliente cuyo ID es 123
// SELECT * FROM `cliente` WHERE `id` = 123
$cliente Cliente::find()
    ->where(['id' => 123])
    ->one();

// retorna todos los cliente activos ordenados por su ID
// SELECT * FROM `cliente` WHERE `estado` = 1 ORDER BY `id`
$cliente Cliente::find()
    ->where(['estado' => Cliente::ESTADO_ACTIVO])
    ->orderBy('id')
    ->all();

// retorna el número de clientes activos
// SELECT COUNT(*) FROM `cliente` WHERE `estado` = 1
$conteo Cliente::find()
    ->where(['estado' => Cliente::ESTADO_ACTIVO])
    ->count();

// retorna todos los clientes en un arreglo de clientes
// SELECT * FROM `customer`
$clientes Cliente::find()
    ->indexBy('id')
    ->all();

En la parte anterior, $cliente es un objeto Cliente, mientras que $clientes (en plural) es un conjunto de objetos Clientes. Todos ellos se rellenan con los datos recuperados de la tabla cliente.

Puesto que yii\db\ActiveQuery se extiende de yii\db\Query, podemos utilizar todos los métodos de construcción de consultas y métodos de consulta descritos en la sección Generador de Consultas (Query Builder).

Es una tarea común consultar por los valores de la clave primaria o un conjunto de valores de columna, por lo que Yii ofrece dos métodos de acceso directo para este propósito:

  • yii\db\ActiveRecord::findOne(): devuelve una sola instancia Active Record con la primera fila del resultado de la consulta.
  • yii\db\ActiveRecord::findAll(): devuelve un conjunto de instancias de Active Record con todos los resultados de la consulta.


Ambos métodos pueden recibir en sus parámetros los siguientes formatos:

  • un valor escalar: el valor es tratado como el valor deseado de la clave primaria a ser buscado. Yii determinará automáticamente qué columna es la columna de clave primaria mediante la lectura de la información del esquema de base de datos.
  • un arreglo de valores escalares: el arreglo se trata como los valores de la clave primaria deseados que se buscarán.
  • un arreglo asociativo: las claves son los nombres de columna y los valores son los correspondientes valores de las columnas deseadas a ser buscados.

Por ejemplo:

// retorna un único cliente cuyo ID es 123
// SELECT * FROM `cliente` WHERE `id` = 123
$clienteCliente::findOne(123);

// retorna cliente cuyos ID son 100, 101, 123 o 124
// SELECT * FROM `cliente` WHERE `id` IN (100, 101, 123, 124)
$clientes Cliente::findAll([100101123124]);

// retorna un cliente activo cuyo ID is 123
// SELECT * FROM `cliente` WHERE `id` = 123 AND `estado` = 1
$cliente Cliente::findOne([
    'id' => 123,
    'estado' => Cliente::ESTADO_ACTIVO,
]);

// retorna todos los clientes inactivos
// SELECT * FROM `cliente` WHERE `estado` = 0
$customer Cliente::findAll([
    'estado' => Cliente::ESTADO_INACTIVO,
]);

Se debe tomar en cuenta que ni yii\db\ActiveRecord::findOne(), ni yii\db\ActiveQuery::one() añadirán LIMIT 1 a la sentencia generada. Si sabemos que la consulta va a retornar más de un registro, debemos añadirlo de manera explícita para mejorar el rendimiento, por ejemplo

Cliente::find()->limit(1)->one()

En lugar de utilizar los métodos de consulta, podemos sobrescribir la sentencia sql y obtener los datos en un Active Record. Para ello debemos llamar al método yii\db\ActiveRecord::findBySql() de manera similar a:

// retorna todos los clientes inactivos
$sql 'SELECT * FROM cliente WHERE estado=:estado';
$clientes Cliente::findBySql($sql, [':estado' => Cliente::ESTADO_INACTIVO])->all();

No debemos llamar a métodos adicionales luego de llamar a findBySql() pues serán ignorados.


También te puede interesar:
Acceso a Datos
Almacenamiento de Datos
Active Record (Registro Activo)


Active Record (Registro Activo)

Active Record proporciona una interfaz orientada a objetos para acceder y manipular los datos almacenados en bases de datos. Una clase Active Record está 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 de Active Record representa el valor de una columna en particular en la fila. En lugar de escribir sentencias SQL, podemos acceder a los atributos de Active Record y llamar a métodos Active Record para acceder y manipular los datos almacenados en las tablas de base de datos.

Por ejemplo, supongamos que Cliente es una clase Active Record que se asocia con la tabla de clientes y nombre es una columna de la tabla de clientes. Para insertar una nueva fila en la tabla Cliente escrbimos algo similar a:

$cliente = new Cliente();
$cliente->nombre 'Juan';
$cliente->save();


El código anterior es equivalente a usar la siguiente declaración SQL para MySQL, que es menos intuitiva, más propensa a errores, y podemos tener incluso problemas de compatibilidad si utilizamos otro tipo de base de datos:

$db->createCommand('INSERT INTO `cliente` (`nombre`) VALUES (:nombre)', [
    ':nombre' => 'Juan',
])->execute();

Yii proporciona el soporte Active Record para las siguientes bases de datos relacionales:

  • MySQL 4.1 o superior: via yii\db\ActiveRecord
  • PostgreSQL 7.3 o superior: via yii\db\ActiveRecord
  • SQLite 2 y 3: via yii\db\ActiveRecord
  • Microsoft SQL Server 2008 o superior: via yii\db\ActiveRecord
  • Oracle: via yii\db\ActiveRecord
  • CUBRID 9.3 o superior: via yii\db\ActiveRecord (Se debe tener en cuenta que debido a un error en la extensión PDO, valores ente comillas no funcionan adecuadamente, por lo que se requiere tanto el cliente como el servidor de CUBRID 9.3)
  • Sphinx: via yii\sphinx\ActiveRecord, requiere la extensión the yii2-sphinx
  • ElasticSearch: via yii\elasticsearch\ActiveRecord, requiere la extensión the yii2-elasticsearch


Además, Yii también admite el uso de Active Record con las siguientes bases de datos NoSQL:

  • Redis 2.6.12 o superior: vía yii\redis\ActiveRecord, requiere la extensión yii2-redis
  • MongoDB 1.3.0 o superior: vía yii\mongodb\ActiveRecord, requiere la extensión yii2-mongodb


Declarando Clases Active Record

Una clase Active Record se declara extendiendo yii\db\ActiveRecord. Debido a que cada clase Active Record se asocia con una tabla de base de datos, en esta clase se debe sobrescribir el método tableName() para especificar a qué tabla la clase está asociada.

En el siguiente ejemplo, declaramos una clase Cliente asociada a la tabla de base de datos cliente:

namespace app\models;

use yii\db\ActiveRecord;

class Cliente extends ActiveRecord{
    const ESTADO_INACTIVO 0;
    const ESTADO_ACTIVO 1;
    
    /**
     * @Retorna una cadena con el nombre de la tabla asociada a ésta clase ActiveRecord.
     */
    public static function tableName()
    {
        return 'cliente';
    }
}

Las instancias Active Record son consideradas como modelos. Por esta razón las colocamos bajo el nombre de espacio app\models.

Y puesto que, yii\db\ActiveRecord se extiende de yii\base\Model, hereda todas las características, tales como atributos, reglas de validación, serialización de datos, entre otros.

Conexión a Bases de Datos

De forma predeterminada, Active Record utiliza el componente de aplicación db como la conexión de base de datos para acceder y manipular los datos de la base de datos. El componente db se lo configura de manera similar a:

return [
    'components' => [
        'db' => [
            'class' => 'yii\db\Connection',
            'dsn' => 'mysql:host=localhost;dbname=testdb',
            'username' => 'demo',
            'password' => 'demo',
        ],
    ],
];

Si requerimos de una conexión de base de datos diferente, debemos sobrescribir el método getDb():

class Cliente extends ActiveRecord{
    // ...

    public static function getDb()
    {
        // utiliza el componente "db2"
        return \Yii::$app->db2;  
    }
}



También te puede interesar:
Trabajando con bases de datos
DAO - Database Access Objects (Objetos de acceso a base de datos)
Consulta de Datos