PHP - Cómo utilizar la clase CBaseDatos de tipo singleton
PHP Ejemplo detallado de la utilización de la clase CBaseDatos
Primeramente saludar y pedir disculpas a Marco y Pere que comentarón en la entrada: Php Patron singleton y la clase para conectar con base de datos CBaseDatos , por no haber creado el ejemplo antes. Cuestion de tiempo. Espero que aun les sirva el post. Partamos sobre la base que estamos trabajando en la arquitectura 3 capas, Modelo Vista Controlador. Antes del ejemplo un apunte. "CBaseDatos" se llama así porque la "C" indica que es un componente, Que es una clase parecida a un "helper" que se puede instanciar en tu Modelo, Controlador y/o Helper. "BaseDatos" porque gestiona las conexiones.El ejemplo:
Asumamos que tenemos una tabla con usuarios con los campos id (como: primary key), nombre, email y clave. llamemosla tbl_usuario.1- La clase componente CBaseDatos:
<?php
/**
* @author Eduardo Acevedo Farje
* @web www.eduardoaf.com
* @Nombre del archivo: component_base_datos.php
*/
class CBaseDatos //Tipo Singleton
{
//almacena un objeto resourse que tiene un identificador de
//conexion "linkid" o 0 en caso de error
private $_oLinkId;
private $_sServidor;
private $_sNombreBD;
private $_sUsuario;
private $_sClave;
private $_sMensaje;
private static $_oSelf = null;
private function __construct()
{
/*
En el post anterior utilizo constantes, pero si lo deseas
puedes escribir directamente los valores o incluso pasar
estos valores en el constructor. Es un poco segun tus necesidades.
Si se pasa los valores en constructor daria versatilidad
en conexiones con bd distintas en un mismo proyecto.
En alguna ocasion para migraciones lo he implementado por parametros.
__construct($sServidor,$sNombreBD..);
*/
$this->_sServidor = "192.168.1.231";
$this->_sNombreBD = "bd_tienda_virtual";
$this->_sUsuario = "imthemaster";
$this->_sClave = "ElSeRvIdOr231";
$this->_sMensaje = "";
}
/**
* Este es el pseudo constructor singleton
* Comprueba si la variable privada $_oSelf tienen un objeto
* de esta misma clase, sino lo tiene lo crea y lo guarda
*/
public static function get_instancia()
{
//Si no hay instancia de CBaseDatos
//en la variable estatica $_oSelf
if( !self::$_oSelf instanceof self )
{
//Se crea un objeto de CBaseDatos guardandolo
//en la varialbe estatica
//new self ejecuta __construct()
self::$_oSelf = new self;
}
//Se devuelve el objeto creado
return self::$_oSelf;
}
/**
* Crea un oRecurso de conexion y lo asigna a oLinkId.
* Si oLinkId es 0. Es porque el recurso no existe
* Realiza la conexion y devuelve el resultado
* @return true si hubo exito en la conexion
*/
public function conectar()
{
//Si no existe un recurso previo se intenta crear uno nuevo
if($this->_oLinkId==0)
{
//VERIFICAMOS LA CONEXION AL MOTOR DE BD
//oMysqlConnect es tipo resource si tiene exito y guarda un "link identifier"
//algo como 5893, vamos un entero.
//sino guarda un false (0)
$oMysqlConnect = mysql_connect
(
$this->_sServidor,
$this->_sUsuario,
$this->_sClave
);
//si no es tipo resource es q no ha tenido exito la conexion;
if(!is_resource($oMysqlConnect))
{
$this->_sMensaje = "ERROR: No se puede conectar a la base de datos..! ".$this->_sNombreBD;
//Lanza la excepcion y se sale del procedimiento
throw new Exception($this->_sMensaje);
die;
}
//Guardamos el id del recurso conectado, esta propiedad nos servirá
//cuando queramos ejecutar una SQL contra la BD deberemos utilizar
//la propiedad get_link_id(). Lo veremos más abajo en este post
$this->_oLinkId = $oMysqlConnect;
//VERIFICAMOS QUE EXISTA LA BASE DE DATOS EN EL MOTOR
$bExisteBD = mysql_select_db($this->_sNombreBD, $oMysqlConnect);
//si no se pudo encontrar esa BD lanza un error
if(!$bExisteBD)
{
$this->_sMensaje = "ERROR: No se puede usar la base de datos..! ".$this->_sNombreBD;
//Lanza la excepcion y se sale del procedimiento
throw new Exception($this->_sMensaje);
die;
}
else //Si se conecto
{
$this->_sMensaje = "SE CONECTO CON EXITO";
mysql_set_charset('utf8',$this->_oLinkId);
}
}//fin Ya existe recurso abierto por lo tanto se puede instanciar con get_link_id()
return true;
}
private function get_servidor()
{
return $this->_sServidor;
}
public function get_usuario()
{
return $this->_sUsuario;
}
private function get_clave()
{
return $this->_sClave;
}
public function get_mensaje()
{
return $this->_sMensaje;
}
public function get_nombre_bd()
{
return $this->_sNombreBD;
}
/**
* Devuelve un entero mayor a 0 si hay conexión.
* En caso contrario 0
*/
public function get_link_id()
{
return $this->_oLinkId;
}
}
?>
2- Nuestro Modelo "MUsuario"
<?php
/**
* @author Eduardo Acevedo Farje
* @web www.eduardoaf.com
* @Nombre del archivo: model_usuario.php
*/
class MUsuario
{
//Una instancia de
private $_oBaseDatos;
private $_sTabla;
private $_id; //Clave primaria, autonumerico
private $_nombre;
private $_email;
private $_clave;
public function __construct($id=0, $sNombre="", $sEmail="", $sClave="")
{
//Abrimos un flujo de conexion con nuestro componente
$this->_oBaseDatos = CBaseDatos::get_instancia();
$this->_oBaseDatos->conectar();
//var_dump($this->oBaseDatos);
$this->_sTabla = "tbl_usuario";
$this->_id = $id;
$this->_nombre = $sNombre;
$this->_email = strtolower($sEmail);
$this->_clave = $sClave;
}
/**
* Sobre los 3 metodos: query(), query_object() y execute()
* Lo ideal seria crear una clase madre con los atributos:
* oBaseDatos, id, sTabla y estos metodos de modo que todos los
* modelos hereden (extiendan) estas propiedades.
* La definicion de esta clase seria: class MUsuario extends ModeloMadre{..}.
* No lo he puesto
* aqui porque el post seria demasiado extenso.
*/
/**
* Se utiliza para SQLs tipo SELECT, es decir, de lectura y
* devuelve un array con el resultado
*/
private function query($sSQL)
{
$arTabla = array();
try
{
/**
* resource = mysql_query ( string $query [, resource $link_identifier ] )
* Aqui entra en juego nuestro Componente tipo singleton CBaseDatos
*/
$oResult = mysql_query($sSQL, $this->_oBaseDatos->get_link_id());
/**
* array = mysql_fetch_array ( resource $result [, int $result_type = MYSQL_BOTH ] )
*/
while($arFila = mysql_fetch_array($oResult, MYSQL_ASSOC))
{
$arTabla[$this->_sTabla][] = $arFila;
}
//Array que tiene como indices los nombres de los campos
//$arTabla['tbl_usuario'][iNumeroFila] = array('id'=>'valor','nombre'=>'valor'...);
return $arTabla;
}
catch (Exception $e)
{
die("Metodo query: Error al ejecutar la sentencia SQL = $sSQL");
}
}
/**
* Se utiliza para SQLs tipo SELECT, es decir, de lectura y
* devuelve un array con el resultado parecido al anterior
* pero este crea un array de pseudo objetos. de modo que puedas
* recuperar un item del array en forma de $arUsuario[$i]->nombre su equivalente
* en el metodo anterior seria $arUsuario[$i]['nombre'].
*/
private function query_object($sSQL)
{
$arTabla = array();
$oResult = mysql_query($sSQL, $this->_oBaseDatos->get_link_id());
if($oResult)
{
while($arFila = mysql_fetch_object($oResult))
{
$arTabla[] = $arFila;
}
}
else
{
die("Metodo query_object: Error al ejecutar la sentencia SQL = $sSQL");
}
//$arTabla[$i]->id, $arTabla[$i]->nombre, $arTabla[$i]->clave ...
return $arTabla;
}
/**
* Este metodo ejecuta una SQL de escritura.
* estas son INSERT, UPDATE y DELETE
*/
private function execute($sSQL)
{
try
{
//$this->oTfw->set_sql($sSQL);
$oResult = mysql_query($sSQL);
//@mysql_query("SET NAMES utf8");
if (!$oResult)
{
$sMensaje = "Sentencia SQL con errores: " . mysql_error() . "\n";
$sMensaje .= "SQL =" . $sSQL;
die($sMensaje);
}
}
catch(Exception $e)
{
}
}
/**
* Utilizado para cargar los datos de un registro de la
* tabla (un usuario) en los atributos privados.
* Recupera un usuario
*/
public function load_atributos()
{
$arUsuario = null;
$sSQL="
SELECT *
FROM $this->_sTabla
WHERE id='$this->_id'
";
$arUsuario = $this->query($sSQL);
$this->_id = $arUsuario[$this->_sTabla][0]['id'];
$this->_nombre = $arUsuario[$this->_sTabla][0]['nombre'];
$this->_email = $arUsuario[$this->_sTabla][0]['email'];
$this->_clave = $arUsuario[$this->_sTabla][0]['clave'];
}
public function agregar()
{
$nombre = $this->_nombre;
$email = $this->_email;
$clave = $this->_clave;
$sSQL="INSERT INTO $this->_sTabla
(nombre, email, clave)
VALUES('$nombre','$email','$clave')";
$this->execute($sSQL);
}
public function modificar()
{
$id_usuario = $this->_id;
$nombre = $this->_nombre;
$email = $this->_email;
$clave = $this->_clave;
$sSQL="UPDATE $this->_sTabla
SET
nombre = '$nombre',
email = '$email',
clave = '$clave'
WHERE id = $id_usuario
";
$this->execute($sSQL);
}
public function get_tabla()
{
$arTabla = array();
$sSQL = "
SELECT *
FROM $this->_sTabla
ORDER BY nombre ASC
";
$arTabla = $this->query_object($sSQL);
return $arTabla;
}
/**
* GETS Y SETS
* Siguiendo la norma de encapsulacion de atributos definimos estos metodos
*/
public function get_id()
{
return $this->_id;
}
public function get_nombre()
{
return $this->_nombre;
}
public function get_email()
{
return $this->_email;
}
public function set_nombre($sValor)
{
$this->_nombre = $sValor;
}
public function set_email($sValor)
{
$this->_email = strtolower($sValor);
}
public function set_clave($sValor)
{
$this->_clave = $sValor;
}
}
?>
3- El controlador "ControllerUsuarios"
<?php
/**
* @author Eduardo Acevedo Farje
* @web www.eduardoaf.com
* @Nombre del archivo: controller_usuarios.php
*/
class ControllerUsuarios
{
public function lista()
{
//Construimos nuestro objeto Usuario
$oUsuario = new MUsuario();
$arTablaUsuarios = $oUsuario->get_tabla();
/*
* Marco con respecto al ejemplo que pedias para la utilización
* de la "clase para paginar cualquier array" seria el siguiente:
* http://www.eduardoaf.com/blog-programacion/programacion-php/clase-para-paginar-un-array/
*/
//Comprobamos si nos viene algo en la variable "page". Sea cual sea el
//valor esto lo gestiona el componente CPaginas. Si es nula asume
//que la pagina pedida es la 1
$iPagina = $_GET['page'];
$sUrl = "protocol//www.tudominio.com/vistas/lista_usuarios.php?page=";
$oPagina = new CPaginas($arTablaUsuarios, $iPagina, $sUrl, 8);
//Reutilizamos el array dejando solo los items que le corresponde
//a la pagina $iPagina
$arTablaUsuarios = $oPagina->get_items_to_show();
$sBotones = $oPagina->get_botones_in_html();
$_SESSION['arTablaUsuarios'] = $arTablaUsuarios;
$_SESSION['sBotones'] = $sBotones;
}
public function agregar()
{
//No voy hacer todo el metodo pero si lo más importante
$sNombre = $_POST['txtNombre'];
$sEmail = $_POST['txtEmail'];
//aqui lo logico es aplicar alguna encriptacion para guardarlo
//se que ya lo estabas pensando pero sino llueven criticas xD
$sClave = $_POST['txtPassword'];
//Aqui habria que validar todos estos datos antes de pasarlo
//al modelo, crear un metodo is_datos_validos(){}
//Construimos nuestro objeto Usuario
$oUsuario = new MUsuario(null,$sNombre,$sEmail,$sClave);
$oUsuario->agregar();
$_SESSION['Mensaje'] = "Usuario Agregado";
}
public function ficha()
{
$id_usuario = $_GET['id_usuario'];
//Construimos nuestro objeto Usuario
$oUsuario = new MUsuario($id_usuario);
//Para esto sirve el metodo load atributos, obtiene
//el registro por su clave principal.
$oUsuario->load_atributos();
$_SESSION['oUsuario'] = $oUsuario;
}
//Los metodos eliminar() y modificar() son similares a los anteriores.
}
?>
3- La vista "ViewUsuariosLista"
<?php
/**
* @author Eduardo Acevedo Farje
* @web www.eduardoaf.com
* @Nombre del archivo: lista_usuarios.php
*/
//IMPORTAMOS TODOS NUESTROS INGREDIENTES :)
include("../componentes/component_paginas.php");
include("../componentes/component_base_datos.php");
include("../modelos/model_usuario.php");
include("../controladores/controller_usuarios.php");
session_start();
//Suponemos que la llamada a este archivo se hace con
//protocol//www.tudominio.com/vistas/lista_usuarios.php
$oContolUsuarios = new ControllerUsuarios();
//Esto nos ha guardado los datos en las variables de sesion
$oContolUsuarios->lista();
$arTablaUsuarios = $_SESSION['arTablaUsuarios'];
//Los links de los botones estaran formados con la url:
//http://www.tudominio.com/vistas/lista_usuarios.php?page=i
$sBotones = $_SESSION['sBotones'];
?>
<html>
<head></head>
<body>
<?php
//Como la tabla la he crado con el metodo query_object me devuelve un array
//de seudo objetos usuario
foreach($arTablaUsuarios as $oSeudo)
{
echo "Nombre: " .$oSeudo->nombre ."<br>";
echo "Email: " .$oSeudo->email ."<br>";
echo "Numero: " .$oSeudo->id ."<br>";
}
//Imprimo la paginacion
echo $sBotones;
?>
</body>
</html>
Al final en la vista he realizado un poco de lógica, es por simplicidad. De lo contrario habria que meterse con enrutadores (otra clase más) y .htaccess.
Comentar a todo esto que he creado un framework
"The Framework" que dentro de poco subiré para que lo puedan descargar.
La filosofia será parecida a la que he expuesto aqui, MVC pero sin pasar valores a la vista con $_SESSION sino que esto lo llevaria a cabo el framework.
Otra ventaja es que nos evitará hacer includes y la necesidad de crear una instancia del Controlador en la vista.
PD: UTILIZO protocol porque sino el syntaxhighlighter me crea urls 404. Ustedes cambienlo por http.
Autor: Eduardo A. F.
Publicado: 02-07-2011 09:26
Actualizado: 02-07-2011 15:54