DAO (Data Access Object)

DAO es realmente un **J2EE pattern**. Es muy fácil implementarlo en PHP y ayuda enormemente a separar el acceso a la base de datos del resto del código. Los DAOs normalmente se tratan de una capa fina. La capa DAO puede ser 'apilada' lo cual ayuda por ejemplo si deseamos añadir cacheo de base de datos cuando queramos mejorar la aplicación.

Veamos la estructura del patrón DAO.

Problema a resolver

La mayoría de las aplicaciones, tienen que persistir datos en algún momento, ya sea serializándolos, guardándolos en una base de datos relacional, un fichero XML, etc. Para hacer esto, la aplicación interactúa con la base de datos. El "como interactúa" NO debe ser asunto de la capa de lógica de negocio de la aplicación, ya que para eso está la capa de persistencia, que es la encargada de interactuar con la base de datos. Sabiendo esto, podemos decir que DAO es un patrón de diseño utilizado para crear esta capa de persistencia.

Pero… ¿de qué sirve tener una capa de persistencia?

Bueno, imaginemos que hicimos una aplicación de ecommerce para nuestra empresa. Utilizando como motor de base de datos MySQL. Pero no tenemos dividida la capa de lógica de negocio de la de persistencia. Por lo que la interacción con la base de datos se hace directamente desde la capa de lógica de negocio. Nuestra aplicación consiste en muchos scripts PHP, y gran parte de ellos interactúan con la base de datos (conectándose a la misma, guardando y recuperando datos, etc.).

Nuestra aplicación funciona perfectamente pero nuestro administrador de sistemas nos avisa que todas las aplicaciones de la empresa van a migrar al motor de la base de datos PostgreSQL por X, Y y Z razones.

Si hubiéramos tenido por separado la capa de lógica de negocio de la de persistencia, habría sido suficiente con modificar la capa de persistencia para que la aplicación pudiera utilizar el nuevo motor de base de datos, sin tener que modificar nada de la capa de lógica de negocio. Por lo que vamos a tener que modificar todos los scripts PHP cambiando todas las consultas SQL, la manera de acceder a la base de datos, etc. para adecuarse al nuevo motor de base de datos.

Bien, ahora que sabemos porque es importante tener separadas las capas de lógica de negocio y de persistencia, vamos a ver como utilizar el patrón de diseño DAO para crear nuestra propia capa de persistencia.

Tal y como se ha comentado anteriormente, debemos intentar que nuestras clases o scripts PHP no tengan más de una responsabilidad, como por ejemplo, que además de acceder a la base de datos tenga responsabilidades de la lógica de negocio.

Otro ejemplo interesante. Supongamos que en una aplicación para una biblioteca tuviéramos una clase **GestorPrestamos** que se ocupara de realizar y gestionar los préstamos de libros. En una primera aproximación podríamos hacer que esta clase se encargara tanto de la lógica de negocio (por ejemplo comprobar si un usuario es "moroso" antes de prestarle un libro) como del acceso a datos (introducir el préstamo en una hipotética tabla de préstamos).

Este tipo de enfoque lleva sistemos poco modulares y dícilmente mantenibles. En nuestro caso, el cambio de las reglas de negocio implicaría cambios en **GestorPrestamos**. El problema es que además, un cambio en la base de datos también implica cambios en **GestorPrestamos**

Para conseguir mayor modularidad y dividir responsabilidades, tendríamos que llevar la persistencia de datos en una clase separada y dejar **GestorPrestamos** solo con la lógica de negocio.

Solución

Tal y como se ha comentado anteriormente, en cualquier proyecto se debe separar la persistencia de datos del resto de funcionalidades del sistema. Esa capa sería la que se encargaría de acceder a la fuente de datos mediante el patrón DAO

El patrón DAO gestionará la conexión con la fuente de datos para obtener y almacenar información en la misma, por lo que si en un futuro cambiamos de base de datos tan sólo tendríamos que modificar la capa DAO.

Imagen gráfica de donde se colocaría el patrón DAO:

![Capa de abstracción que soluciona el problema de que varios ficheros accedan a la base de datos][fig_capa_acceso_datos] [fig_capa_acceso_datos]: maestro-img173.png "Capa de abstracción que soluciona el problema de que varios ficheros accedan a la base de datos" ###Funcionamiento

Como se ha comentado anteriormente, DAO encapsula el acceso a la base de datos. Por lo que cuando la capa de lógica de negocio necesite interactuar con la base de datos, va a hacerlo a través de la API que le ofrece DAO. Generalmente esta API consiste en métodos CRUD (Create, Read, Update y Delete). Por ejemplo, cuando la capa de lógica de negocio necesite guardar un dato en la base de datos, llamará a un método create(). Lo que haga este método, es problema de DAO y depende de como DAO implemente dicho método, puede que lo implemente de manera que los datos se almacenen en una base de datos relacional como puede que lo implemente de manera que los datos se almacenen en ficheros de texto. Lo importante es que la capa de lógica de negocio no tiene porque saberlo, lo único que sabe es que el método create() va a guardar los datos, así como el método delete() va a eliminarlos, el método update() actualizarlos, etc. Pero no tiene idea de como interactúa DAO con la base de datos.

