Uso de MySQL con PDO – PHP (I). Conectar, leer y escribir.

Facebooktwittergoogle_pluslinkedinmailFacebooktwittergoogle_pluslinkedinmail

En los tutoriales de PHP hemos aprendido bastante sobre el uso teórico del lenguaje SQL. En este artículo vamos a aprender a manejar el motor más popular, MySQL, desde PHP. Lo bueno de esto es que lo que aquí aprendamos nos servirá, con muy pocos cambios, para manejar otros motores de base de datos, si llegara el caso.

PHP incluye la extensión mysqli para conectar con el motor de MySQL y enviar consultas. La extensión tradicional, mysql, fue declarada obsoleta en PHP 5.4 y ha sido eliminada en PHP 7.0, por lo que no hablaremos aquí de ella. La extensión mysqli puede ser manejada según el estilo procedimental, o mediante POO. Dado que el POO es más robusto, fiable y acorde con los cánones de programación actuales, es en esta modalidad en la que vamos a centrarnos en este artículo.

Para manejar una conexión mysqli mediante POO, PHP nos proporciona PDO_MYSQL, que es un controlador que implementa la interfaz de Objetos de Datos de PHP (PDO, PHP Data Object) para permitir el acceso de PHP a bases de datos de MySQL 3.x, 4.x y 5.x. El motor actual de MySQL son las versiones 5.x, por lo que omitiremos las especificaciones o limitaciones propias de las anteriores.

ATENCIÓN. Centrarnos en MySQL es debido a varias razones: La primera es que se trata de uno de los motores de base de datos más potentes, flexibles y rápidos que existen. Su uso es libre y gratuito. Aunque existe una licencia de pago que proporciona soporte técnico, no es necesario, en la mayoría de los casos, recurrir a ella. Es un sistema fiable, con gran capacidad de almacenamiento y respuesta, y que implementa todas las funcionalidades de SQL. Además, se integra perfectamente con PHP, a través de mysqli y PDO. En el mercado existen otros motores de bases de datos, pero ninguno reune todas estas ventajas. Los que son muy potentes, tienen licencias caras. Los que son gratuitos o más asequibles adolecen de ciertas limitaciones. Sólo MySQL está, digamos, a la altura.

Para acceder a una base de datos, ya sea MySQL o cualquier otra, y sea cual sea la extensión o modo de uso, se realizan tres procesos fundamentales:

  • Establecer la conexión con el Motor de Base de Datos.
  • Efectuar las consultas necesarias, tanto para leer y recuperar uno o más registros, cómo para insertar, eliminar, o actualizar el contenido.
  • Liberar los recursos empleados.

ESTABLECER LA CONEXION

Para establecer una conexión con MySQL recurrimos a PDO, que implementa un controlador específico para este motor de base de datos. La forma de establecer la conexión es crear un objeto de la clase PDO, a través del constructor de la misma. La sintaxis genérica es la siguiente:

$conexion = new PDO ($dsn, $usuario, $password, $opciones);

El último parámetro, $opciones, es opcional, y no suele establecerse en la conexión, porque puede establecerse y/o modificarse posteriormente, cómo veremos en este artículo.

Los parámetros $usuario y $password creo que están claros. Son el nombre de usuario y contraseña que tenemos para acceder al motor de MySQL.

El parámetro $dsn es el que realmente define la conexión, el controlador que se va a usar, según el motor de base de datos que tengamos, el nombre de la base de datos…. Su formato generico es el siguiente:

motor:host=servidor;dbname=base_de_datos;port=puerto;charset=UTF8

  • motor es lo primero que le pasamos al constructor, para que sepa que controlador tiene que usar. Si vamos a usar una base de datos MySQL, Oracle, Firebird, etc, lo especificaremos aquí. Se separa del resto de la cadena con el signo :.
  • host=servidor se refiere al servidor que vamos a emplear. Para una conexión que vayamos a probar con Xammp en nuestro equipo, será localhost, o 127.0.0.1, cómo prefieras.
  • dbname=base_de_datos es el nombre de la base de datos que vamos usar a través de esta conexión.
  • port=puerto indica el puerto por el que escucha el motor de base de datos. Los motores de base de datos tienen un puerto por defecto (en el caso de MysQL es el 3306). Si tu instalación está configurada con el puerto por defecto, no necesitas especificarlo. El controlador de PDO que hayas elegido, según el parámetro motor, coge el puerto por defecto si no especificas otra cosa.
  • charset=UTF-8 es más importante de lo que parece. Se refiere a la codificación con la que se almacenan los datos en tu base de datos, y con la que se intentan recuperar. Si no coincide, puedes tener problemas con caracteres especiales, letras acentuadas, etc. Usa siempre UTF-8, tanto en tu base de datos como en la conexión a la misma, y te ahorrarás problemas.

