Conectarnos (Para ello necesitamos un software específico del gestor de bases de datos con el que vayamos a trabajar).
Seleccionar (La base de datos con la que vamos a trabajar).
Trabajar con Bases de datos
Actuar con la base de datos (Consultas, inserciones, modificaciones y/o borrados)
Procesar información
Cerrar la base de datos
Para realizar estas acciones disponemos de diversas Funciones/Clases específicas dentro de PHP, Nos referiremos a ellos como extensiones de PHP
Bases de datos y PHP
PHP tiene un API especifico para trabajar directamente con mysql mysqli, el cual incorpora el driver y conector necesario para trabajar con ella de forma nativa
Que el driver sea nativo es que está implementado utlizando un framework de extensiones dde php.
También vamos a disponer de la extensión PDO, la cual se independiza del gestor concreto de bases datos que vayamos a utilizar.
Extensiones de php
Por lo tanto en este tema vamos a ver dos extensiones:
mysqli usar una extensión nativa con su SGBD en concreto mysql que viene con el propio lenguaje
PDO usar una extensión genéricoa que permite conectarse con cualquier gestor de BD, sin necesidad de cambiar nada de código.
Introducción al Uso de extensiones orientadas a objetos
Para trabajar con las extensiones, las usaremos siempre orientadas a objetos, aunque tengan
la correspondiente funcionalidad en el lenguaje estructurado.
Para recordar muy brevemene posemos usar el siguiente enlace
Instalamos y probamos las diferentes opciones con esta herramienta
Probamos a hacer un diseño en modelo realcional y a partir del modelo creamos las tablas.
Probamos las opciones forward engineer y reverser engineer
Herramientas de administración
Con phpmyadmin, podemos hacer casi todo de administrar y manejar las bases de datos
No obstante mysql proporciona una serie de herraminetas que perminten administrar por línea de comandos .
En muchas ocasiones este tipo de operaciones resultan muy interesantes.
mysql
myhsqladmin
mysqlshow
mysql
Esta heramienta ya la hemos comentado, permite conectarse a un servidor MySQL para ejecutar sentencias SQL
Opciomes básicas ya conocidas
connect -h,-u, -p
use database
exit o quit
help Para conocer los comandos que se pueden usar.
A continuación iremos viendo como implementar las acciones básicas en el lenguaje
Conectarse
Para conectarse a una base de datos , creamos una instacia de la clase mysqli de la forma
$miConexion=new mysqli(....);
Extensión Mysqli
El constructor de la clase puede recibir hasta 5 parámetros, de los cuales 4 se suelen usar con bastante frecuencia
$host nombre o ip del equipo (null o localhost, identificaría el equipo actual).i
$usario es el usuario de la base de datos
$pass
$nombreBD
$puerto
$shocket
Ejemplo new mysqli(...)
$host="localhost"$usuario="manolo";$pass="romero";$nombreBD="alumnos";$miConexion=new mysqli ($host,$usuario,$pass,$nombreBD);if($miConexion==null)echo"Error conectando a la base de datos";elseecho"Conectado a la base de datos";
mysqli(...)
Esta función retorna el recurso de la conexión
Para gestionar los errores deberíamos usar el atributo connect_error de la clase mysqli
Este atributo aporta información sobre el error o contiene null si no se ha producido ninguno.
En el código anterior
if($miConexion->connect_error)echo"Error conectando con la base de datos: ".$miConexion->connect_error;
Para ver información sobre la conexión se puede usar los atributos *server_info o host_info
Configura dicho fichero, para poder conectar a la base de datos sin aportar parámetros al constructor
Luego déjalo como estaba :)
Cambiar la base de datos
Si hemos seleccionado una base de datos, o no hemos seleccionado ninguna y queremos cambiar a otra
$miConexion->select_db("nombre_base_datos");
Cuando ya no vamos a usar un recurso, conviente y repito CONVIENE, liberarlo.
$miConexion->close();
DML
En SQL sabemos que tenmos tres tipos de lenguajes DDL, DML, DCL
Ahora toca DML, Leguane de maninputación de datos
Podemos clasificar en dos tipos de clúsulas:
las que no devuelven registros de datos (INSERT, DELETE, UPDATE)
Generalmente retornan un entero que es el número de filas aceptadas o un booleano que indica si se realizó no la operación
DML
Las que pueden retornan una colección de filas (SELECT), generalmente conocidas como cursor.
En mysqli podemos enviar cualquiera de estas claúsulas con el método query
INSERT, UPDATE Y DELETE: Método query
Este tipo de sentencias retornan un booleano que indica si se ha realizado o no la acción
El número de filas afectadas lo podemos ver en el atributo affected_rows
Resumen
$reusultado=miConexion->query($consulta)if($resultado){echo"Se han afectado $miConexion->affected_rows filas en esta acción");}
$sentenciaDelete="DELETE FROM tabla WHERE condicion";$sentenciaInsert="INSERT INTO tabla (opcional lista de campos) VALUES (valores en posiciones respectivas a los campos);
$sentenciaUpdate="UPDATE tabla WHERE condicion";
$miConexion->query($sentenciasDelete);
$resultado =$miConexion->query($sentenciaDELETE);
if ($resultado){
echo "Se han borrado $miConexion->affected_rows filas ";
}
$miConexion->query($sentenciasUpdate);
$resultado = $miConexion->query($sentenciaUPDATE);
if ($resultado){
echo "Se han actualizado $miConexion->affected_rows filas ";
}
</sourece>
</div>
<div class="slide">
<source lang=php>
$miConexion->query($sentenciasInsert);
$resultado = $miConexion->query($sentenciaINSERT);
if ($resultado){
echo "Se han insertado $miConexion->affected_rows filas ";
}
Observemos su uso en el ejemplo
//Establecemos la conexión$miConexion=new mysqli('localhost','manolo','12345.','baseDatosPrueba');//Capturamos un posible error$error=$miConexion->connect_errno;//En caso de error informamos de elloif($error==null){$resultado=$miConexion->query('DELETE FROM stock WHERE unidades=0');if($resultado){print"<p>Se han borrado $miConexion->affected_rows registros.</p>";}}$miConexion->close();}
Clausula SELECT con query
Tenemos dos maneras de realizar consultas con mysqli
query
real_query
En el primero caso el método nos retorna un cursor que será de la clase mysqli_result
En el segundo caso nos retornará un booleano y para leer los datos deberemos usar o store_result o use_result según veamos a continuación.
Método query
Una vez que tenemos los datos almacenados debemos saber acceder.
Tenemos 4 formas de poder acceder a los datos según usemos un método u otro
fetch_array()
Va obteniendo cada registro como un array
Este array podemos usar tanto de forma indexada, como asociativa (con el nombre del campo)
fetch_assoc()
En este caso el array que retorna es asociativo
fetch_row()
En este caso el array que retorna es indexado
fetch_object()
En este caso en lugar de retornar un array, retorna un objeto, donde cada campo son los diferentes atributos de ese objeto
En todos los casos cada vez que leemos un elemento de mysqli_result, lo que por comparativa sería un cursor, vamos avanzando al siguiente. Cuando hayamos leído todos retornaría null
Actividad
Optén todos los registros de la tabla familia y visualizalos en una tabla usando los tres modos de lectura de datos vistos anteriormente
Para liberar un recurso del tipo mysqli_result, usamos el método free();
La clase mysqli_result, además de los métodos vistos tiene un par de atributos interesantes
int $field_count;
Nos dice cuantas columnos tiene el query actual
int $num_rows;
Nos dice cuantas filas hemos obtenido con la consulta
$conexion=new mysqli($host,$user,$pass,$bd);if($conexion->connect_errno==null){$resultado=$conexion->query($consulta);$numFilas=$resultado->num_rows;$numCampos=$resultado->fields_count;echo"La consulta ha retornado $numFilas filas con $numCampos columnas";$fila=$resultado->fetch_array();while($fila){echo"El valor del primer campo es $fila[0]";$fila=$resultado->fetch_array();}$resultado->free();$conexion->close();}
Actividad
Haciendo una consulta del tipo select * from producto where pvp < 200, realiza un código que visualizce en una tabla los resultados
Transacciones
Definición
Una transacción es un conjunto de acciones u operaciones que se realizan contra una base de datos de modo que o bien se realizan correctamente todas o no se realiza ninguna
Supongamos que hacemos una transferencia bancaria; cuando menos implica descontar dinera de una cuenta e ingresaro en otra
Transacciones
Ahora supongamos que nada mas descontar el dinero de una cuenta se cae la luz y se apaga el servidor
Esto creará una inconsistencia en la base de datos
Por defecto en mysqli cada acción con la base de datos es una transacción en sí misma, pero esto se puede modificar
Si se ha desactivado el autocommit, para terminar una transacción debemos usar los métodos commit o rollback
$conexion=new mysqli(..);$conexion->autocommit(false);.....if(CondicionOK){//Terminamos la transacción confirmando todas las acciones sobre la base de datos desde que se inició la
transaccion
$conexion->commit();}else{//Terminamos la transacción desaciendo todas las acciones sobre la base de datos desde que se inició la
transaccion, y dejando la base de datos igual que estaba al principio
$conexion->rollback();}
Actividad
Ejercicio de transacciones
Injecciones SQL
Es un problema de seguridad importante, que hay que conocer y evitar
Existe mucha documentación al respecto, en general podemos afirmar que un buen conocimiento de SQL proporcina herramientas tanto para poder establecer este tipo de ataques, como para podernos prevenir de ellos.
Aportamos referenicas de web que nos pueden interesar consultar
A continuación y usando el ejemplo anterior de acceso vamos a probar a realizar un sencillo ataques sql.
Entrar en la plataforma sin tener acceso
Entrar en la plataforma sin tener acceso
Entramos en una página y vemos el siguiente acceso
Como no sabemos el usuario ni contraseña probamos a ver si se puede hacer una inserción no controlada
Como programadores esperamos que en el código haya algo del estilo, como es nuestro caso
$nombre=$_POST['usuario'];$pass=$_POST['pass'];$consulta="select * from usuarios where nombre = \"$nombre\" and pass = \"$pass\" ";$resultado=$conexion->query($consulta);
Si todo fuera normal y nombre fuera por ejemplo "maría" la consulta que se envía al servidor sería
select * from usuarios where nombre ="maria"
Esta consulta si existe el usuario maría nos retornará una tupla, si no no devolverá ninguna.
Inyecciones sql
Pero si añadimos más cosas obtendremos segura una respuesta, por ejemplo si en el codigo $nombre="maria or \"1\"= \"1\" "
Entonces la consulta quedaría
select * from usuarios where nombre ="maria" or "1"="1"
Que nos devolverá todas las filas
Así que si introducimos estos datos
Entramos al sistema sin conocer usuario y contraseña
Consultas preparadas
Una consulta preparada consiste en establecer una consulta como si fuera una variable y ejecutarla posteriormente tantas veces como sea necesario.
Estas consultas se almacenan en el servidor y están listas para ser ejecutadas cuando sea necesario. El servidor solo tiene que analizarlas una vez
Para trabajar con consultas preparadas, debemos usar la clase mysqli_stmt, e inicializarla con el método stmt_init
$conexion=new mysqli('localhost','dwes','abc123.','dwes');//Preparo el objeto $consulta para crear consultas preparadas en él$consulta=$conexion->stmt_init();
Los pasos para trabajar con consultas preparadas son:
Preparar la consulta en el servidor MySQL utilizando el método prepare.
Ejecutar la consulta, tantas veces como sea necesario, con el método execute.
Una vez que ya no se necesita más, se debe ejecutar el método close.
$consulta=$conexion->stmt_init();$consulta->prepare('INSERT INTO familia (cod, nombre) VALUES ("TABLET", "Tablet PC")');$consulta->execute();$consulta->close();$conexion->close();
Parametrizar las consultas preparadas
El uso real de las consultas preparadas es que los valores que pasas se asignen antes de ejectuar la consulta.
La idea es preapara la consulta sin indicar los valores.
Asignar los valores y ejectuar la consulta cuantas veces sea necesario.
Veamos el proceso
Parametrizar la consulta
Consiste en indicar en la consulta preparada en lugar de los valores, signos de interrogación ?
En el caso anterior
$consulta->prepare('INSERT INTO familia (cod, nombre) VALUES (?,?);
Ahora habría que asigar los valores. Para ello usamos el método bind_param'
bind_param(tipoDatos, variables_con_los_valores)
Este método recibe dos tipos de parámetros
El primero es una cadena de caracteres, donde cada carácter especifica el tipo de valor que va a recibir cada uno de los valores esperados en la consulta.
La codificación sería :
s: cadena de caracteres
i: número entero
d: número float
b: valor binario (BLOB)
Consultas preparadas
En nuestro caso como va a recibir en los dos parámetros cada uno una cadena de caracteres sería "ss"
El segundo grupo sería cada uno de los valores.SIEMPRE hay que especificar variables
En el ejemplo que estamos siguiendo
$consulta=$conexion->stmt_init();$consulta->prepare('INSERT INTO familia (cod, nombre) VALUES (?, ?)');$cod_producto="TABLET";$nombre_producto="Tablet PC";$consulta->bind_param('ss',$cod_producto,$nombre_producto);
Insisto en que siempre hay que especificar variables, nunca directamente valores.
Vemos el siguiente ejemplo:
$consulta->bind_param('ss','TABLET','Tablet PC');// Genera un error
Resumen
$conexion=new mysqli(...);$consulta=$conexion->stmt_init();$consulta->prepare(...sentencia ... con ???)$consulta->bind_param('s-i-b-d(tipo_de_valores)',valores_en_variables_respectivos_a_????');
$consulta->execute();
$consulta->close();
$conexion->close();
Consultas preparadas que retornan valores
En caso de que la consulta preparada retorne valores se recogen con el método bind_result'
Este método recibirá variables en los que se almacenarán los valores
Para recorre el conjunto de valores, usamos el método fectch()
Vemos el siguiente ejemplo
Consultas preparadas que generan valores
$consulta=$conexion->stmt_init();$consulta->prepare('SELECT producto, unidades FROM stock WHERE unidades<2');$consulta->execute();$consulta->bind_result($producto,$unidades);while($consulta->fetch()){print"<p>Producto $producto: $unidades unidades.</p>";}$consulta->close();$conexion->close();
Aquí hay un enlace para una información completa sobre consultas preparadas
Modifica el ejercicio anterior usando consultas parametrizadas
Práctica de tienda
Vamos a trabajar con la base de datos de la tienda
Lo primero usando la herramienta workbench generamos el modelo de tablas de la base de datos dwes y la analizamos
Crea una página web en la que se muestre el stock existente de un determinado producto en cada una de las tiendas.
Para seleccionar el producto concreto utiliza un cuadro de selección dentro de un formulario en esa misma página.
{{{1}}}
Práctica de tienda
Puedes usar como base los siguientes ficheros css y plantilla adjuntos.
Añade la opción de modificar el número de unidades del producto en cada una de las tiendas.
Utiliza una consulta preparada para la actualización de registros en la tabla stock.
No es necesario tener en cuenta las tareas de inserción (no existían unidades anteriormente) y borrado (si el número final de unidades es cero).
{{{1}}}
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.
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).
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
$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>";
SELECT
En este caso debemos usar el método de la clase PDO llamado query($consulta)
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 />";}
Resumen
$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.
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)
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.
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
<!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";elseecho"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 resultadoswhile($fila=$consulta->fetch()){echo"Visualizo el producto $fila[0]<br/>";}?>
</body>
</html>
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.
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 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
try {if($divisor==0)thrownew Exception("División por cero.");$resultado=$dividendo/$divisor;}
catch (Exception $e){echo"Se ha producido el siguiente error: ".$e->getMessage();}
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