Páginas

miércoles, 15 de diciembre de 2010

clase para conexion utilizando PDO, el patron factory, confireader y registry

buenas para este segundo post ahora si trabajaremos con PHP, utilizaremos el patrón de diseño factory y registry para realizar una conexión a base de datos.

Para realizar la conexión trabajaremos con PHP Data Objects (PDO), lo que nos dará una abstracción de base de datos permitiendo que nuestro script pueda conectar con diferentes manejadores, en nuestro caso usaremos MySQL, bueno ahora si manos a la obra:

Lo primero que creamos es  el archivo con la configuración:

config.ini

[MySql]
host     = 'localhost'
dbname   = 'db'
username = 'root'
password = ''

acá vamos a indicar los parámetros para instanciar el dns de PDO en este caso con MySQL
[CITA]
$db = new PDO ( 'mysql:host=localhost;dbname=<SOMEDB>' , '<USERNAME>' , '<PASSWORD>' );
[/CITA]
esta pequeña cita es del manual donde nos indica que dns para instanciar PDO, como vemos los todos los parámetros los tenemos dentro de nuestro archivo config.ini.

ahora hacemos uso de la clase configReader para leer el archivo config.ini y así obtener los parámetros que necesitamos para el dns de nuestra clase

configReader.php

<?php
class ConfigReader
{
    private $_config;
    public function __construct($archivo)
    {                  
     if (!file_exists($archivo)) {
            throw new Exception('No se encontro el archivo: '.$archivo);
        }   
        $this->_config = parse_ini_file($archivo, true);
    }
 
    public function getConfig()
    {
        return $this->_config;
    }
}
?>

ahora vamos a crear la clase que nos permitirá conectar con MySQL

mysql.php

<?php
final class MySQL extends PDO
{
    private $_username;
    private $_password;
    private $_host;
    private $_db;
 
    public function __construct(ConfigReader $config)
    {
        $config_data = $config->getConfig();
        $this->_username = $config_data['MySql']['username'];
        $this->_password = $config_data['MySql']['password'];
        $this->_host = $config_data['MySql']['host'];
        $this->_db = $config_data['MySql']['dbname'];
        $this->_dsn = 'mysql:host='.$this->_host.';dbname='.$this->_db;
        parent::__construct($this->_dsn, $this->_username, $this->_password);     
    }
}
?>

si nos damos cuenta esta clase no es extendible ya que tiene la palabra final, ademas exiende directamente de PDO, lo que esta clase hace es la instancia para trabajar con MySQL en PDO, vemos el DNS que colocabamos al principio, los parametros los extraemos de la isntancia de la clase confiReader que pedimos como parametro en el constructor de nuestra clase MySQL.

Ahora vamos a trabajar el patron factory que nos permite instanciar objetos de un subtipo en este caso el subtipo sera MySQL

primero creamos la interface

DbFactory_Interface.php

<?php
interface DbFactory_Interface
{
    public static function create($sIniFile);
}
?>

ahora la clase dbfactory que implementa la interface creada anteriormente

dbfactory.php
<?php
error_reporting ( E_ALL );
ini_set ( "display_errors" , 1 ); 
include("DbFactory_Interface.php");
include("registry.php");
include("ConfiReader.php");
include("db.php");
class DatabaseFactory implements DbFactory_Interface
{   
    public static function create($sIniFile)
    {        
        $config = new ConfigReader($sIniFile);        
        $config_data = $config->getConfig();
        $database_class = key($config_data);
        include($database_class.'.php');
        $db = new $database_class($config);
        Registry::add($sIniFile, $db);       
        return $db;
    }
}
?>
en este archivo incluimos todas nuestras clases que vamos a utilizar para que nuestra clase de conexion haga  el trabajo. Si se fijan dentro de esta clase es utiliza el patron registry para poder realizar diferentes instancias de la clase, ya que la clase si necesitamos conectar con varios manejadores de base de datos podemos hacerlo solo creando el archivo .ini dandole los parametros del DNS de PDO para el manejador con el que deseemos conectar y el archivo con la clase que extiende de PDO donde indicamos con que manejador conectar en nuestro caso MySQL.php, aca le dejo el patron registry