Una conexión típica en localhost (usando Xampp), en la que la base de datos está configurada para un usuario root sin contraseña, y escuchando por el puerto por defecto, es la siguiente:

$conexion = new PDO('mysql:host=localhost;dbname=agenda;charset=UTF8', 'root', '');

Decíamos hace unas líneas que las opciones podemos establecerlas y modificarlas a posteriori. Para ello empleamos el método PDO::setAttribute() sobre el objeto que hemos creado con el constructor, así:

$conexion->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

A este método se le pasan dos parámetros. El primero se refiere a un atributo de comportamiento del objeto PDO. El segundo es el valor que recibe ese atributo, y que determina cómo actúa. Para entendernos. En el ejemplo que ves arriba, el atributo es PDO::ATTR_ERRMODE, que determina que ocurrirá si se produce un fallo durante una operación sobre la base de datos. El valor que le he asignado es PDO::ERRMODE_EXCEPTION, que hace que, ante un fallo, se lance una excepción capturable. Esta es, sin duda, la mejor opción, ya que nos permite usar el mecanismo de captura de excepciones de PHP. Las otras posibilidades para este atributo serían PDO::ERRMODE_SILENT, que silencia el error, y PDO::ERRMODE_WARNING, que lanza un error de nivel warning a la página. Yo, personalmente, prefiero las excepciones capturables, porque me avisa de cualquier problema, y me da más control por programación.

Este es, realmente, el único atributo que yo uso en mi día a día y, probablemente, el único que tú vayas a necesitar. Si quieres conocer otros, por curiosidad, te remito a la documentación oficial de PHP al respecto.

Y ya está. Con las dos líneas de código que hemos visto, ya tenemos la conexión establecida. Por supuesto, podemos agregar un mecanismo de captura de excepciones, para asegurarnos de que la conexión se ha establecido sin problemas, así:

try {
    $conexion = new PDO('mysql:host=localhost;dbname=agenda;charset=UTF8', 'root', '');
} catch (PDOException $e) {
    echo 'Se ha producido una excepción: '.$e->getMessage();
    die();
}

Esto haría que, ante una excepción, se lance un mensaje y se interrumpa la ejecución.

EFECTUANDO CONSULTAS CON PDO

Una vez establecida la conexión con la base de datos, podemos efectuar consultas contra la misma. Las consultas pueden englobarse en dos grandes grupos: las que devuelven resultados (consultas de selección), y las que no (consultas de inserción, actualización y/o borrado).

Para ver cómo hacemos consultas con el objeto PDO vamos a partir de una base de datos llamada agenda que, a su vez, contiene una tabla llamada contactos, donde almacenamos los nombres de algunos contactos, su ciudad y su pais. La tabla es muy sencillita, para que veamos claros los ejemplos, cómo ves aquí:

id nombre ciudad pais
1 Paco Madrid España
2 León Barcelona España
3 Sonia Málaga España
4 Carmen Asturias España
5 John Londres Reino Unido
6 Ann Liverpool Reino Unido
7 Marcel París Francia
8 Giancarlo Roma Italia
9 Louise Marsella Francia
10 Francesca Milán Italia
11 Rudolf Berlín Alemania
12 Mariska Berlín Alemania

Vamos a empezar con un script muy simple que nos recupera los contactos que tenemos en Italia. Lo hemos llamado italianos.php:

italianosEl resultado es una matriz con dos elementos (los dos que hay de Italia), cómo se ve a la izquierda.

Veamos lo que hemos hecho. Las dos primeras líneas resaltadas establecen la conexión con la base de datos, cómo hemos descrito en el apartado anterior, creando un objeto PDO llamado $conexion. Este objeto será el que empleemos para ejecutar cualquier consulta sobre la base de datos.

En la última línea resaltada vemos que se emplea el método query(), propio de los objetos PDO, para efectuar las consultas que devuelven resultados (las de tipo SELECT). Este método recibe dos argumentos. El primero es obligatorio, y es la propia consulta que queremos ejecutar. El segundo es opcional, y establece cómo queremos recuperar cada elemento de la consulta. Se trata de una constante propia de PDO, que determina el modo de recuperar elementos. Al haber puesto PDO::FETCH_ASSOC hemos indicado que deseamos los resultados cómo una matriz asociativa, y eso es lo que ves en el resultado. Aunque la matriz de contactos es indexada, porque cada contacto es independiente y no tienen una clave asociativa específica, dentro de cada contacto tenemos los datos con claves asociativas que se corresponde, cómo ves, con los nombres de las columnas en la tabla. Si hubiéramos puesto PDO::FETCH_NUM, los datos de cada contacto aparecerían con una clave indexada. Si ponemos PDO::FETCH_BOTH (o no ponemos nada, ya que este es el valor por defecto), en cada contacto los datos aparecerán duplicados, con clave asociativa e indexada. Pruébalo para ver que ocurre.

