1.12.05

¿Cómo abstraer nuestra conexión con la base de datos?


En este pequeño ejemplo nos vamos a introducir en el uso del Framework PEAR", y en particular hemos seleccionado la "clase" (concepto de la programación orientada a objetos) para hacer la "abstracción de la base de datos".


La idea es tener "algo" entre medio de "nuestro sistema" y la "base de datos" que nos "oculte" la complejidad de la misma y toda dependencia con ella.


Si nosotros programáramos usando directamente las sentencias (o funciones) que nos provee el propio lenguaje, estaríamos atados de forma explícita a la base de datos.


Por ejemplo, disponemos de funciones como:



$con = mysql_connect($cadena_de_conexion);
$result = mysql_query('SELECT * FROM clientes', $con);
while ($row = mysql_fetch_row($result)) {
echo $row[0] . ': ' . $row[1] . "\n";
}
mysql_free_result($result);
msql_close();
?>


Este ejemplo está hecho con funciones donde explicitan cual es nuestra base de datos (mysql_connect, mysql_query, etc). Si en un futuro deseamos cambiar de motor a PostgreSQL (por ejemplo) deberíamos recorrer todos nuestros fuentes que usen la base y sustituir por las funciones correspondientes: pg_connect, pg_query.


Es evidente que volveríamos a tener el mismo problema si en un futuro necesitáramos cambiar nuevamente de base, por ejemplo, a Oracle.


Una solución es usar "algo" que nos "abstraiga" del trabajo con la base de datos; en este caso nos apoyaremos en una de las clases disponibles del Framework Pear: "DB"




require_once 'DB.php';
$db =& DB::connect('mysql://root:@localhost/prueba');
$result =& $db->query('SELECT * FROM clientes');
while ($result->fetchInto($row)) {
echo $row[0] . ': ' . $row[1] . "\n";
}
$db->free();
$db->disconnect();
?>


Si nos encontramos que debos cambiar de motor de base de datos, solo deberíamos cambiar cambiar el nombre de "motor" en la cadena de conexión (mysql -> pgsql).


Alguien podría decir que lo único que mejoramos es que en vez de cambiar muchas líneas explícitas sobre MySQL ahora debemos cambiar una (la cadena de conexión) en todos nuestros fuentes.


La idea es hacer un ejemplo rápido para explicar de forma fácil las ventajas de esta forma de trabajo. Se me ocurren tres alternativas rápidas para solucionar estos inconvenientes:



  • Creamos un archivo con la sintaxis de conexión y lo incluimos en todos nuestros fuentes. Cuando se necesite cambiar la cadena, solo se deberá modificar un solo fuente.

  • Creamos un archivo de configuración que define "constantes" y luego estas se usan en todas las cadenas de conexión.

  • Creamos nuestra propia clase "BaseDeDatos" que en su estructura tienen los atributos cargados con valores por defecto que definen como será nuestra conexión a la base. Modificamos los atributos de la clase, y cambia el comportamiento de la conexión.


La última opción es la que prefiero, pues agregamos una nueva capa de abstracción que nos abstrae ahora de la capa de abstracción de Pear (valga todo lo redundante que fui).

¿Cual es el beneficio de esto?

Estamos protegidos ante un nuevo potencial problema: que debamos cambiar la clase "DB" por otra que ofrezca mayores funcionalidades, lo que nos obligaría a modificar todos nuestros fuentes nuevamente y agregar la sintaxis de la nueva clase de abstracción!



¿Fantástico, no? ;-)



¡Saludos abstractos!

6 comentarios:

Anónimo dijo...

es genial... sin mencionar las cosas que podemos agregarle, por ejemplo el paquete DB_DataObjects... que ya nos crea una clase con metodos para altas/bajas/modificaciones/etc de cada una de nuestras tablas en la bd. se los recomiendo!

Enrique Place dijo...

Si, está muy bueno. En su momento pensé que había encontrado la "panacea" y que este paquete iba a resolver todos los problemas de trabajar con una base de datos desde PHP.