<?php
class registry
{
     static private $registry = array();     
     public function add($name, &$item)
     {
         if (!self::exists($name)) {
             self::$registry[$name] = $item;
             return true;
         } else {
             return false;
         }
     }      
      public function exists($name)
     {
         if (is_string($name)) {
             return array_key_exists($name, self::$registry);
         } else {
             throw new Exception('Registry item\'s name must be a string');
         }
     }
     public function &get($name)
     {
         if (self::exists($name)) {
             $return = self::$registry[$name];
         } else {
             $return = NULL;
         }
         return $return;
     }    
 }
 ?>
 pues ahora solo nos falta crear la clase que se va a encargar de ejecutar las sentencias en este caso yo realize una clase para la ejecucion de consultas preparadas de manera que se evite la inyeccion sql ya que es importante el tema de la seguridad.


<?php 
   #######################################################
   #          Clace para conexion con db                 #
   #                                                     #
   #   Creado por Carlos  Belisario                 #
   #                                                     #
   #                                                     #
   #   Fecha: 20/11/2010                                 #
   #                                                     #
   #######################################################
 class db
{
    /*establese la conexion*/
    protected $_conect;
    /*trabaja los query*/
    protected $_query;
    /*resultados de las consultas*/
    protected $_row;
    protected $_result;
    public function __construct()
    {
        $site_path = realpath(dirname(__FILE__)).DIRECTORY_SEPARATOR.'config.ini';
        $this->_conect=DatabaseFactory::create($site_path);
    }
    /*trae todos los datos de las tablas indicadas en el array tabla*/
    public function selectAll($table = array(),$condicion = array(),$relacion = array()){
        $result = array();
        try{           
            if(!array($table)){
                die("Las Tablas deben de estar en formato arreglo");
            }
            if(count($table)>1){               
                foreach($table as $id=>$valor){                   
                    if($id==0){
                        $tablas = $valor." INNER JOIN ".next($table)." ON(".$relacion[$id].")";                       
                    }
                    else{
                        if($id!=1){                       
                            $tablas.= " INNER JOIN ".$valor." ON(".$relacion[$id-1].")";
                        }
                    }
                }                           
            }           
            else{
                $tablas = $table[0];
            }
            if(empty($condicion))
            {
                $sql = "SELECT * FROM $tablas";               
                $this->_query = $this->_conect->prepare($sql);
                $this->_query->execute();
            }
            else{               
                foreach($condicion as $key=>$value){
                    $parametros[] = "$key=?";
                    $condic[] = $value;
                }           
                $condiciones = implode(" AND ",$parametros);
                $sql = "SELECT * FROM $tablas WHERE $condiciones";               
               $this->_query = $this->_conect->prepare($sql);               
                $this->_query->execute($condic);
            }   
            while($this->_row = $this->_query->fetch()){   
                $result[] = $this->_row;           
            }           
            return $result;
        }
        catch(PDOExeption $e){
            echo $e->getMessaage();
        }
    }   
    /*ejecuta una sentencia de MySQL Preparada usando el formato de PDO*/
    public function ejecutarSentencia($query,$parametro = NULL,$return = false)
    {
        $result=array();
        try{                   
            $this->_query = $this->_conect->prepare($query);
            $this->_query->execute($parametro);   
            if($return==true){                                   
                while($this->_row = $this->_query->fetch()){   
                    $result[] = $this->_row;
                }               
                return $result;
            }           
        }
        catch(PDOExeption $e){
            echo $e->getMessaage();
        }
    }       
    /*inserta registros en la base de datos*/     
    public function insert($table,$valores)
    {
        if(!is_array($valores)){
            echo "Los datos deben de estar en formato de arreglo";
        }
        try{               
            foreach($valores as $id=>$valor){                               
                $parametros[] = '?';                               
                $campos[] = $id;   
                $value[] = $valor;
            }
            $val = implode(",",$parametros);
            if(!empty($campos)){
                $fields = implode(",",$campos);
                $sql = "INSERT INTO $table ($fields) VALUES ($val)";           
            }               
            $insert=$this->_conect->prepare($sql);           
            $insert->execute($value);
        }
        catch(PDOExeption $e){
            echo $e->getMessaage();
        }   
    }   
    /*Actualiza registros de la base de datos*/
    public function update($table,$valores,$condicion)
    {
        if(!is_array($valores)){
            echo "Los datos deben de estar en formato de arreglo";
        }
        try{           
            foreach($valores as $id=>$valor){
                $parametro[]="$id=?";
                $values[]=$valor;
            }
            foreach($condicion as $key=>$value){
                $de=explode(".",$value);
                if(count($de)>1){
                    $parametro_condicion[]="$key=$value";                   
                }
                else{
                    $parametro_condicion[]="$key=?";
                    $values[]=$value;                   
                }
            }               
            $parm=implode(",",$parametro);           
            if(count($condiciones)>0){
                $condiciones=implode(" AND ",$parametro_condicion);
                $sql="UPDATE $table SET $parm WHERE $condiciones"
            }       
            else{
                $sql="UPDATE $table SET $parm"
            }                                                   
            $update=$this->_conect->prepare($sql);           
            $update->execute($values);
        }
        catch(PDOExeption $e){
            echo $e->getMessaage();
        }   
    }
    /*borra registros de la base de datos*/
    public function deleteAll($table,$condicion = NULL,$return = false)
    {
        try{   
            foreach($condicion as $key=>$value){
                $de=explode(".",$value);
                if(count($de)>1){
                    $valorCondicion[] = "$key=".$value;
                }
                else{
                    $valorCondicion[] = "$key='".$value."'";
                }
            }               
            $condiciones = implode(" AND ",$valorCondicion);               
           $this->_result = $this->_conect->exec("DELETE FROM $table WHERE $condiciones");           
            if($return){
                return $this->_result;
            }               
        }
        catch(PDOExeption $e){
            echo $e->getMessaage();
        }
    }
    #reinicia la base de datos 
    public function reiniciarTabla($table){
        try{           
            $this->_result = $this->_conect->exec("TRUNCATE TABLE $table");               
        }
        catch(PDOExeption $e){
            echo $e->getMessaage();
        }
    }
}

