0 follower

Objetos de Acceso a Datos (DAO)

Los Objetos de Acceso a Datos (DAO) proveen una API genérica para acceder a los datos almacenados en diferentes sistemas de administraciond de bases de datos (DBMS). Como resultado, se puede cambiar de un DBMS a otro sin la necesidad de cambiar el código que usa DAO para acceder a los datos.

El DAO de Yii está construido sobre Objetos de Datos de PHP, que es una extensión que provee acceso unificado a datos a los DBMS más populares, como MySQL, PostgreSQL, etc. Por lo tanto, para usar el DAO de Yii, tienen que ser instaladas tanto la extensión PDO como el driver PDO de la base de datos (ej.: PDO_MYSQL).

El DAO de Yii principalmente consiste de las siguientes cuatro clases:

  • CDbConnection: representa una conexión a una base de datos.
  • CDbCommand: representa una sentencia SQL a jecutar en la base de datos.
  • CDbDataReader: representa un flujo (solo de avance) de filas del resultado de una consulta.
  • CDbTransaction: representa una transacción de base de datos.

A continuación, mencionaremos el uso del DAO Yii en diferentes escenarios.

1. Estableciendo la Conexión con la Base de Datos

Para establecer la conexión con una base de datos, creamos una instancia de CDbConnetion y la activamos. Es necesario un nombre de fuente de datos (DNS) para especificar la información requerida para conectarse a la base de datos. Un nombre de usuario y contraseña pueden ser también necesarios para establecer la conexión. Será arrojada una excepción en el caso de que ocurra algún error al establecer la conexión (ej.: DNS malo o nombre de usuario/contraseña inválidos).

$connection=new CDbConnection($dsn,$username,$password);
// establish connection. You may try...catch possible exceptions
$connection->active=true;
......
$connection->active=false;  // close connection

La forma del DNS depende del driver PDO de la base de datos en uso. En general, un DNS consiste del nombre del driver PDO, seguido por dos puntos (:), seguido por la sintaxis específica del driver. Mira la Documentación PDO para una información completa. A continuación, una lista de los formatos DNS más comunmente utilizados:

  • SQLite: sqlite:/path/to/dbfile
  • MySQL: mysql:host=localhost;dbname=testdb
  • PostgreSQL: pgsql:host=localhost;port=5432;dbname=testdb

Puesto que CDbConnetion extiende de CApplicationComponent, podemos también usarla como un Componente de Aplicación. Para hacerlo, configuramos un componente de aplicación db (u otro nombre) en Configuración de la Aplicación como sigue,

array(
    ......
    'components'=>array(
        ......
        'db'=>array(
            'class'=>'CDbConnection',
            'connectionString'=>'mysql:host=localhost;dbname=testdb',
            'username'=>'root',
            'password'=>'password',
        ),
    ),
)

Podemos entonces acceder a la conexión de la base de datos a través de Yii::app()->db (donde db es el nombre que le pusimos al componente) que ya está activada, a menos que explicitamente configuremos a CDbConnection::autoConnect en false. Usar este enfoque, una simple conexión con la base de datos puede ser usada en diferentes lugares en nuestro código.

2. Ejecutando Sentencias SQL

Una vez establecida la conexión con la base de datos, las sentencias SQL pueden ser ejecutadas usando CDbCommand. Se crea una instancia CDbCommand llamando a CDbConnetion::createCommand() con la sentencia SQL especificada:

$command=$connection->createCommand($sql);
// if needed, the SQL statement may be updated as follows:
// $command->text=$newSQL;

Una sentencia SQL es ejecutada a través de CDbCommand en una de las siguientes dos maneras:

  • execute(): ejecuta una sentencia SQL que no es consulta, como INSERT, UPDATE y DELETE. Si es exitosa, devuelve el numero de filas afectadas por la ejecución.
  • query(): ejecuta una sentencia SQL que devuelve filas de datos, como SELECT. Si es exitosa, develve una instancia de CDbDataReader, a partid de la cual se recorrer el resultado de las filas de datos. Por conveniencia, están implementados un conjunto de métodos queryXXX(), los cuales devuelven directamente el resultado de la consulta.

Será arrojada una excepción si ocurre un error durante la ejecución de una sentencia SQL.