Necesitaba crear un formulario de consultas con muchos componentes que podían venir cargados de datos o venir vacíos. Cada componente "no vacío" generaba un cambio en la consulta SQL (agregar campos en el SELECT, tablas en el FROM, condiciones en el WHERE, etc).

El primer prototipo que planteaba una solución a este problema usaba DB_DataObject y funcionaba muy bien.

El problema se originó cuando pasamos al desarrollo profundo de todas las funcionalidades y nos terminamos dando contra la pared cuando las condiciones obligaban a usar muchos JOINS entre tablas.

Al final, tuvimos que desecharlo y resolverlo de otra forma: crear una clase propia llamada SentenciaSQL que creaba dinámicamente el SQL y luego era pasada la sentencia final al paquete DB (el más sencillo de todos) de forma directa.

Moraleja: "hay que tener mucho cuidado con los prototipos porque no dejan de ser prototipos y la realidad es mucho más compleja" ;-)

Buscaré tiempo para armar un post con esta experiencia ;-)

Anónimo dijo...

Tal vez el problema es que pensamos demasiado en el álgebra relacional, no crees?

Sería genial que compartieras aquel problema que te encontraste con ciertos JOINs que no podías sustituir con DataObject, seguro que encontramos la manera de sacarlo con DataObject ;)

Un saludo

Pd. Felicidades por tus weblogs, sin duda, muy buenos artículos.

Enrique Place dijo...

Es que ese es el problema, y ahí es cuando debes decidir si lo que más conviene es el modelo-relacional o el orientado a objetos, cuando es conveniente mantener los objetos o cuando es conveniente saltearte todo el modelo y pasarte directamente a la base de datos.

La traducción entre modelos de datos es el problema. Cuando podamos usar bases de datos orientadas a objetos y persistir un objeto en la base, sea literalmente eso, nuestros problemas cambiarán (no dije que desaparecieran ;-)

Voy a ver si esta semana me hago un tiempo para retomar toda la documentación que tengo y armar un post prolijo sobre el tema.

Gracias por los alagos, es reconfortante sentir el feedback y saber que son útiles a otras personas.

Anónimo dijo...

Exelente articulo...

Yo he estado trabajando al respecto con PEAR desde hace poco. He trabajado con QuickForms + Smarty y ahora estoy en el tema de tuarticulo, la abstraccion de la base de datos, pero no crees que es mejor opcion MDB2.

He estado buscando una comunidad donde pueda hacer preguntas sobre mis dudas sobre PEAR y no encuentro, sera que tu tienes alguna bajo la manga?

Saludos::..

Enrique Place dijo...

Lo de "mejor o no", es dependiente siempre del contexto ;-)

No he tenido el gusto de probar todas las alternativas existentes en Pear, como el caso de MDB2, pero si te puedo hacer una sugerencia:

Crea un "capa de abstracción" que te "abstraiga" de las herramientas que ofrecen los servicios de "abstracción de bases de datos concretos" ;-)

Es decir, crea una clase BaseDeDatos (o como quieras que se llame) que use internamente, por ejemplo, MDB2. Deberás implementar por lo menos las operaciones básicas (conectar, desconectar, consultar, registros afectados, etc) ... pero no lo haces de cero, internamente lo haces con MDB2.

Si en un futuro, cercano o lejano, encuentras otra herramienta de abstracción que se adapta maś a tu contexto (rendimiento, flexibilidad, simplicidad, etc), podrás reemplazar MDB2 por la nueva, sin que tu aplicación se vea afectada por el cambio (ella dependerá de tu clase "BaseDeDatos", y no concretamente de una herramienta específica).

Una premisa en Diseño Orientado a Objetos es: "no dependas de implementaciones concretas, solo de implementaciones abstractas".

Veo muy seguido que los "novatos" (los que hacen sus primeras armas) lo primero que intentan es crearse componentes "de cero" (como la capa de abstracción) sin usar herramientas que los auxilien.

Reutiliza, no reinventes la rueda constantemente.

Pierde tiempo en las cosas que interesan, las que aportan "valor agregado" a tus desarrollos y a tus clientes.

PD: no te olvides que ahora nos mudamos a PHP Senior y que este blog no tendrás actualizaciones (si haces comentarios, tal vez nadie los lea).