Identifícate...

...o regístrate

codeando.net

Abstracción de bases de datos (I)
mié, 26 nov 2008 - Por Jose y archivado en PHP, Tutoriales y Bases de datos

Hola de nuevo, hoy vamos a comenzar una clase de artículos en los que crearemos una librería de abstracción de bases de datos ¿que qué es eso? pues vamos a verlo.

De entrada, una capa de abstracción de bases de datos, nos permitirá cambiar el motor de la base de datos, de forma fácil y sencilla, de forma que la aplicación continúe funcionando de forma normal y sin efectuar ningún cambio en ella. Esto significa, que también podremos desarrollar aplicaciones que trabajen contra cualquier motor de bases de datos, sin cambiar nuestra sintaxis ni las funciones a utilizar.

Además, podemos implementar métodos para realizar selects, inserts, updates y deletes, entre otros, con lo que nuestro código podría quedar limpio de sentencias SQL (personalmente, es una práctica que prefiero no usar; a mí me es muy útil leer mis sentencias SQL de forma literal, pero para gustos... colores)

Bueno, pongámonos manos a la obra, lo primero que debemos hacer es crear la clase y establecer una conexión con la base de datos:

class MyDBLayer {
 
	private $db = '';				// Base de datos.
	private $host = 'localhost';	// Servidor.
	private $user = 'user';			// Usuario.
	private $password = 'password';	// Password.
 
	public $handler = null;			// Handler de la conexión.
 
	private $engine = 'mysql';		// Motor de base de datos.
	private $autoconnect = false;	// Indica si debemos autoconectar al crear el objeto.
	private $connected = false;		// Indica si estamos conectados.
	private $queries = array();		// Contiene los queries ejecutados.
 