Ahora vamos a ver cómo agregamos un nuevo contacto. Esta vez nos vamos a ir, nada menos, que al país de los tulipanes. El código se llama nuevo_contacto.php:

Si ahora abres tu base de datos (por ejemplo, con PHPMyAdmin, verás que el nuevo contacto aparece en la tabla correspondiente, al final de la misma. En la línea resaltada ves cómo hemos efectuado la consulta.

En el caso de las consultas que NO devuelven resultados (INSERT, UPDATE o DELETE), se puede emplear el método exec(), en lugar de query(), pero esto no funciona con las consultas de tipo SELECT. Así pues, en el último script visto, la línea resaltada se podría haber escrito así:

$conexion->exec($consulta);

Cómo ves, con este sistema podemos efectuar cualquier tipo de consulta sobre una base de datos.

ATENCiÓN. Los ejemplos que aquí aparecen son muy breves, porque se trata de ilustrar el funcionamiento de PDO, y cómo gestionar bases de datos desde PHP. El lenguaje SQL, en sí (con el que se escriben las consultas) es objeto de estudio en otros artículos de este blog.

CONSULTAS PREPARADAS

Se llama consultas preparadas a aquellas que se almacenan cómo una plantilla, pero sin asignarle valores a los criterios de selección. Estos valores se asignan justo antes de ejecutar la consulta, y PHP incorpora una clase, llamada PDOStatement, que nos permite trabajar con este tipo de sentencias.

Las razones para emplear sentencias preparadas pueden ser varias. Por un lado, la flexibilidad. Una consulta puede estar definida una vez y, cambiando un valor darnos un resultado diferente. Si hablas con otros programadores, seguramente te den otras razones. Sin embargo, a mi juicio, la más importante es la seguridad. El uso de sentencias preparadas contribuye a evitar los ataques por inyección SQL (este es un tema del que hablaremos en otra sección de este blog, en su momento).

Igual que antes, vamos a empezar viendo un ejemplo de uso de consulta con recuperación de datos. Se llama recuperacion_preparada.php:

Al igual que antes, vamos a sacar la lista de los contactos que tenemos en Italia. Sin embargo, fíjate en la línea resaltada. Ponemos, cómo condición, pais = ?, es decir, no especificamos Italia, ni ningún otro país. El signo ? es un comodín al que le podemos asignar un valor y, según el que le asignemos, la consulta variará.

A continuación creamos un objeto de la clase PDOStatement pero, en lugar de crearlo directamente, usando un constructor, esta vez hacemos algo diferente: usamos el método PDO::prepare(), del objeto $conexion, pasándole, cómo argumento, la consulta que queremos preparar. Esto nos crea el objeto $hacerConsulta, que pertenece a la clase PDOStatement, cómo hemos comentado.

Ahora le pasamos el valor que sustituye al comodín en la consulta, mediante el método PDOStatement::bindValue(), del objeto $hacerConsulta. Este método recibe dos argumentos. El primero es un número de orden, que indica que comodín vamos a sustituir. En esta consulta sólo hay uno, pero podría haber más. Si hay más, se deberán numerar consecutivamente desde 1 en adelante. El segundo argumento es el valor que queremos que adopte el comodín.

Una vez que los comodines (o el comodín, en este caso) ya tienen valor asignado, ejecutamos la consulta con el método PDOStatement::execute() que, en este ejemplo, no recibe argumentos (ya veremos otros casos).

Ahora recuperamos TODOS los resultados que se ajusten a la consulta, mediante el método PDOStatement::fetchAll(). Este recibe un indicador que ya conocemos, para obtener los resultados en matrices asociativas. Cómo ves, los resultados se asignan, directamente, a una matriz, de modo que cada contacto queda en un elemento de la misma.

Por último, con el método PDOStatement::closeCursor() liberamos el recurso empleado.

El resultado es el mismo que en el caso anterior: una matriz con los dos contactos de Italia, dónde cada contacto es, a su vez, una matriz asociativa. Pruébalo para verlo.

Cuando le pasamos a la consulta los datos para sustituir a los comodines, podemos usar valores fijos, cómo hemos hecho en este ejemplo, o variables. En este último caso, el método PDOStatement::bindValue() debe ser sustituido por PDOStatement::bindParam(). Vamos a ver un ejemplo en el que recuperamos los contactos de Francia, pero usando una variable, en francia_variable.php:

Observa cómo lo hemos hecho. Ahora vamos a suponer que queremos los contactos de Francia, o aquellos cuyo nombre sea por Ann. Observa mas_condiciones.php:

Observa la consulta, donde en cada condición se ha sustituido el valor por un comodín. Y presta especial atención a las líneas resaltadas. Ves que el primer comodin lo sustituimos por una variable, por lo que recurrimos al método PDOStatement::bindParam(), mientras que el segundo es un valor, por lo que usamos PDOStatement::bindValue(). Eso es indiferente a la hora de numerarlos, cómo ves. El que corresponde al primer comodín lleva el número 1 y el que corresponde al segundo comodín lleva el 2.

Veamos otro ejemplo, en el que buscamos los contactos de Francia, o aquellos cuyo nombre empieza por A. Se llama condiciones_flexibles.php:

Observa la consulta, y cómo hemos asignado valores. Esto es lo que se llama parámetros numerales. Son muy cómodos, pero hay otra alternativa que debes conocer: los parámetros nominales. En este caso, en los métodos PDOStatement::bindParam() y PDOStatement::bindValue() el primer argumento no son números consecutivos, sino referencias precedidas por el signo : y que también se incluyen en la consulta, en lugar de los comodines, cómo vemos en nominales.php. Es el mismo código que antes, pero cambiando los parámetros nominales por numerales.

Presta especial atención a las líneas resaltadas. Observa cómo, en la consulta, se han empleado referencias nominales en lugar de comodines, y las mismas se usan en la asignación de valores y parámetros en el objeto $hacerConsulta.

Ahora vamos a ver cómo hacer consultas sin recuperación de datos. Empezaremos agragando un nuevo contacto usando sentencias preparadas. El listado se llama nuevo_con_numerales.php;

Poco te voy a comentar ya nuevo. Sólo comprueba el funcionamiento y examina el listado. En la consulta ves que hemos sustituido los valores por comodines y, después, asignado valores reales a los mismos. Esto puede hacerse igual con parámetros nominales, cómo vemos a continuación en nuevo_con_nominales.php:

Y ¿Cuándo usar parámetros numerales o nominales? Bien. Yo te diría que en consultas cortas, con pocos parámetros, en las que se vea todo claro a primer golpe de vista, emplees parámetros numerales, ya que producen un código más corto y compacto. En consultas “enrevesadas”, largas, en las que haya uniones o impliquen varias tablas, emplea parámetros nominales ya que, con los nombres de referencias, queda más claro y es más fácil seguir el código.

EL ÚLTIMO REGISTRO

El acceso a base de datos PDO presenta un peculiaridad muy interesante. A todos nos ha pasado: cuando insertas un registro en una tabla que tiene un campo autoincrementable cómo clave primaria, necesitas recuperar el valor de ese campo (llamémosle id por convencionalismo), para “hacer algo con él”. Por ejemplo, ese valor, puede ser clave foránea en otra tabla, y necesitas conocer cual es su valor.

Tradicionalmente, lo que hacemos es lanzar una consulta de selección que nos devuelva el valor de la calve primaria del último registro de la tabla. El problema es que, aparte del engorro programático que eso supone, si la tabla tiene muchas concurrencias es posible que el valor que estemos leyendo ya no sea el del último registro que hemos introducido, sino el del último que haya introducido otro usuario.

Afortunadamente, PDO nos proporciona el método PDO::lastInsertId(), que nos devuelve la clave primaria del último registro que hemos guardado con nuestra instancia de PDO, por lo que, ni es necesario lanzar más consultas, ni va a interferir lo que haya grabado, mientras tanto, otro usuario, ya que él está ejecutando otra instancia diferente de PDO. Simplemente, usa, tras la grabación del nuevo registro, una sentencia como la siguiente:

$ultimo_id_insertado = $conexion->lastInsertId();

Suponemos, en este ejemplo que $conexion es el objeto PDO que estamos usando, cómo le hemos llamado en todo este artículo.

     

7 comentarios:

  1. Pingback: PHP-TUT-22 Bases de datos SQL (I) » eldesvandejose.com

  2. Pingback: PHP-TUT-23 Bases de datos SQL (II) » eldesvandejose.com

  3. Pingback: El plugin DataTables (V) – El desván de Jose

  4. Pingback: El editor de DataTables (IV). Datos de múltiples tablas. – El desván de Jose

  5. Pingback: Uso de MySQL con PDO – PHP (II). Tratamiento de excepciones. – El desván de Jose

  6. Pingback: Uniendo tablas MySQL (I). Introducción. – Recursos para programadores

  7. Rene Quintanilla

    Necesito recuperar los datos de una base de datos MySQL que está en un sitio Web-A, desde otro sitio Web-B, he dado permisos para conectarse remotamente, pero no logro que se conecte y me recupere los datos. Solicito su ayuda.

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *