Plantilla:PHP/PDO
Contenido
Qué es PDO
- La extensión PDO (PHP Data Objects) permite acceder a diferentes gestores de bases de datos utilizando las misma funciones.
- Esto es una gran ventaja frente a la extensión vista anteriormente mysqli,
- PDO nos abstrae de forma completa el sistema gestor que se utiliza.
- Como comentamos en el tema anterior, necesitaremos el driver concreto dependiendo del sistema gestor de bases de datos.
- Esto es lo único que tendremos que cambiar en nuestro programa para que funcione en uno u otro gestor de bases de datos, sin tener que cambiar nada del sql.
- En PHP 5 existen drivers para acceder a las bases de datos más populares (MySQL, Oracle, MS SQL Server, PostgreSQL, SQLite, Firebird, DB2, Informix, etc).
- En el siguiente enlace podemos ver los controladores de PDO que soporta directamente php.
http://es.php.net/manual/es/pdo.drivers.php
- En esta lección se explica el acceso a MySQL y SQLite mediante PDO. La extensión PDO no evalúa la correción de las consultas SQL.
Establecer conexión con PDO
- Para establecer una conexión lo que hacemos es instanciar un objeto de la clase PDO
$conexion = new PDO(...);
El constructor tiene 4 parámetros de los cuales sólo el primero es obligatorio
- Origen de datos (DSN).
data source name o nombre de origen de datos
- Este parámetro es un string que la información del controlador del driver que voy a utilizar y se especifica de la siguiente manera
controlador:parametro1=dato1;parametro2=datos...parametron=daton
- Los parámetros a especificar dependerá del controlador que vamos a utilizar, en general me informarán del controlador del driver que voy a utilizar como por ejemplo el nombre o dirección IP del servidor, el nombre de la base de datos).
- Por ejemplo en el caso del controlador mysql
$conexion = new PDO('mysql:host=localhost;dbname=dwes', ...);
- Nombre de usuario
- Contraseña del usuario.
- Opciones de conexión, almacenadas en forma de array.
- Muchas de las opciones de conexión dependerán del driver que vayamos a utilizar
- Por ejemplo con mysql podemos verlas aquí http://php.net/manual/es/ref.pdo-mysql.php
(Ver dentro de cada página de controladores http://php.net/manual/es/pdo.drivers.php)
$con =
Conxión con mysql
- En el caso de mysql en parámetro DNS tendríamos los siguientes datos
- host Nombre o dirección IP del servidor.
- port Número de puerto TCP en el que escucha el servidor.
- dbname Nombre de la base de datos.
- unix_socket Socket de MySQL en sistemas Unix.
- Como podemos ver en el ejemplo anterior, no todos los datos del parámetro DNS son obligatorios, podemos establecer la conexión con host y dbname.
- Respecto a las opciones de conexión permiten establecer varios cuestiones
- Una vez establecida la conexión se pueden consultar/acutalizar valores de opciones de la conexión usando los métodos
getAtribute(int $atributo); setAtribute(int $atributo, mixed $valor);
- Podemos ver los atributos en la página http://es.php.net/manual/es/pdo.setattribute.php http://es.php.net/manual/es/pdo.getattribute.php
Realizar consultas con PDO
- En el caso de PDO, se diferencias las consultas que retornan datos (SELECT) y las que actúan sobre el contendio de los datos (INSERT, UPDATE, DELETE)
- INSERT, UPDATE, DELETE
- En este caso la sentencia se ejecuta envíandola con el método exec($sentencia)
- Este método retorna un entero que indica en número de registros afectados
- En caso de que se produzca algún error, retornaría FALSE
$conexion = new PDO("mysql:host=localhost;db=dwes","root","root"); $registros = $conexion->exec("DELETE FROM stock WHERE unidades=0"); print "<p>Se han borrado $registros registros.</p>";
- Exactamente igual funcionarían con las sentencias insert o bien update
- SELECT
- En este caso debemos usar el método de la clase PDO llamado query($consulta)
- Este método retorna un objeto de la clase PDOStatement http://es1.php.net/manual/es/class.pdostatement.php
- Una vez que tenemos el objeto de la clase ya tenemos ese cursor o conjunto de filas con su puntero
- Para extraer cada fila usamos el método fetch(), el cual en caso de que no haya filas que retornar devuelve null (El mismo concepto trabajado hasta ahora).
- Cada vez que hacemos un fetch obtenemos un array con la fila que podemos usar tanto de forma asociativa como indexada.
- Este comportamiento por defecto se puede cambiar, es decir que podemos obligar a que el array que devuelve sea indexado, asociativo o que sea un objeto.
- Para ello debemos pasar al método fetch un valor que lo especifique según la lista siguiente.
- Para cerrar el cursor se emplea el método closeCursor(); muchos gestores de bases de datos necesitas que se libere, antes de ser usado para realizar otra consulta.
- PDO::FETCH_ASSOC. Devuelve solo un array asociativo.
- PDO::FETCH_NUM. Devuelve solo un array con claves numéricas.
- PDO::FETCH_BOTH. Devuelve un array con claves numéricas y asociativas. Es el comportamiento por defecto.
- PDO::FETCH_OBJ. Devuelve un objeto cuyas propiedades se corresponden con los campos del registro.
- A continuación diferentes formas de hacer exactamente lo mismo
$conexion = new PDO("mysql:host=localhost;dbname=dwes", "dwes", "abc123."); $resultado = $conexion->query("SELECT producto, unidades FROM stock"); while ($registro = $resultado->fetch()) { echo "Producto ".$registro['producto'].": ".$registro['unidades']."<br />"; }
$conexion = new PDO("mysql:host=localhost;dbname=dwes", "dwes", "abc123."); $resultado = $conexion->query("SELECT producto, unidades FROM stock"); while ($registro = $resultado->fetch(PDO::FETCH_ASSOC)) { echo "Producto ".$registro['producto'].": ".$registro['unidades']."<br />"; }
$conexion = new PDO("mysql:host=localhost;dbname=dwes", "dwes", "abc123."); $resultado = $conexion->query("SELECT producto, unidades FROM stock"); while ($registro = $resultado->fetch(PDO::FETCH_NUM)) { echo "Producto ".$registro[0].": ".$registro[1]."<br />"; }
$conexion = new PDO("mysql:host=localhost;dbname=dwes", "dwes", "abc123."); $resultado = $conexion->query("SELECT producto, unidades FROM stock"); while ($registro = $resultado->fetch(PDO::FETCH_OBJ)) { echo "Producto ".$registro->producto.": ".$registro->unidades."<br />"; }
$conexion="mysql:host=localhost;dbname=dwes"; $user="root"; $pass="root"; $opciones=array(PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES utf8"); $conexion=new PDO($conexion,$usuario,$pass, $opciones); $consulta = "Select * from ..."; $sentencia = "Insert into ....." $resultado = $conexion->exec($sentencia); $resultado->closeCursor(); echo "Se han insertado $resultado filas"; $resultado = $conexion->query($sconsulta); while $resultado->fetch(){ echo "se la leído el valor $resultado[0], ..."; } $conexion=null; //Es la manera de liberar a la memoria de este objeto. |
Consultas preparadas
- Al igual que en mysqli, podemos preparar las consultas. Esta forma de trabajar es cómoda y mas segura que la habitual, según viemos en apartados anteriores
- Para realizar una consulta parametrizada, hay que seguir unos pasos al igual que en mysqli
- preparar la consulta prepare(...)
- Para ello se pueden pasar con ? los valores de los parámetros o bien poner un nombre precedido de :
$conexion = new PDO("mysql:host=localhost;dbname=dwes", "dwes", "abc123."); $consulta = $conexion->prepare('INSERT INTO familia (cod, nombre) VALUES (?, ?)');
Es igual que hacer
$conexion = new PDO("mysql:host=localhost;dbname=dwes", "dwes", "abc123."); $consulta = $conexion->prepare('INSERT INTO familia (cod, nombre) VALUES (:codigoProducto, :nombreProducto)');
- Asignar valores a la consulta pararmetrizada
- Si se han especificado ? se asigna dando a cada parámetro un valor con el método bindParam(posicion, valor)
$cod_producto = "TABLET"; $nombre_producto = "Tablet PC"; $consulta->bindParam(1, $cod_producto); $consulta->bindParam(2, $nombre_producto);
- Si se han especificado con nombre se usan los nombre en lugar de los números
$cod_producto = "TABLET"; $nombre_producto = "Tablet PC"; $consulta->bindParam(":cod", $cod_producto); $consulta->bindParam(":nombre", $nombre_producto);
- Se ejecuta con el método execute()
- Este método permite alternativamente suplir las asignaciones anteriores realizadas con el método bindParam, pasándole en un argumento meditante una array dicha asignación.
- El array utilizado será asociativo o con claves numéricas dependiendo de la forma en que hayas indicado los parámetros.
- En el primer caso
$parametros = array[":nombre","TABLET"]; $consulta->execute($parametros);
- En el segundo caso
$parametros = array(":cod" => "TABLET", ":nombre" => "Tablet PC"); $consulta->execute($parametros);
Idioma no válido. Necesita especificar un idioma como esto: <source lang="html4strict">...</source> lenguajes soportados para sintaxis remarcada: 4cs, 6502acme, 6502kickass, 6502tasm, 68000devpac, abap, actionscript, actionscript3, ada, algol68, apache, applescript, apt_sources, arm, asm, asp, asymptote, autoconf, autohotkey, autoit, avisynth, awk, bascomavr, bash, basic4gl, bf, bibtex, blitzbasic, bnf, boo, c, c_loadrunner, c_mac, caddcl, cadlisp, cfdg, cfm, chaiscript, cil, clojure, cmake, cobol, coffeescript, cpp, cpp-qt, csharp, css, cuesheet, d, dcl, dcpu16, dcs, delphi, diff, div, dos, dot, e, ecmascript, eiffel, email, epc, erlang, euphoria, f1, falcon, fo, fortran, freebasic, freeswitch, fsharp, gambas, gdb, genero, genie, gettext, glsl, gml, gnuplot, go, groovy, gwbasic, haskell, haxe, hicest, hq9plus, html4strict, html5, icon, idl, ini, inno, intercal, io, j, java, java5, javascript, jquery, kixtart, klonec, klonecpp, latex, lb, ldif, lisp, llvm, locobasic, logtalk, lolcode, lotusformulas, lotusscript, lscript, lsl2, lua, m68k, magiksf, make, mapbasic, matlab, mirc, mmix, modula2, modula3, mpasm, mxml, mysql, nagios, netrexx, newlisp, nsis, oberon2, objc, objeck, ocaml, ocaml-brief, octave, oobas, oorexx, oracle11, oracle8, oxygene, oz, parasail, parigp, pascal, pcre, per, perl, perl6, pf, php, php-brief, pic16, pike, pixelbender, pli, plsql, postgresql, povray, powerbuilder, powershell, proftpd, progress, prolog, properties, providex, purebasic, pycon, pys60, python, q, qbasic, rails, rebol, reg, rexx, robots, rpmspec, rsplus, ruby, sas, scala, scheme, scilab, sdlbasic, smalltalk, smarty, spark, sparql, sql, stonescript, systemverilog, tcl, teraterm, text, thinbasic, tsql, typoscript, unicon, upc, urbi, uscript, vala, vb, vbnet, vedit, verilog, vhdl, vim, visualfoxpro, visualprolog, whitespace, whois, winbatch, xbasic, xml, xorg_conf, xpp, yaml, z80, zxbasic <?php $dns = "mysql:host=172.17.0.4; dbname=dwes"; $user='root'; $pass='root'; $opciones= array( PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES utf8"); //Realizamos una conexión básico pdo $msj =""; try { $conexion = new PDO($dns, $user, $pass, $opciones); $msj .= "conexión realizada satisfactoriamente<br />"; }catch (PDOException $excepcion) { die ("Se ha producido una excepción conectando " . $excepcion->getMessage()); } //Ahora planteamos una sentencia de tipo insert $sentencia = "insert into producto values('NEW_PRODUCTO12','NOMBRE_PRODUCTO','NOMBRE_CORTO','DESCRIPCION DESCRIPCION',10000,'MP3')"; //Y planteamos tambión una sentencia select $consulta ="select nombre_corto from producto"; $filas= $conexion->exec($sentencia); $msj .="<h3> Insertando </h3>"; $msj .= "Se ha insertado correctamene $filas<nr />"; //$filas será un objeto del tipo PDOStatement $filas= $conexion->query($consulta); $msj .="<h3> Consultando con un select </h3>"; while ($fila=$filas->fetch()){ $msj .= "Se ha recuperado $fila[0]<br/>"; } $filas->closeCursor(); $conexon=null; //Ahora hacemos la consulta parametrizadas //usando un objeto de la clase PDOStatement //Hacemos el prepare $sentencia= "Select producto from stock where tienda = :nom"; $consulta = $conexion->prepare($sentencia); $tienda =3; //$consulta->bindParam(':nom',$tienda,PDO::PARAM_INT); //$consulta->execute(); //Podemos usar la opción de antes o esta otra $consulta->execute(array(":nom"=>$tienda)); //Ahora mostramos los resultados $msj .="<h3> Realizando una consulta preparada</h3>"; while ($fila=$consulta->fetch()){ $msj .= "Visualizo el producto $fila[0]<br/>"; } ?> <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title></title> </head> <body> <?=$msj?> </body> </html>
|
Control de excepciones
- A partir de la versión 5 se introdujo en PHP un modelo de excepciones similar al existente en otros lenguajes de programación:
- El código susceptible de producir alguna situación de excepción, la cual produciría un error en ejecución, se puede introduce en un bloque try - catch, con la intención de controlar dicho error de ejecución o excepción (por ejemplo dividir por cero).
try{ //Instrucciones que pueden lanzar una excepción y //puedo capturar en tiempo de ejecución }cathc(Exception $e){ echo "Se ha producido una excepcion". $e->getMessage(); }
- Cuando se producen estas situaciones de excepción que queremos controlar, antes de que se produzcan, se lanza una excepción utilizando la instrucción throw.
- Después del bloque try debe haber como mínimo un bloque catch encargado de procesar dicha excepción.
- Si una vez acabado el bloque try no se ha lanzado ninguna excepción, se continúa con la ejecución en la línea siguiente al bloque o bloques catch.
- PHP ofrece una clase base Exception para utilizar como manejador de excepciones.
- Esta clase implementa dos métodos generales que nos muestran información sobre la excepticon que se ha podido producir
- getMessage. Devuelve el mensaje, en caso de que se haya puesto alguno.
- getCode. Devuelve el código de error si existe.
- El caso de PDO define su propia clase de excepciones que deriva o hereada de la clase Exception
- Para el caso concreto de PDO, hay que configurar para que lance las excepciones, puediento esta configuración tomar los siguientes valores:
- PDO::ERRMODE_SILENT. No se hace nada cuando ocurre un error. Es el comportamiento por defecto.
- PDO::ERRMODE_WARNING. Genera un error de tipo E_WARNING cuando se produce un error.
- PDO::ERRMODE_EXCEPTION. Cuando se produce un error lanza una excepción utilizando el manejador propio PDOException.
- Vamos a ver como se utiliza:
- Primero activamos las excepciones, y luego ya se pueden utilizar
$dwes = new PDO("mysql:host=localhost; dbname=dwes", "dwes", "abc123."); $dwes->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); try { $sql = "SELECT * FROM stox"; $result = $dwes->query($sql); //… } catch (PDOException $p) { echo "Error ".$p->getMessage()."<br />"; }
- En este caso que no existe la tabla nos diría
Error SQLSTATE[42S02]: Base table or view not found: 1146 Table 'dwes.stox' doesn't exist
- En el caso de mysqli usaríamos la clase mysqli_sql_exception que gestiona el tema de las excepciones
http://es.php.net/manual/es/class.mysqli-sql-exception.php
|
Excepciones en PDO
En PDO tenemos 3 niveles de controlar situaciones de excepción. Cada nivel corresponde a cada uno de los valores que a continuación comentamos.
- Para establecer el nivel de excepción se puede hacer de dos maneras
- Al establecer la conexión, estableciendo el tercer parámetro del constructor PDO, usando el índice
- Usando el método setAtribute() del objeto de la conexión PDO.
Cada nivel tiene una mayor o menor profundidad en el detalle de información que da en un determinado error de ejecución. Por ejemplo si no podemos conectar a una tabla de una base de datos por