	public function __construct( $db, $host, $user = '', $password = '', $connect = false, $engine = 'mysql' ) {
 
		//
		// Inicializamos variables
		//
		$this->db = $db;
		$this->host = $host;
		if (strpos($host,'@') !== false) {
			$tmp = explode( '@', $host );
			$this->host = $tmp[0];
			$this->user = $tmp[1];
			if (count($tmp) > 2) {
				$this->password = $tmp[2];
			}
			if (count($tmp) > 3) {
				$this->autoconnect = (in_array($tmp[4],array('yes','true','1') ? true : false;
			}
		} else if (is_array($host)) {
			$this->host = $host[0];
			$this->user = $host[1];
			$this->password = $host[2];
			if (count($host) > 3) {
				$this->autoconnect = (in_array($host[4],array('yes','true','1') ? true : false;
			}
		} else {
			$this->user = $user;
			$this->password = $password;
			$this->autoconnect = $connect;
		}
 
		if ($engine != 'mysql') $this->engine = $engine;
 
		if ($this->autoconnect) {	// Si debemos conectar al inicio lo hacemos...
			$this->connect();
		}
 
	}
 
	public function connect() {
 
		if ($this->engine == 'mysql') {
			$this->handler = @mysql_connect( $this->host, $this->user, $this->password );
		}
 
		if ($this->handler) {
			if (!mysql_select_db($this->db, $this->handler)) {
				$this->errors[] = 'Error de selección de base de datos: ' . mysql_error();
				return false;
			}
			$this->connected = true;
			return true;
		} else {
			$this->errors[] = 'Error de conexión con el servidor: ' . mysql_error();
			return false;
		}
 
	}
  

Bien, como vereis, el constructor lo hemos montado de forma que sea capaz de recibir los parámetros de distintas formas: le podemos pasar los parámetros de forma tradicional o podemos agrupar los parámetros de conexión en el parámetro $host, ya sea como una cadena con el caracter "@" como separador (ej: host@usuario@password[@autoconectar[@motor]]) o como un array numérico, mantiendo el mismo orden de parámetros (host, usuario, password, autoconectar y motor) y una vez ha establecido las propiedades base de la clase, es capaz de realizar la conexión llamando a la función "connect", si se lo especificamos, claro.

En la función "connect", realizamos la conexión al servidor y seleccionamos la base de datos, comprobando el motor seleccionado, lo que nos da la posibilidad de extender nuestra clase para que sea capaz de trabajar sobre cualquier motor, ya sea mysql, postgre, mssql, odbc, etc...

Y para acabar con la primera entrega de esta serie, vamos a darle un poco de funcionalidad a la clase, implementando los métodos necesarios para extraer datos:

public function query( $sql, $query = 'main' ) {
 
	if (!$this->connected) {		// Comprobamos si estamos conectados, si no lo hacemos.
		$this->connect();
	}
 
	if ($this->connected) {
 
		if ($this->engine == 'mysql') {
 
			//
			// Realizamos el query y almacenamos el resultado en el array "queries"
			//
			$this->queries[$query]['query'] = $sql;
			$this->queries[$query]['result'] = mysql_query( $this->queries[$query]['query'], $this->db ) || $this->SendError();
 
			if ($this->queries[$query]['result']) {
 
				//
				// Si el query ha tenido éxito, extraemos información del resultado.
				//
				$this->queries[$query]['recno'] = 0;
 
				if ((substr( strtoupper(trim($this->queries[$query]['query'])), 0, 6 ) == "SELECT") || (substr( strtoupper(trim($this->queries[$query]['query'])), 0, 7 ) == "EXPLAIN") || (substr( strtoupper(trim($this->queries[$query]['query'])), 0, 4 ) == "SHOW")) {
 
					$this->queries[$index]['numfields'] = mysql_num_fields( $this->queries[$index]['result'] );
					$this->queries[$index]['numrows']   = mysql_num_rows( $this->queries[$index]['result'] );
 
					for ($ct=0;$ct<$this->queries[$index]['numfields'];$ct++) {
						$this->queries[$index]['fields'][$ct]['name'] = mysql_field_name( $this->result, $ct );
						$this->queries[$index]['fields'][$ct]['type'] = mysql_field_type( $this->result, $ct );
						$this->queries[$index]['fields'][$ct]['size'] = mysql_field_len( $this->result, $ct );
					}
 
				}
 
			}
 
			return $this->queries[$index]['result'];
 
		}
 
	}
 
}
 
public function get( $row, $col, $query = 'main' ) {
 
	$return = false;
 
	if ($this->engine == "mysql") {
		$return = mysql_result( $this->queries[$query]['result'], $row, $col );
	}
 
	return $return;
 
}

Como se puede observar, el método "query" realiza una consulta sobre la base de datos y la almacena en un array con un índice definido por nosotros, o usando uno por defecto. Además, si la consulta debe devolver resultados (es decir, no es un UPDATE ni un INSERT) extrae y almacena información sobre el resultado, como número de registros o número de campos, así como el nombre, tipo y tamaño de los mismos.

El método "get", símplemente extrae y devuelve un valor del resultado de cualquiera de las consultas realizadas o false si hay algún error.

Y aquí terminamos la entrega de hoy, en las siguientes iremos implementando nuevos métodos de acceso a datos y agregaremos nuevos motores de bases de datos a nuestra clase.

Déjanos tu comentario:

Intenta comentar sobre el tema del que trata el artículo y evita descalificaciones o palabras malsonantes.
 
 

Comentarios:

  1. Escrito por grimborg el mar, 09 dic 2008

    if (!$this->connected) { $this->connect(); }

    Es posible que las conexiones se cierren al cabo de un rato (por ejemplo, por abrirla y no usarla) Si conexión se perdió, el código debería intentar volver a conectar.

  2. Escrito por Jose el mar, 09 dic 2008

    No es una mala idea, pero en realidad las conexiones no deberían cerrarse (y me consta que en condiciones normales no suele pasar) a no ser que termine la ejecución del script o se llame específicamente a mysql_close().

    No obstante, seguro que podría ser útil en una buena gestión de errores ;)

  3. Escrito por Brigitte el jue, 05 mar 2009

    Hola,

    Quiero saber como puedo extraer datos de Oracle y As400, relacionarlos, para luego almacenar dicha información en otra tabla de Oracle.

    Gracias!!!

  4. Escrito por Brigitte el jue, 05 mar 2009

    Hola,

    Quiero saber como puedo extraer datos de Oracle y As400, relacionarlos, para luego almacenar dicha información en otra tabla de Oracle.

    Gracias!!!

  5. Escrito por Oliver Mezquita el lun, 16 nov 2009

    Interesante... pero quizás también sería útil utilizar alguna librería para implementar una capa de persistencia... como Hibernate/NHibernate, por ejemplo.