si se fijan en el constructor de nuestra esta clase es donde comienza la magia (:-) e instanciamos la clase dbfactory que es la que se va a encargar de que se llamen a las clases que necesitamos, luego tenemos una serie de metodos para la ejecucion de consultas sql como usar:


<?php
#incluimos el archivo donde estan todas las clases
include("dbfactory.php");
#instanciamos la clase db() que es la que se encarga de trabajar el estandar e instanciar la clase dbfactory
$db = new db();
$rows = $db->selectAll("tabla",array('campo'=>'condicion'));
?>

 si se fijan utilice el metodo select all  si hicieramos un print_r($rows) si la consulta trajo resultados se mostrara

Array ( [0] => Array ( [correlativo] => SAL-0 [0] => SAL-0) [1] => Array ( [correlativo] => SAL-1 [0] => SAL-1)) 

como se puede notar todas las consultas son  consultas preparadas, menos las de borrar y reiniciar la tabla, estan bastante facil de entender su funcionamiento y estan comentadas espero que este articulo le sirva, saludos

6 comentarios:

  1. Hola carlos_belisario
    pues buen aporte el que haces, imagino
    disfrutaste bastante haciendo el asunto,
    y bueno, espero también que le sirva a
    otros
    saludos
    att: morti

    ResponderEliminar
  2. Gracias por tu comentario Morti, y por supuesto que lo disfrute ya que estoy haciendo lo que me gusta, ademas de ayudando a otros y a la vez aprendiendo en el intento, saludos y estamos en contacto

    ResponderEliminar
  3. Y muy agradecidos por el ejemplo, hace ratos me venia quebrando la cabeza con este asunto de pdo

    Mil gracias

    ResponderEliminar
  4. me alegra que le haya gustado los ejemplos, saludos

    ResponderEliminar
  5. Hola Carlos Belisario. Es un gusto enorme. Me ha ayudado mucho su post. Mi nombre es Carlos Javier. Soy músico y vendedor. Vivo en Córdoba capital, Argentina. Hace pocos años comencé explorando este maravilloso mundo del diseño web y la programación. Tengo conocimientos básicos de jquery, css, html y poco de php. Le comento que tengo un proyecto que avancé muchísimo pero aún creo que falta poquito para llegar al éxito y le pido ayuda por favor, de hace varias semanas trato de aprender más qué sucede con esta conexión por PDO. ¿Tendrá algún email por favor? que pueda contactarme con Ud. y enviarle 2 carpetas adjuntas: 1 con el proyecto que funciona excelente con arrays. Y otra es la que necesito reemplazar esos arrays para que los tome de la BD; allí le enviaré todos los progresos alcanzados.


    Mi email es: pruebajquery@gmail.com

    Desde ya muchas gracias.

    ResponderEliminar