miércoles, 20 de julio de 2011

Conectando Oracle con PHP (PDO)

 En esta ocasion veremos cómo hacer una conexión mediante PHP y Oracle, para esto nos basaremos en el objeto PDO mencionado anteriormente, hasta aquí se asume que ya está configurado el php.ini para usar el respectivo driver (Si no es así, puedes ver el artículo sobre la configuración del PDO aquí ).

Empecemos...

Voy a explicarles, línea por línea, posteriormente, mostraré el código completo.

La Conexión...
1. Para la conexión a Oracle, necesitaremos 3 datos indispensables:
  1. El listener o la cadena de host a conectarnos.
  2. La Contraseña.
  3. El Usuario
-Así pues,  para facilitar la visualización del código, declararemos 3 variables; la primera de ellas es la más larga, esta variable almacenará un TNSNAME, esto con el fin de que nuestra conexión sea exitosa; sin embargo, existe otra forma más corta, por el momento, en este ejemplo usaré la más robusta y eficaz de todas.

$tns="(DESCRIPTION=(ADDRESS_List=
                                   (ADDRESS=(PROTOCOL=TCP)(HOST=127.0.0.1)(PORT=1521))
                      )
                      (CONNECT_DATA=
                              (SERVICE_NAME=DB1)
                      )
         )" ;
*En este punto, los tres campos que podrías modificar son:
  • HOST: Este se modifica en caso de que la Base de Datos se encuentre en otro host, diferente servidor, de lo contrario puede quedarse así tal cual se muestra (O escribir "localhost"). 
  • PORT: El puerto por defecto es el 1521, a excepción de que lo hayas modificado. 
  • SERVICE_NAME: Este pertenece a la base de datos, en muchas bases, se maneja el nombre de "orcl" por ejemplo.
-Posteriormente declararemos nuestras variables de usuario y contraseña.

$user="pruebas" ; //El nombre de usuario
$pass="daniboy"; //La contraseña
2. Ahora, nos centraremos en la creación del objeto que nos permitirá conectar a la Base de Datos.  Este objeto lo almacenaremos en una variable:
                $conexion =new PDO("oci:dbname".$tns,$user,$pass);

La cadena "oci:dbname:" es usada por el driver para generar la conexión, además de los parámetros ya mencionados.
A pesar de que la línea anterior nos permite conectar a la Base de Datos, puede ser que esta no exista, para tal caso, colocar la línea dentro de un bloque try/catch nos permitirá saber si la conexión fue exitosa.
try
{
      $conexion =new PDO("oci:dbname".$tns,$user,$pass);
      echo "Accediendo...";
} 
catch ( PDOException $e )
echo "Error: ".$e->getMessage( );  }
3. Capturar una excepción de esta forma, sólo nos limita a que aparezca el mensaje de error cuando no se pueda conectar a la base de datos, pero ¿Qué sucede si una transacción no se termina correctamente debido a algúna anomalía de integridad o alguna otra causa? Simplemente la excepción no nos mostraría nada (Esto se debe a los valores por defecto con que el objeto PDO se crea). Para poder activar la excepción cuando se produzca un error, basta con  colocar el atributo PDO::ATTR_ERRMODE,PDO::ERRMODE_EXCEPTION en el método setAttribute del objeto PDO:
 $conexion->setAttribute(PDO::ATTR_ERRMODE,PDO::ERRMODE_EXCEPTION);
 Ahora nuestro bloque podrá capturar excepciones tanto a nivel de conexión, como a nivel de transacciones.

Las transacciones...
1. Realizar una operación como Insertar, Borrar, Modificar es una tarea fácil, para esto, el objeto cuenta con el método exec, el cual admite un parámetro que será nuestra  sentencia SQL.
  Para demostrar esto, insertaremos un registro en una tabla llamada prueba2.

          $inser="insert into prueba2 values('x',1,'z')"; //Cadena con la instrucción insert
          $conex->exec( $st );
//Ejecutamos la transacción
          
echo "La transacción fue realizada exitosamente";
            
Si la operación ha sido realizada exitosamente, se mostrará el mensaje "La transacción fue realizada exitosamente" de lo contrario, saltará al catch.

2. Una vez que hayamos insertado nuestro registro, nos dispondremos a ver si realmente se ha guardado además de ver las otras filas que existen en la tabla prueba2.

    Para esto, usaremos tres funciones: prepare, executefetchAll. La primer función(prepare) almacena la consulta que vayamos a ejecutar; la segunda función (execute) precisamente se encarga de eso, de ejecutar la sentencia; por último, la función fetchAll nos devolverá un arreglo, en el cual cada registro obtenido se almacena también como un arreglo, es decir se produce un arreglo que contiene 'x' arreglos de filas.

    Aunque toda esta definición parece ser muy difícil de llevarla a la práctica, en realidad no demuestra ser gran ciencia, para mostrar los datos, usaremos un ciclo foreach para recorrer todo el arreglo, también usaremos la herramient list ( ) para obtener los valores por cada fila (En este caso, la tabla sólo contiene tres campos).

$st=$conex->prepare("Select * from prueba2"); //La función devuelve un statement object

            $st->execute( );

            $resultado=$st->fetchAll( ); //El resultado lo guardamos en una variable diferente.

            foreach($resultado as $mat//Por cada arreglo guardalo en $mat
            {
              //Declaro mi list con tres variables, cada una tendrá el valor de cada columna por registro.
               list($c1,$c2,$c3)=$mat;    
                //Mandamos a imprimir en pantalla
                  echo "$c1---$c2--- $c3";
            }
Con esto el código quedaría de la siguiente manera:
< ?php
 
$user="pruebas" ; 
$pass="daniboy"; 
$tns="(DESCRIPTION=(ADDRESS_List=
                                   (ADDRESS=(PROTOCOL=TCP)(HOST=127.0.0.1)(PORT=1521))
                      )
                      (CONNECT_DATA=
                              (SERVICE_NAME=DB1)
                      )
         )" ;
try
{
      $conexion =new PDO("oci:dbname".$tns,$user,$pass);
      $conexion->setAttribute(PDO::ATTR_ERRMODE,PDO::ERRMODE_EXCEPTION);
       echo "Accediendo..."
      $inser="insert into prueba2 values('x',1,'z')"; //Cadena con la instrucción insert
     $conex->exec( $st ); //Ejecutamos la transacción
      echo "La transacción fue realizada exitosamente";          
$st=$conex->prepare("Select * from prueba2"); //La función devuelve un statement object
            $st->execute( )
            $resultado=$st->fetchAll( ); //El resultado lo guardamos en una variable diferente.
            foreach($resultado as $mat//Por cada arreglo guardalo en $mat
            {
       //Declaro mi list con tres variables, cada una tendrá el valor de cada columna por registro.
               list($c1,$c2,$c3)=$mat;  
                    echo "$c1---$c2--- $c3"; //Mandamos a imprimir en pantalla
            }//Fin Foreach
  }
catch ( PDOException $e )
echo "Error: ".$e->getMessage( );  }
? >

martes, 19 de julio de 2011

Objetos PDO y los controladores para Bases de Datos- PHP

El PDO (PHP DataObjects) es un objeto que nos brinda una interfaz en PHP para realizar conexiones a bases de datos (Oracle, MySQL, Firebird, Informix, PostgreSQL, etc) y realizar transacciones.

Ventaja de usar PDO
          La interfaz de un objeto PDO, permite usar las mismas funciones sin importar la Base de Datos que vayas a usar (Oracle, MySQL, Informix, etc), esto proporciona un entorno, en el cual sólo nos preocupemos por las operaciones que vayamos a realizar con la Base de Datos. 


