Foros del Web » Programando para Internet » PHP » Frameworks y PHP orientado a objetos »

ORM casero creado desde cero

Estas en el tema de ORM casero creado desde cero en el foro de Frameworks y PHP orientado a objetos en Foros del Web. Hola. He estado trabajando en un ORM casero para conectar Php con MySql. Sus funciones son muy básicas (Select, Update, Insert, Delete, transacciones, etc.) pero ...
  #1 (permalink)  
Antiguo 02/01/2009, 06:09
 
Fecha de Ingreso: abril-2007
Mensajes: 114
Antigüedad: 17 años
Puntos: 2
ORM casero creado desde cero

Hola.

He estado trabajando en un ORM casero para conectar Php con MySql. Sus funciones son muy básicas (Select, Update, Insert, Delete, transacciones, etc.) pero puede ser interesante para alguien que como yo hasta hace poco, mezclaba código PHP con consultas Sql.

La explicación del ORM se encuentra en el blog de desarrollo del juego de Balonmano Online que estoy construyendo ( http://blog.bmtotal.net ) y la entrada concreta (parte de ella) la transcribo a continuación, por supuesto aquellos que puedan aportar alguna mejora o sugerencia que lo hagan, se lo agradezco, y a los que les parezca interesante pueden utilizar este código libremente.

-----------------------------------------------------------------------------------------

Para la elaboración de este ORM he creado las siguientes clases:
elemento: Clase de la que heredarán todas las que necesiten interactuar con la base de datos
Conexion: Clase encargada de establecer la conexión con Mysql (enviar, consultas, abrir conexion, etc.)
CondicionWhere: Clase que sirve para definir las condiciones de una consulta de selección, edición o borrado, es decir, definir los valores del "WHERE" de mysql.
CampoValor: Clase muy simple con dos propiedades; campo y valor. Sirve para pasar a las consultas de edición e inserción con la asignación de valores por campo.

Vamos a explicar las clases de una en una, para que quede mas claro, con ejemplos de código PHP.

Conexión
Esta clase se encarga de abrir la conexión con la base de datos y de enviar las consultas que le pasemos. La clase abre la conexión en el constructor y lo hace hacia la base de datos por defecto. Si queremos conectarnos con otra base de datos simplemente debemos pasarle los parametros de "servidor", "base_de_datos" y "password". El código php de la clase "Conexion" queda así:

Código php:
Ver original
  1. class Conexion {
  2.  
  3. //++++++++++++++++++
  4. //Funciones de conexion
  5. //++++++++++++++++++
  6.  
  7. function Conexion($servidor , $base_de_datos, $password) {
  8.  
  9. // Comprobamos si se han pasado parámetros, y si no se ha hecho, se definen los predeterminados
  10.  
  11. if ($servidor==null && $base_de_datos==null && $password==null){
  12.     $servidor="Servidor";
  13.     $base_de_datos="Nombre_Base_De_Datos";
  14.     $password="Contraseña";
  15. }
  16.  
  17. // Se intenta conectar a la base de datos, y si no se puede captura la excepcion y muestra el mensaje de error.
  18. try {
  19.     mysql_connect($servidor , $base_de_datos, $password);
  20. }
  21. catch (Exception $er){
  22.     print("ERROR: " + $er);
  23. }
  24. }
  25.  
  26. // La siguiente función es la que envía las consultas a la base de datos. Ocurre lo mismo que en la anterior:
  27. // Si se le pasa la base de datos como parametro lo envia a esa, si no, a la prederminada
  28.  
  29. public function Consulta($sql, $base_de_datos){
  30.  
  31. if ($base_de_datos==null){
  32.     $base_de_datos="Nombre_Base_De_Datos";
  33. }
  34.  
  35. if (! $res=mysql_db_query($base_de_datos, $sql)){
  36.     echo mysql_error();
  37.     exit;
  38. }
  39. return $res;
  40. }
  41.  
  42. //A continuacion se muestran las funciones de transacciones, que lo que hacen es enviar consultas simples para iniciar, terminar e interrumpir transacciones.
  43.  
  44. public function iniciaTransaccion($base_de_datos){
  45.  
  46. if ($base_de_datos==null){
  47.     $base_de_datos="Nombre_Base_De_Datos";
  48. }
  49. mysql_db_query($base_de_datos, "BEGIN;");
  50. }
  51.  
  52. public function finalizaTransaccion($base_de_datos){
  53. if ($base_de_datos==null){
  54.     $base_de_datos="Nombre_Base_De_Datos";
  55. }
  56. mysql_db_query($base_de_datos, "COMMIT;");
  57. }
  58.  
  59. public function interrumpeTransaccion($base_de_datos){
  60. if ($base_de_datos==null){
  61.     $base_de_datos="Nombre_Base_De_Datos";
  62. }
  63. mysql_db_query($base_de_datos, "ROLLBACK;");
  64. }
  65.  
  66. }

Y así queda la clase Conexion.

Última edición por paloto; 02/01/2009 a las 06:18
  #2 (permalink)  
Antiguo 02/01/2009, 06:09
 
Fecha de Ingreso: abril-2007
Mensajes: 114
Antigüedad: 17 años
Puntos: 2
Respuesta: ORM casero creado desde cero

A continuación, por orden de importancia, habría que explicar la clase "elemento", pero voy a hacerlo antes con "CampoValor" y "CondicionWhere", pues son más sencillas y se utilizan en la clase "elemento" por lo que será más fácil entenderla una vez explicadas estas.

CampoValor
Clase muy sencilla que define un tipo de objeto con dos propiedades: campo y valor.


Código php:
Ver original
  1. class CampoValor {
  2.      public $campo;
  3.      public $valor;
  4.  
  5.      function CampoValor($campo, $valor){
  6.           $this->campo=$campo;
  7.           $this->valor=$valor;
  8.      }
  9. }



CondicionWhere
Esta es una clase también bastante sencilla con dos parámetros llamados (en un exceso de creatividad) "parametro1", "parametro2" y un tercero llamado "operador". Esta función se encarga de definir las condiciones "WHERE" para las consultas. En un principio los parámetros eran tan simples como "campo", "valor" y "operador" de forma que simplemente se concatenaban "campo+operador+valor" dando por ejemplo, "id=35", sin embargo, me propuse prepararlo para consultas más complejas, y por este motivo abstraje más los parámetros de forma que se pudiera meter condiciones dentro de condiciones con diferentes niveles de prioridad, para crear por ejemplo, clausulas WHERE de este estilo: WHERE ((nombre LIKE 'Can%') AND ((nombre_cortisimo = 'CAN') OR (nombre_cortisimo = 'CNT'))).

Lo cierto es que no sé si me va a compensar pues me supuso más de un quebradero de cabeza pero finalmente funciona y de esta forma me cubro las espaldas.

La clase en sí es sencilla. Lo complicado es su utilización por parte de la clase "elemento", pero debemos tener claro que los parámetros que le pasamos al constructor de esta clase pueden ser: ("nombre", "Can%", "LIKE") es decir, 3 cadenas, o puede ser del tipo: ($Condicion1, $Condicion2, "OR"), es decir, dos objetos CondicionWhere y un operador que los una.


Código php:
Ver original
  1. class CondicionWhere {
  2.  
  3. public $parametro1;
  4. public $parametro2;
  5. public $operador;
  6.  
  7. function CondicionWhere($parametro1, $parametro2, $operador){
  8.  
  9. $this->parametro1=$parametro1;
  10. $this->parametro2=$parametro2;
  11.  
  12. if ($operador==""){ // Si no se le pasa operador, pone "=" por defecto
  13.     $this->operador="=";
  14. }
  15. else{
  16.     $this->operador=$operador;
  17. }
  18. }
  19. }



Bien, una vez entendida esta clase, estamos en disposición de meternos en el gran meollo, la clase "elemento".

Última edición por paloto; 02/01/2009 a las 06:19
  #3 (permalink)  
Antiguo 02/01/2009, 06:10
 
Fecha de Ingreso: abril-2007
Mensajes: 114
Antigüedad: 17 años
Puntos: 2
Respuesta: ORM casero creado desde cero

elemento
Esta clase contiene las funciones principales de comunicación con la base de datos: Select, Update, Insert y Delete, además de dos funciones extras de uso interno como son SacarCampos y Recursiva (esta última no tiene un nombre muy adecuado, pero bueno, no sabía como llamarla) .

Esta clase no tiene constructor así que iré explicando método a método, pero debemos tener en cuenta que todo ello se encuentra entre las etiquetas:



Código php:
Ver original
  1. class elemento{
  2.     public $tabla;
  3.     ...
  4. }



Empezaremos por una sencilla, SacarCampos($tabla), que se encarga de obtener un array con el tipo de dato de cada campo de la tabla que pasamos como parametro.


Código php:
Ver original
  1. function SacarCampos($tabla){
  2.  
  3. $consulta="SELECT COLUMN_NAME, DATA_TYPE FROM INFORMATION_SCHEMA.COLUMNS WHERE table_name = '$tabla'";
  4. $cn=new Conexion();
  5. $res=$cn->Consulta($consulta);
  6. while($row=mysql_fetch_array($res)){
  7.     $resultado[$row['COLUMN_NAME']]=$row['DATA_TYPE'];
  8. }
  9. return $resultado;
  10. }



Del resto de métodos de la clase elemento me voy a centrar en el Update, pues utiliza la clase "CampoValor" y la clase "CondicionWhere" y a partir de él se pueden deducir el resto de métodos, pues estos serán inevitablemente, más sencillos.



Código php:
Ver original
  1. function Update($camposvalores, $condicion){ // El parametro $camposvalores es un array de CampoValor
  2.  
  3. $tabla=$this->tabla;
  4. if ($camposvalores!="" && $tabla!="") //Comprobamos que los datos sean correctos
  5. {
  6.    $bdd_campos=$this->SacarCampos($tabla); // Obtenemos el array con el tipo de dato de los campos
  7.    $consulta = "UPDATE $tabla SET "; // $consulta será la cadena que finalmente lanzaremos contra la base de datos
  8.    foreach($camposvalores as $c){ // Recorremos el array de $camposvalores
  9.    $consulta.=$c->campo."="; // Añadimos a la cadena de la consulta el campo y el operador de asignacion
  10.    switch($bdd_campos[$c->campo]) // Comprobamos el tipo de dato para dcidir si ponemos comillas ( ' ) o no
  11.    {
  12.       case "char":
  13.       case "varchar":
  14.       case "date":
  15.       case "tinytext":
  16.       case "tinyblob":
  17.       case "text":
  18.       case "blob":
  19.       case "mediumtext":
  20.       case "mediumblob":
  21.       case "longtext":
  22.       case "longtext":
  23.       case "datetime":
  24.       $consulta.="'".$c->valor."', ";
  25.       break;
  26.       default:
  27.       $consulta.=$c->valor.", ";
  28.       break;
  29.    }
  30. }
  31.  
  32. if ($condicion !=null){ // Si le pasamos condiciones...
  33.    $consulta=substr($consulta, 0, strlen($consulta)-2)." WHERE ";
  34.    $cadena=$this->Recursiva($condicion); //Llamamos a la función "Recursiva" y obtenemos el valor de retorno
  35.    $consulta.=$cadena; // Lo añadimos a la cadena de consulta y Violá!
  36. }
  37. $cn=new Conexion();
  38. return $cn->Consulta($consulta);
  39. }



Vale, una vez hemos explicado estos métodos, solo nos queda hacerlo con el método "Recursiva", que aunque no es muy extenso, a mi me parece el más complicade de implementar y de explicar, y es precisamente como su nombre indica, por tratarse de una función recursiva. No sé si es que son de lo más complicado de asumir/entender de la programación o simplemente a mi se me atraviesan especialmente, pero lo cierto es que tuve verdaderos problemas para crearla.


Código php:
Ver original
  1. function Recursiva($condicion){ // el parametro $condicion puede ser un objeto CondicionWhere o una cadena y lo comprobamos en el siguiente if
  2.  
  3. if(strlen($condicion->parametro1)>0){ // Si se trata de un objeto CondicionWhere...
  4. $cadena.="("; // Añadimos el paréntesis de apertura para establecer la prioridad
  5. $cadena.=$this->Recursiva($condicion->parametro1); //La función se llama a si misma para profundizar en el "parametro" del CondicionWhere.
  6. // Para tenerlo mas o menos claro, construimos mentalmente $cadena.
  7. // Hasta este momento $cadena vale esto: "(", si resulta que 4l "parametro1" de este CondicionWHERE
  8. // resulta ser una cadena, irá por el else y $cadena pasará a valer "(NombreDelCampo".
  9. // A continuación dejará un espacio y hará lo mismo con "operador", que irá por el else.
  10. // igual con el "parametro2" de forma que al final la $cadena nos queda así (imaginemos que el operador es "=" ): "(NombreDelCampo = Valor)".
  11. // Si se tratara de condiciones mas complejas, con unas dentro de otras, lo único que cambia es que se repetirá este proceso las veces necesarias construyendo finalmente la condición final.
  12.  
  13. $cadena.=" ";
  14. $cadena.=$this->Recursiva($condicion->operador);
  15. $cadena.=" ";
  16. //Comprobar el campo
  17. // Aquí comprobamos si hay que ponerle comillas ( ' ) al "parametro2".
  18. $bdd_campos=$this->SacarCampos($this->tabla);
  19.  
  20. switch($bdd_campos[$condicion->parametro1])
  21. {
  22. case "char":
  23. case "varchar":
  24. case "date":
  25. case "tinytext":
  26. case "tinyblob":
  27. case "text":
  28. case "blob":
  29. case "mediumtext":
  30. case "mediumblob":
  31. case "longtext":
  32. case "longtext":
  33. case "datetime":
  34.    $marca="'";
  35.    break;
  36. default:
  37.    $marca="";
  38.    break;
  39. }
  40.  
  41. $cadena.=$marca; // Si necesita comillas se las ponemos
  42. $cadena.=$this->Recursiva($condicion->parametro2);
  43. $cadena.=$marca; // Si necesita comillas se las ponemos
  44. $cadena.=")";
  45. }
  46. else{
  47.    $cadena.="$condicion";
  48. }
  49. return $cadena; // Devuelve la cadena para seguir construyéndola o para devolverla al método que llamó a este.
  50. }



Llegado a este punto, para todo quede más claro, solo me falta mostrar la forma de usar estas clases y métodos.

Creamos una clase sencilla llamada "Lugar", creamos un objeto "Lugar", creamos el objeto CondicionWhere con varios CondicionWhere anidados y el array de CampoValor de asignación y llamamos a la función Update.



Código php:
Ver original
  1. class Lugar extends elemento
  2. {
  3.    public $id;
  4.    public $nombre;
  5.    public $nombre_corto;
  6.    function Lugar(){
  7.       $this->tabla="Lugar";
  8.    }
  9. }
  10. $lug=new Lugar();
  11. $con1=new CondicionWhere("nombre", "Can%", "LIKE");
  12. $con2=new CondicionWhere("nombre_corto", "CAN");
  13. $con3=new CondicionWhere("nombre_corto", "CNT");
  14. $con4=new CondicionWhere($con2, $con3, "OR");
  15. $con5=new CondicionWhere($con1, $con4, "AND");
  16.  
  17. $campos=array(new CampoValor("nombre", "ValorNombre"),
  18.  
  19. new CampoValor("nombre_corto", "ValorNombreCorto"));
  20.  
  21. $lug->Update($campos, $con5);



El resultado es que el ORM envía a la base de datos MySql la consulta siguiente:

Cita:
UPDATE Lugar SET nombre_corto='ValorNombreCorto' WHERE ((nombre LIKE 'Can%') AND ((nombre_corto = 'CAN') OR (nombre_corto = 'CNT')))
No sé si he logrado explicarme correctamente, pero he de decir que yo mismo me pierdo al intentar seguir el camino de la aplicación cuando llega a esta función recursiva. El caso es que funciona correctamente después de muchas pruebas y modificaciones.

Si a alguien le parece interesante y quiere utilizar este código puede hacerlo y si desea tener el código completo (con los métodos Select, Insert y Delete) solo tiene que mandarme un correo electrónico o pedirlo en los comentarios y se lo enviaré con mucho gusto, igualmente si alguien tiene alguna duda o sugerencia sobre esto, puede ponerla también en los comentarios.

Un saludo

Última edición por paloto; 02/01/2009 a las 06:20
  #4 (permalink)  
Antiguo 03/01/2009, 12:50
Avatar de GatorV
$this->role('moderador');
 
Fecha de Ingreso: mayo-2006
Ubicación: /home/ams/
Mensajes: 38.567
Antigüedad: 17 años, 11 meses
Puntos: 2135
Respuesta: ORM casero creado desde cero

Tema trasladado a PHP Orientado a Objetos.
Atención: Estás leyendo un tema que no tiene actividad desde hace más de 6 MESES, te recomendamos abrir un Nuevo tema en lugar de responder al actual.
Respuesta




La zona horaria es GMT -6. Ahora son las 05:47.