DAO genérico
1Presentación
Como pudiste ver rápidamente en la introducción, Temma es capaz de crear DAO automáticamente.
El caso más simple es cuando creas un controlador que necesita acceder únicamente a una tabla de la base de datos.
Basta entonces con que este controlador tenga un atributo protegido llamado $_temmaAutoDao, definido con el valor booleano true.
Si tomamos el ejemplo dado en la introducción:
class Article extends \Temma\Web\Controller {
// indica que el DAO debe crearse automáticamente
protected $_temmaAutoDao = true;
}
Al hacer esto, Temma creará un objeto de tipo \Temma\Dao\Dao, que se configurará automáticamente
para facilitar la manipulación de los datos almacenados en una tabla cuyo nombre es idéntico al del controlador (article).
Este objeto estará disponible a través del atributo $this->_dao.
2Configuración avanzada
2.1Principio general
Por defecto, los DAO asumen varias cosas:
- La conexión a la base de datos está configurada con una fuente de datos llamada db.
- Si la caché está accesible (configurada con una fuente de datos llamada cache), se usa para acelerar el acceso a los datos.
- La tabla está en la base de datos sobre la que se abre la conexión.
- El nombre de la tabla coincide con el nombre del controlador.
- El campo que contiene la clave primaria se llama id.
- Todos los campos de la tabla deben recuperarse en cada acceso.
- Cuando obtenemos los campos de la tabla, obtenemos los nombres tal como están en la base de datos.
Para poder configurar el funcionamiento del DAO (desactivar la caché, especificar un nombre de base de datos o de tabla diferente, renombrar los campos, ...), es recomendable escribir un objeto DAO personalizado, que puede contener la información específica que necesites.
2.2Configuración específica
Sin embargo, si estás en la situación en la que un controlador necesita manipular datos configurando finamente
el comportamiento del DAO, pero no quieres crear un objeto DAO personalizado, es posible
proporcionar los parámetros directamente en el controlador, rellenando un array asociativo.
Ten en cuenta que esta técnica sigue siendo limitada y no debe usarse, en particular, en el caso de que la misma
tabla sea accedida por varios controladores diferentes.
Aquí tienes un ejemplo de uso con parámetros específicos:
class Article extends \Temma\Web\Controller {
protected $_temmaAutoDao = [
'cache' => false,
'base' => 'cms',
'table' => 'content',
'id' => 'cid',
'fields' => [
'cid' => 'contentId',
'title',
'login',
'content' => 'text',
'date' => 'creationDate',
'status',
],
];
}
- Línea 2: Definición del array de configuración del DAO.
- Línea 3: Desactiva la caché.
- Línea 4: Definición del nombre de la base de datos que contiene la tabla.
- Línea 5: Definición del nombre de la tabla.
- Línea 6: Definición del nombre del campo que contiene la clave primaria.
- Líneas 7 a 14: Definición de un array asociativo que contiene la lista de campos que se deben recuperar de la base de datos. Si los campos necesitan renombrarse, basta con declarar un par asociativo cuya clave es el nombre del campo en la tabla, y el valor asociado es el nombre por el que debe renombrarse.
Los parámetros son independientes, no tienes que redefinirlos todos.
3Operaciones básicas
Los objetos \Temma\Dao ofrecen 6 métodos básicos:
- count(): Cuenta el número de elementos de una tabla.
- get(): Devuelve toda la información sobre un elemento de la tabla.
- search(): Busca registros.
- create(): Añade un nuevo elemento a la tabla.
- remove(): Elimina uno o varios elementos.
- update(): Actualiza uno o varios registros.
Estos métodos se explican en detalle más abajo, pero antes veremos cómo se construyen los criterios de búsqueda y los criterios de ordenación, que se pueden usar en algunos de estos métodos.
4Criterios de búsqueda
Los DAO ofrecen un mecanismo para componer fácilmente filtros de búsqueda. Para ello, tienes que crear un criterio, que se puede pasar como parámetro de ciertos métodos.
El tipo de criterio más simple es un array asociativo cuyas claves corresponden a campos de la tabla, y cuyos valores corresponden a los que se buscarán en las filas seleccionadas.
Alternativamente, puedes crear un criterio usando el método criteria(), que crea un objeto de tipo \Temma\Dao\Criteria.
Después puedes combinar llamadas con los siguientes métodos:
- equal(): un campo vale un determinado valor (con la posibilidad de dar una lista de valores posibles)
- different(): un campo no vale un determinado valor (con la posibilidad de dar una lista de valores posibles)
- like(): un campo de texto cumple una expresión de búsqueda
- notLike(): un campo de texto no coincide con una expresión de búsqueda
- is(): un campo booleano está definido como "true"
- isNot(): un campo booleano está definido como "false"
- lessThan(): el valor de un campo numérico es menor que un valor dado
- greaterThan(): el valor de un campo numérico es mayor que un valor dado
- lessOrEqualTo(): el valor de un campo numérico es menor o igual que un valor dado
- greaterOrEqualTo(): el valor de un campo numérico es mayor o igual que un valor dado
Existen alias, para simplificar la escritura:
- has(): equivalente a is()
- hasNot(): equivalente a isNot()
- eq(): equivalente a equal()
- ne(): equivalente a different()
- lt(): equivalente a lessThan()
- gt(): equivalente a greaterThan()
- le(): equivalente a lessOrEqualTo()
- ge(): equivalente a greaterOrEqualTo()
Por defecto, los criterios se combinan usando operadores booleanos "AND", lo que lleva a la creación de un sistema de filtrado de datos:
solo se recuperan los datos que cumplen todas las condiciones. Es posible combinar todos los criterios según el operador "OR"
pasando la cadena "or" como parámetro del método criteria().
También es posible combinar los criterios con operadores booleanos gracias a los métodos and() y
or(), que reciben cada uno un nuevo objeto de criterio como parámetro.
4.1Ejemplo de un array asociativo
// elimina los registros cuya categoría es "article"
// y cuya visibilidad es "hidden"
$criteria = [
'category' => 'article',
'visibility' => 'hidden',
];
$this->_dao->remove($criteria);
4.2Ejemplo equal() e is()
// busca registros cuya dirección de email es igual a "tom@tom.com"
// Y cuyo booleano "free" es true
$critera = $this->_dao->criteria()
->equal('email', 'tom@tom.com')
->is('free');
$users = $this->_dao->search($criteria);
- Línea 3: Creación del objeto de criterio.
- Línea 4: Añade un criterio de igualdad. El campo email debe tener el valor tom@tom.com.
- Línea 5: Añade un criterio de igualdad sobre un booleano. El campo free debe estar definido como "true".
- Línea 6: El objeto de criterio se usa con el método search() del DAO para recuperar los elementos que corresponden al criterio.
4.3Ejemplo greaterThan() y lessThan()
// busca registros con una edad mayor que 12
// Y menor que 20
$criteria = $this->_dao->criteria()
->greaterThan('age', 12)
->lessThan('age', 20);
- Línea 3: Creación del objeto de criterio.
- Línea 4: Añade un criterio de comparación. El campo age debe ser estrictamente mayor que 12.
- Línea 5: Añade un criterio de comparación. El campo age debe ser estrictamente menor que 20.
4.4Ejemplo or(), like() y different()
// busca registros cuyo email sea de Gmail
// O cuyo nombre no sea el de un creador de Google
$criteria = $this->_dao->criteria('or')
->like('email', '%@gmail.com')
->different('name', ['Sergey', 'Larry']);
- Línea 3: Creación del objeto de criterio, especificando que los criterios se asociarán mediante operadores "OR" (y no "AND" como por defecto).
- Línea 4: Añade un criterio de comparación. El campo email debe terminar con la cadena "@gmail.com".
- Línea 5: Añade un criterio de comparación. El campo name no debe contener el valor "Sergey" ni "Larry".
4.5Ejemplo de lógica booleana
// busca registros donde:
// el email es "john@john.com" o "bob@bob.com",
// Y cuya edad es menor o igual que 12
// O estrictamente mayor que 24
$criteria = $this->_dao->criteria()
->equal('email', ['john@john.com', 'bob@bob.com'])
->and(
$this->_dao->criteria('or')
->lessOrEqualTo('age', 12)
->greaterThan('age', 24)
);
- Línea 5: Creación del objeto de criterio.
- Línea 6: Añade un criterio de igualdad, proporcionando una lista de valores.
- Línea 7: Añade un operador booleano "AND", que contiene un subconjunto de criterios.
- Línea 8: Creación del subconjunto de criterios. Especificamos que los distintos criterios de este conjunto se enlazarán mediante operadores "OR".
- Línea 9: Añade un criterio de comparación. El campo age debe contener un valor menor o igual que 12.
- Línea 10: Añade un criterio de comparación. El campo age debe ser estrictamente mayor que 24.
5Criterios de ordenación
El método search() puede devolver varios elementos, que quizá quieras ordenar de una determinada manera.
Es posible ordenar de 3 maneras diferentes:
- Proporcionando el nombre de un campo, lo que realizará una ordenación ascendente sobre los valores de ese campo. Si el nombre del campo empieza con un guion, se realizará una ordenación descendente.
- Transmitiendo un array que contiene los nombres de los campos sobre los que se debe realizar la ordenación. Por defecto, la ordenación es ascendente (del valor más pequeño al más grande), pero es posible especificar una ordenación descendente colocando un guion delante del nombre del campo. También es posible usar un array asociativo cuya clave es el nombre del campo y cuyo valor es la cadena desc.
- Proporcionando el valor booleano false, para obtener una ordenación aleatoria.
// ordenación por fecha de nacimiento, ascendente
$sort = 'birthday';
// ordenación por fecha de nacimiento, descendente
$sort = '-birthday';
// ordenación por fecha de nacimiento (ascendente)
// y por número de puntos (descendente)
$sort = ['birthday', '-points'];
// equivalente al anterior
$sort = [
'birthday',
'points' => 'desc'
];
// equivalente al anterior
$sort = [
'birthday' => 'asc',
'points' => 'desc'
];
// ordenación aleatoria
$sort = false;
6Métodos
6.1count()
count(null|array|\Temma\Dao\Criteria $criteria=null) : int
Si este método se llama sin parámetros, devuelve el número total de elementos de la tabla.
Si se llama con un objeto de criterio o un array como parámetro, devuelve el número de elementos que corresponden a los criterios.
Ejemplo de uso:
// obtiene el número total de elementos de la tabla
$cnt = $this->_dao->count();
// obtiene el número de usuarios llamados "Bob" (array)
$cnt = $this->_dao->count(['name' => 'Bob']);
// obtiene el número de usuarios llamados "Bob" (objeto)
$cnt = $this->_dao->count(
$this->_dao->criteria()->equal('name', 'Bob')
);
6.2get()
get(int|string|array|\Temma\Dao\Criteria $id) : array
Este método devuelve toda la información sobre un registro de la tabla, cuyo identificador de clave primaria se pasa como parámetro. Los datos se devuelven en un array asociativo, que enumera los pares cuya clave es el nombre del campo.
Se puede proporcionar un criterio de búsqueda como parámetro en lugar de una clave primaria. Si este criterio recupera varios registros, solo se devolverá el primero.
Si se proporcionó una lista de campos al crear el DAO, solo se devuelven los campos en cuestión. Si esa lista incluía el renombrado de campos, las claves del array asociativo se modifican en consecuencia.
Ejemplo de uso:
// obtiene la información del artículo con el identificador 12
$data = $this->_dao->get(12);
// muestra el título
print($data['title']);
6.3search()
search(null|array|\Temma\Dao\Criteria $criteria=null, null|false|string|array $sort=null,
?int $limitOffset=null, ?int $limit=null) : array
Este método se usa para recuperar datos de varias filas de la tabla. Devuelve una lista cuyo cada elemento es
un array asociativo (cuyo contenido es idéntico al que devuelve el método get(), ver arriba).
El primer parámetro es un criterio de búsqueda, tal como se explicó antes en esta página. Si no se proporciona, o se pasa como null,
el método tomará todas las filas de la tabla.
El segundo parámetro contiene las opciones de ordenación, tal como se explicó antes en esta página.
El tercer parámetro puede contener el número del primer elemento a devolver (empezando en cero).
El cuarto parámetro puede contener el número de elementos a devolver.
Ejemplo de uso:
// busca los 5 artículos más recientes,
// entre todos los escritos desde el 1 de enero de 2000
$articles = $this->_dao->search(
$this->_dao->criteria()->greaterThan('date', '2011-01-01'),
'-date',
null,
5
);
// muestra los títulos de todos los artículos recuperados
foreach ($articles as $article)
print($article['title']);
// busca 3 artículos aleatorios
$articles = $this->_dao->search(null, false, null, 3);
6.4create()
create(array $data, mixed $safeData=null) : int
Este método añade una nueva fila a la tabla.
Se debe proporcionar un array asociativo como parámetro, que contenga pares clave/valor correspondientes a cada campo de la tabla.
El método devuelve el identificador de clave primaria del elemento recién creado.
Ejemplo de uso:
// creación del nuevo artículo
$id = $this->_dao->create([
'title' => 'Título de prueba',
'login' => 'Bob',
'date' => date('c'),
'content' => 'Texto...',
]);
// muestra el identificador del nuevo artículo
print($id);
El método puede recibir un segundo parámetro opcional, que se usa para evitar deadlocks causados por inserciones que generan una duplicación de clave.
El parámetro puede tomar como valor:
- null: (valor por defecto) La consulta genera un error si hay una duplicación de clave.
- true: Se actualizarán todos los campos listados en el primer parámetro, usando los valores proporcionados.
- El nombre de un campo: Este campo se actualizará. Si este campo está listado en el primer parámetro, se usará el valor asociado; en caso contrario, el campo conservará el valor que tiene en la base de datos.
- Un array asociativo que contiene los campos que se actualizarán. Las claves son los nombres de los campos, y los valores asociados se usarán para actualizarlos.
6.5remove()
remove(null|int|string|array|\Temma\Dao\Criteria $criteria=null) : int
Este método se usa para eliminar registros de la tabla.
Si se llama sin parámetros, elimina todos los elementos.
Si se pasa un identificador numérico como parámetro, se usará como la clave primaria del elemento que se eliminará.
Si se pasa un criterio de búsqueda como parámetro, se usa para elegir los elementos que se eliminarán.
Este método devuelve el número de filas eliminadas.
Ejemplo de uso:
// elimina el artículo con el identificador 12
$this->_dao->remove(12);
// elimina todos los artículos escritos por Bob (array)
$this->_dao->remove(['login' => 'Bob']);
// elimina todos los artículos escritos por Bob (objeto)
$this->_dao->remove(
$this->_dao->criteria()->equal('login', 'Bob')
);
6.6update()
update(null|int|string|array|\Temma\Dao\Criteria $criteria, array $data=[],
null|false|string|array $sort=null, ?int $limit=null) : int
Con este método, puedes modificar los datos guardados en la tabla.
Si el primer parámetro se define como null, se modificarán todos los elementos de la tabla.
Si se pasa un identificador numérico o una cadena como parámetro, se usará como la clave primaria del elemento modificado.
Si se pasa un criterio de búsqueda como parámetro, se usará para seleccionar los elementos que se modificarán.
El segundo parámetro debe contener un array asociativo, con pares clave/valor correspondientes a los campos que se van a actualizar (con una declaración idéntica a la del método create()).
El tercer parámetro (opcional) contiene las opciones de ordenación, tal como se explicó antes en esta página. Solo es útil junto con el cuarto parámetro.
El cuarto parámetro (opcional) se usa para limitar el número de filas que se actualizarán. Si se define como null (valor por defecto), se modificarán todas las filas que coincidan con los criterios.
Este método devuelve el número de filas modificadas.
Ejemplo de uso:
// renombra "Bob" a "Robert" en todos sus artículos (array)
$this->_dao->update(
['login' => 'Bob'],
['login' => 'Robert']
);
// renombra "Bob" a "Robert" en todos sus artículos (objeto)
$this->_dao->update(
$this->_dao->criteria()->equal('login', 'Bob'),
['login' => 'Robert']
);
7Gestión de la caché
Usar la caché acelera las lecturas de la base de datos. Lamentablemente, esto puede tener un efecto indeseable: todas las consultas idénticas devolverán resultados idénticos durante el tiempo de vida de la caché. Esto puede ser un problema cuando ejecutas consultas que se escriben de forma idéntica pero necesitan devolver resultados diferentes (por ejemplo, selecciones cuyos resultados se ordenan de forma aleatoria, o si de vez en cuando necesitas obtener datos actualizados).
Por lo tanto, es posible desactivar temporalmente el uso de la caché usando el método disableCache(). La reactivación se hace usando el método enableCache(). Estos dos métodos devuelven la instancia de su DAO, a menos que se les pase un parámetro, que será entonces el que se devuelva.
Ejemplo de uso:
// desactiva la caché
$this->_dao->disableCache();
// búsqueda
$result = $this->_dao->search();
// reactiva la caché
$this->_dao->enableCache();
// igual que el anterior
$result = $this->_dao->disableCache()->search();
$this->_dao->enableCache();
// resultado idéntico a los anteriores
// orden de ejecución:
// 1. disableCache()
// 2. search(), que se escribe como parámetro de enableCache()
// 3. enableCache()
// al final, se recupera el resultado de search(),
// porque es lo que devuelve enableCache()
$result = $this->_dao
->disableCache()
->enableCache($this->_dao->search());