¿Cómo instalarlo?
 (*Fuentes: Domine PHP5 -José López Quijado, editorial Alfaomega y http://www.php.net/manual/en/pdo.installation.php)

 En sistemas UNIX (configurar primero la extensión PDO como un módulo compartido):
    ./configure --with-zlib --enable-pdo=shared

Ahora, buscaremos el archivo php.ini y vamos a modificarlo según lo siguiente:

Encontrar la siguiente línea y descomentarla (Es decir, quitarle el  que le precede):
     En  UNIX:
          extension=pdo.so
     En WINDOWS:
           extension=php_pdo.dll
(*Si no aparece,  puedes escribirla en el mismo archivo).


Posteriormente descomentar la línea del controlador correspondiente al motor de la base de datos que vas a usar (Si no aparece la línea en el archivo de configuración, la puedes escribir ahí mismo, pero asegúrate de que no aparezca en otro lado del archivo o no se esté llamando de otra forma genérica. Nota: Ver más abajo sobre drivers genéricos). A continuación se muestran los drivers que se pueden utilizar, según el motor de la base de datos a usar y el sistema operativo.

Motor UNIX Windows
Firebird/Interbase pdo_firebird.so php_pdo_firebird.dll
Informix pdo_informix.so php_pdo_informix.dll
MsSQL pdo_mssql.so php_pdo_mssql.dll
MySQL pdo_mysql.so php_pdo_mysql.dll
Oracle Call Interface pdo_oci.so php_pdo_oci.dll
Oracle Call Interface 8 pdo_oci8.so php_pdo_oci8.dll
ODBC v3 pdo_odbc.so php_pdo_odbc.dll
PostgreSQL pdo_pgsql.so php_pdo_pgsql.dll
SQLite pdo_sqlite.so php_pdo_sqlite.dll
 (-Fin de las fuentes citadas).



No olvidar que...
  • Los controladores deben estar almacenados en la carpeta "ext"  o carpeta de extensiones, del directorio PHP. Asegúrate de que tu controlador (el archivo .dll ó  .so) esté en esa carpeta.
  • Por lo regular  las líneas vienen comentadas, a fin de que sólo se descomente aquella que se vaya a utilizar.

Usando XAMPP....

Si utilizas XAMPP, no hay que preocuparse por buscar y descargar los drivers, estos ya vienen junto con  la instalación. Este punto sólo puedo asegurarlo para Windows, desconozco si en la instalación en los sistemas Unix/Linux sea igual.

Drivers Genéricos...
Puede suceder que, alguno de los drivers citados anteriormente, no se encuentre en el php.ini, sin embargo, esto no quiere decir que no queda de otra más que escribirlo, posiblemente ya exista otra línea con otro driver PDO similar. Por ejemplo:
Seguramente en el php.ini que trae XAMPP no aparezca la línea "extension=php_pdo_mysql.dll" . Sin embargo antes de escribirla, recomiendo que primero busquen la línea:
   extension=php_pdo_mysql_libmysql.dll
Esta línea es equivalente a la anterior. Posiblemente esta última tenga más funcionalidades a comparación de la primera.

Escribir la línea del driver a usar  es la última opción a la cuál recurrir.

Dejar dos drivers iguales activados...
Por otro lado, no intenten dejar dos líneas de drivers iguales descomentadas, ya que el servidor les mostrará una molesta advertencia indicando que el controlador ya está en uso. Por ejemplo, retomando el caso anterior de MySQL, dejen sólo una de las dos líneas descomentadas.




Por mi parte, esto es lo más esencial acerca de lo que hay que saber sobre el PDO y los drivers. Dudas, comentarios, lo que sea, háganmelo saber, saludos.

viernes, 1 de julio de 2011

Un contador de palabras iguales en PL SQL

Este procedimiento permite buscar una palabra a partir de un texto y devolver el número de ocurrencias.

El procedimiento admite dos argumentos, el primero es la cadena (texto) a evaluar y el segundo argumento es la palabra a buscar.


Puntos a tomar en cuenta:



  • Necesitamos tener una variable de tipo number que nos permita guardar el total de las palabras iguales( cont number(3) ).
  • Como PL SQL no tiene una función específica para separar palabra por palabra de una cadena, nos serviremos de las funciones count, substr e instr (más adelante veremos porqué).
  • Debemos tener en cuenta  (para casos más detallistas) por ejemplo  la palabra "Hola" es diferente a "hola", así que lo mejor es darle formato  al texto y a la palabra a buscar, en este caso, lo que yo hice fue poner todo el texto en minúsculas mediante la función lower.
  • Al punto anterior se le añade  también la diferencia por ejemplo entre "Hola." y "Hola", debido a que en la primera existe un punto final, así que debemos de tener en cuenta esos signos de puntuación (;  , . :) , así bien lo que recomiendo es tener una variable por cada caso de signo.
Manos a la obra: 

/* Creamos el bloque con los dos argumentos, el primero es la cadena (párrafo) y el segundo la palabra a buscar.
El procedimiento se llama buscador
*/

create or replace procedure buscador (parrafo in varchar, palabra in varchar)
is
cont number(3):=0; -- Inicializamos el contador a 0
token varchar2(30); -- Almaceno una palabra que termine sin ningun signo de puntuacion.
tokpc varchar2(30); --
Almaceno una palabra que termine con punto y coma.
tokpf varchar2(30); --
Almaceno una palabra que termine con punto final
tokcc varchar2(30); --
Almaceno una palabra que termine con coma
tokdp varchar2(30); --
Almaceno una palabra que termine con dos puntos

/* Almacenaremos en una variable llamada parr el párrafo y le agregaremos un espacio en blanco
   El espacio en blanco es para evitar problemas en los cursores implicitos. 
   En esta variable el parrafo se le da el formato de minúsculas.
*/
parr  varchar2(1000):=lower(parrafo)||' ';
ntoken number(2):=0; -- Para contar si todavia hay palabras en la cadena
begin

 -- Haremos un ciclo para obtener palabra por palabra del párrafo.  
LOOP
-- A partir de la consulta, obtendremos una palabra del párrafo o cadena de texto.
-- La tabla dual viene por defecto y no hay que crearla (Esta es de mucha utilidad). 
select count(substr(parr,0,(instr(parr,' ')-1)))  into ntoken from dual;
 -- Si hay palabras  disponibles,entonces entra al if; de lo contrario saliir del ciclo.
   if ntoken > 0 then
-- Obtenemos cualquier palabra que pueda contener algún o ningún signo de puntuación
    select substr(parr,0,(instr(parr,' ')-1)) into token from dual;
     select substr(parr,0,(instr(parr,'. ')-1)) into tokpf from dual;
     select substr(parr,0,(instr(parr,'; ')-1)) into tokpc from dual;
     select substr(parr,0,(instr(parr,', ')-1)) into tokcc from dual;
     select substr(parr,0,(instr(parr,': ')-1)) into tokdp from dual

-- Comparamos si las palabras obtenidas son iguales a la palabra a buscar
-- En caso de ser cierto, incrementamos el contador.
    if token=palabra or tokpc=palabra or tokcc=palabra or tokpf=palabra or tokdp=palabra then
       cont:=cont+1;
     end if;

-- Aquí simulamos avanzar a la palabra siguiente descartando la palabra anterior.
 select substr(parr,(instr(parr,' ')+1)) into parr from dual;  

else 
  -- Salir de ciclo
  exit; 

end if; 
END LOOP;
DBMS_output.put_line('Palabras encontradas: '||cont);
end;
/


Resultados:

Llamaremos a nuestro procedimiento mediante la instrucción exec y los cuatro ejemplos deben devolver 4 palabras encontradas, las palabras en este color representan la cadena de texto y las que tienen este color, representan la cadena a buscar .
 
exec buscador('Uno. uno dos uno.','uno');
exec buscador('Dos; dos tres dos; ','dos');
exec buscador('Tres: TrEs cuatro tres','tres');
exec buscador('Cuatro, cuaTro cinco, CuATRo','cuatro');





Detalles:
  • La intstrucción count ( ) la utilizamos para verificar si aún podemos obtener alguna palabra del párrafo o cadena de texto.
  •  La instrucción substr ( )  nos ayuda a obtener una cadena, en este caso la combino con la función instr( ) que nos devuelve el índice del primer elemento (que coincida en su segundo argumento) para obtener el límite de una palabra y así obtener una palabra completa.
  • Así también uso las dos últimas funciones para descartar la palabra que hemos comparado y avanzar a una nueva.