|
|
Línea 941: |
Línea 941: |
| </div> | | </div> |
| --> | | --> |
− |
| |
− | ==Bases de datos con PDO==
| |
− | ===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
| |
− | <source lang=php>
| |
− | $conexion = new PDO(...);
| |
− | </source>
| |
− | El constructor tien 4 parámetros de los cuales sólo el primero es obligatorio
| |
− | ;Origen de datos (DSN).
| |
− | :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
| |
− | <source lang=php>
| |
− | $conexion = new PDO('mysql:host=localhost;dbname=dwes', ...);
| |
− | </source>
| |
− | ;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)
| |
− | ====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, OPDATE, 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
| |
− | <source lang=sql>
| |
− | $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>";
| |
− | </source>
| |
− | ;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
| |
− | <source lang=php>
| |
− | $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 />";
| |
− | }
| |
− | </source>
| |
− | <source lang=php>
| |
− | $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 />";
| |
− | }
| |
− | </source>
| |
− | <source lang=php>
| |
− | $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 />";
| |
− | }
| |
− | </source>
| |
− |
| |
− | <source lang=php>
| |
− | $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 />";
| |
− | }
| |
− | </source>
| |
− | {{Resumen|
| |
− | <source lang=php>
| |
− | $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);
| |
− | $sentencia->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.
| |
− |
| |
− | </source>
| |
− | }}
| |
− |
| |
− | ===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 :
| |
− | <source lang=php>
| |
− | $conexion = new PDO("mysql:host=localhost;dbname=dwes", "dwes", "abc123.");
| |
− | $consulta = $conexion->prepare('INSERT INTO familia (cod, nombre) VALUES (?, ?)');
| |
− | </source>
| |
− | Es igual que hacer
| |
− | <source lang=php>
| |
− | $conexion = new PDO("mysql:host=localhost;dbname=dwes", "dwes", "abc123.");
| |
− | $consulta = $conexion->prepare('INSERT INTO familia (cod, nombre) VALUES (:codigoProducto, :nombreProducto)');
| |
− | </source>
| |
− | ;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)
| |
− | <source lang=php>
| |
− | $cod_producto = "TABLET";
| |
− | $nombre_producto = "Tablet PC";
| |
− | $consulta->bindParam(1, $cod_producto);
| |
− | $consulta->bindParam(2, $nombre_producto);
| |
− | </source>
| |
− | *Si se han especificado con nombre se usan los nombre en lugar de los números
| |
− |
| |
− | <source lang=php>
| |
− | $cod_producto = "TABLET";
| |
− | $nombre_producto = "Tablet PC";
| |
− | $consulta->bindParam(":cod", $cod_producto);
| |
− | $consulta->bindParam(":nombre", $nombre_producto);
| |
− | </source>
| |
− | *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
| |
− | <source lang=php>
| |
− | $parametros = array["TABLET", ":nombre"];
| |
− | $consulta->execute($parametros);
| |
− | </source>
| |
− | *En el segundo caso
| |
− | <source lang=php>
| |
− | $parametros = array(":cod" => "TABLET", ":nombre" => "Tablet PC");
| |
− | $consulta->execute($parametros);
| |
− | </source>
| |
− | {{Actividad|
| |
− | *Realiza un pequeño programa en php que usando la extensión '''''PDO''''', realice las siguientes acciones
| |
− | #Se conecte a la base de datos '''''dwes'''''
| |
− | #insertamos un nuevo eleemntos o tupla en la tabla producto
| |
− | #consultamos todos los productos y los visualizamos
| |
− | #Hacemos otra consulta parametrizada de todos los productos de la tabla stock de una determinada tienda
| |
− | ##Esta última acción primero usando '''''bindParam''''' y luego sin usarlo (pasando directamente el parámetro al método execute
| |
− |
| |
− | <source lang=php>
| |
− | <!DOCTYPE html>
| |
− | <html>
| |
− | <head>
| |
− | <meta charset="UTF-8">
| |
− | <title></title>
| |
− | </head>
| |
− | <body>
| |
− | <?php
| |
− | $dns = "mysql:host=localhost; dbname=dwes";
| |
− | $user='root';
| |
− | $pass='root';
| |
− | $opciones= array( PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES utf8");
| |
− | //Realizamos una conexión básico pdo
| |
− | $conexion = new PDO($dns, $user, $pass, $opciones);
| |
− | if ($conexion)
| |
− | echo "conexión realizada satisfactoriamente";
| |
− | else
| |
− | echo "ohhhh!!!! no se ha conectado";
| |
− |
| |
− | //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);
| |
− | echo"Se ha insertado correctamene $filas";
| |
− | //$filas será un objeto del tipo PDOStatement
| |
− | $filas= $conexion->query($consulta);
| |
− |
| |
− | while ($fila=$filas->fetch()){
| |
− | echo "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
| |
− | while ($fila=$consulta->fetch()){
| |
− | echo "Visualizo el producto $fila[0]<br/>";
| |
− |
| |
− | }
| |
− | ?>
| |
− | </body>
| |
− | </html>
| |
− |
| |
− | </source>
| |
− |
| |
− | }}
| |
− |
| |
− | ===Control de excepticones===
| |
− | *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 algún error se introduce en un bloque '''''try - catch'''''.
| |
− | <source lang=php>
| |
− | 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();
| |
− | }
| |
− | </source>
| |
− | #Cuando se produce algún error, 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 el error.
| |
− | #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.
| |
− | {{Actividad|
| |
− | Haz un programa que si dividimos por cero pase una exepcion
| |
− |
| |
− | <source lang=php>
| |
− | try {
| |
− | if ($divisor == 0)
| |
− | throw new Exception("División por cero.");
| |
− | $resultado = $dividendo / $divisor;
| |
− | }
| |
− | catch (Exception $e) {
| |
− | echo "Se ha producido el siguiente error: ".$e->getMessage();
| |
− | }
| |
− | </source>
| |
− | }}
| |
− | *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
| |
− | <source lang=php>
| |
− | $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 />";
| |
− | }
| |
− | </source>
| |
− | *En este caso que no existe la tabla nos diría
| |
− | <source lang=php>
| |
− | Error SQLSTATE[42S02]: Base table or view not found: 1146 Table 'dwes.stox' doesn't exist
| |
− | </source>
| |
− | *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
| |
− | {{Actividad|
| |
− | *Realicemos un fichero de conexión a base de datos que contenga las siguientes funciones
| |
− | #'''''conectar'''''($bd, $usuario,$password) Retornará un objeto de la clase PDO si todo ok
| |
− | #'''''consulta($sentencia,$parametros)''''' Retorna un objeto de la clase PDOStatement si todo ok,
| |
− | ##Recibe dos argumentos, un string que será la consulta parametrizada, y un array con los valores para cada parámetro
| |
− | #'''''Insertar($tabla,$valores)'''''
| |
− | ##Recibe un string que es el nombre de tabla y un vector que serán los diferentes valores para cada campo de la tabla
| |
− | ##Suponemos que se pasan los valores ok, si no, capturamos la excepción
| |
− | }}
| |
| | | |
| ===Filtros=== | | ===Filtros=== |