En una aplicación, hay tantos DAOs como modelos. Es decir, en una base de datos relacional, por cada tabla, habría un DAO.

DAO consiste básicamente en una clase que es la que interactúa con la base de datos. Los métodos de esta clase dependen de la aplicación y de lo que queramos hacer. Pero generalmente se implementan los métodos CRUD para realizar las "4 operaciones básicas" de una base de datos. Por ejemplo, cuando la capa de lógica de negocio llama al método create(), ¿qué es lo que hace DAO? inserta un nuevo dato ¿pero qué dato? el que la capa de lógica de negocio le pase como parámetro.

Por ejemplo, en la base de datos relacional **uazon** disponemos de la tabla libros, por lo que tendremos una clase llamada **LibroDAO** que se encargará de las cuatro operaciones básicas (CRUD) insertar, leer, actualizar y borrar libros (posiblemente haga más cosas pero eso tiene que ser básico). Cuando la capa de lógica de negocio quiera guardar un libro en la base de datos, va a enviar los datos del mismo (encapusaldos en un array o un nuevo objeto) al método create() de LibroDAO y este será el encargado de guardar ese nuevo libro en la base de datos. Lo mismo pasaría para eliminar datos. Y para actualizarlos además se le pasaría el id, para saber que libro actualizar. Para buscar datos, sería parecido, ya que se le pasa al método read() el id del libro para usarlo como patrón de búsqueda, pero con la diferencia de que este método tiene valor de retorno, ya que devuelve, por ejemplo, un array asociativo con los datos del resultado de la búsqueda.

Consecuencias

Destacamos algunos puntos importantes:

  • Transparencia: el fichero index.php le pide los libros al DAO y este le devuelve un array con los mismos. Dicho fichero no tiene porqué saber que hay detrás del DAO.
  • Fácil migración a otra fuente de datos.
  • Reduce la complejidad en la lógica de negocio.
  • Centraliza el acceso a los datos en una capa separada.
  • No es útil cuando este servicio lo proporciona algún mecanismo (framework)
  • Añade una capa extra al sistema
  • Necesita una jerarquía de clases

El DAO no tiene por qué implementar todas las operaciones CRUD (Create-Read-Update-Delete), por ejemplo, es posible que no queramos borrar ningún pedido de la base de datos. En general por cada objeto de negocio crearemos un DAO distinto. Para el backend tendríamos **LibroDAO**, **CometarioDAO**, **PedidoDAO**, etc.

El código PHP siguiente podría ser una aproximación de lo que suele contener una clase DAO:

class LibroDAO {
   private $conn;
   function __construct($conn) {
      $this->conn = $conn;
   }
   function create() {
      #ejecutamos una sentencia INSERT
   }
   function read($id) {
      #EJECUTAMOS SENTENCIA SELECT
      return $libro
   }
   function delete($id) {
      #ejecutar SENTENCIA DELETE
   }
   function update($id) {
      #ejecutamos una sentencia UPDATE
   }
}

Ejemplo de LibroDAO con código PHP específico:

class LibroDAO {
   private $connection;
   public function __construct($pconnection) {
      $this->connection = $pconnection;
   }
   public function create(){;}
   public function read($id) {
      $query = "SELECT id, isbn, titulo " .
         "FROM libros " .
         "WHERE id = ? ";
      $stmt = $this->connection->prepare($query);
      $stmt->execute(array($id));
      $libro = $stmt->fetch();
      if ($libro === false)
         return null;
      return $libro;
   }
   public function update(){;}
   public function delete(){;}
}

Ejemplo de cómo integrar la clase LibroDAO dentro del fichero **libros_ficha.php** encargado de obtener el id por GET y leer ese libro de la base de datos. Este sería el código de ejemplo:

<?php
......
try
{
   $db = new PDO('mysql:host=localhost;dbname=uazon;', $user, $pwd);
   if ( !isset ( $_GET['id'] ) )
   {
      throw new Exception("No has especificado un identificador de libro");
   }
   $id = $_GET['id'];
   //pasamos la conexi&oacute;n de la base de datos al DAO
   $libroDAO = new LibroDAO($db);
   $libro = $libroDAO->read($id);
   echo '<b>Titulo: ' . $libro['titulo'] . '</b>';
   ......
?>

Insertar un libro

<?php
......
if (!isset($_POST['insertarLibro']))
   throw new Exception('No has pulsado el bot&oacute;n de insertar libro.');
//Recoger las variables del formulario de inserci&oacute;n
$isbn = $_POST['isbn'];
$titulo = $_POST['titulo'];
$editorial = $_POST['editorial'];
//VALIDACIONES
$libro = array(
   "isbn"=>$isbn,
   "titulo"=>$titulo,
   "editorial"=>$editorial));
$db = new PDO('mysql:host=localhost;dbname=uazon;', $user, $pwd);
$libroDAO = new LibroDAO($db);
$libroDAO->create($libro);
echo '<b>Libro insertado correctamente</b>';
......
?>

results matching ""

    No results matching ""