$rowCount=$command->execute();   // ejecuta una sentencia SQL sin resultados
$dataReader=$command->query();   // ejecuta una consulta SQL
$rows=$command->queryAll();      // consulta y devuelve todas las filas de resultado
$row=$command->queryRow();       // consulta y devuelve la primera fila de resultado
$column=$command->queryColumn(); // consulta y devuelve la primera columna de resultado
$value=$command->queryScalar();  // consulta y devuelve el primer campo en la primer fila

3. Obteniendo Resultados de la Consulta

Luego de que CDbCommand::query() genere la instancia de CDbDataReader, podemos recuperar filas del resultado llamando a CDbDataReader::read() de manera repetida. Podemos tambien usar un CDbDataReader en un foreach de PHP para recuperar fila a fila.

$dataReader=$command->query();
// calling read() repeatedly until it returns false
while(($row=$dataReader->read())!==false) { ... }
// usando foreach para atravesar cada fila de datos
foreach($dataReader as $row) { ... }
// recuperando todos los datos de una vez en un único arreglo
$rows=$dataReader->readAll();

Nota: A diferencia de query(), todos los métodos queryXXX() devuelven datos directamente. Por ejemplo, queryRow() devuelve un arreglo representando la primera fila del resultado de la consulta.

4. Usando Transacciones

Cuando una aplicación ejecuta unas pocas consultas, cada una leyendo y/o escribiendo información en la base de datos, es importante aseguarse que la base de datos no se quede sólo con algunas de las consultas llevadas a cabo. Para evitar esto, puede ser iniciada una transacción, representada en Yii como una instancia de CDbTransaction:

  • Comenzar la transacción.
  • Ejecutar consultas una a una. Ninguna actualización a la base de datos es visible al mundo exterior.
  • Consignar la transacción. Las actualizaciones se vuelven visibles si la transacción es exitosa.
  • Si una de las consultas falla, la transacción entera se deshace.

El anterior flujo de trabajo puede ser implementado usando el siguiente código:

$transaction=$connection->beginTransaction();
try
{
    $connection->createCommand($sql1)->execute();
    $connection->createCommand($sql2)->execute();
    //.... other SQL executions
    $transaction->commit();
}
catch(Exception $e) // se arroja una excepción si una consulta falla
{
    $transaction->rollBack();
}

5. Vinculando Parámetros

Para evitar ataques de SQL injection y para mejorar el rendimiento de sentencias SQL usadas repetidas veces, podemos "preparar" una sentencia SQL con marcadores de posición de parámetros opcionales, que son marcadores que serán reemplazados con los parámetros reales durante el proceso de vinculación de parámetros. El driver subyacente de la base de datos lo hará por nosotros. La vinculación de parámetros debe hacerse antes de que la sentencia SQL sea ejecutada.

// una SQL con dos marcadore de posición, ":username" and ":email"
$sql="INSERT INTO users(username, email) VALUES(:username,:email)";
$command=$connection->createCommand($sql);
// reemplaza el marcador de posición ":username" con el valor real de username
$command->bindParam(":username",$username,PDO::PARAM_STR);
// reemplaza el marcador de posición ":email" con el valor real de email
$command->bindParam(":email",$email,PDO::PARAM_STR);
$command->execute();
// inserta otra fila con un nuevo conjunto de parámetros
$command->bindParam(":username",$username2,PDO::PARAM_STR);
$command->bindParam(":email",$email2,PDO::PARAM_STR);
$command->execute();

Los métodos bindParam() y bindValue() son muy similares. La única diferencia es que el primero vincula un parámetro con una variable PHP mientras que el último con un valor. Para parámetros que representan grandes bloques de memoria de datos, es preferible el primero por consideraciones de rendimiento.

Para más detalles acerca de la vinculación de parámetros, mira la documentación PHP relevante.

6. Vinculando Columnas

Al recoger los datos del resultado de una consulta, podemos tambien vincular columnas con variables PHP para que sean automáticamente rellenadas con los datos apropiados cada vez que una fila es recogida.

$sql="SELECT username, email FROM users";
$dataReader=$connection->createCommand($sql)->query();
// vincular la 1er columna (username) con la variable $username
$dataReader->bindColumn(1,$username);
// vincular la 2da columna (email) con la variable $email
$dataReader->bindColumn(2,$email);
while($dataReader->read()!==false)
{
    // $username e $email contienen el nombre de usuaario y el email de la fila actual
}