Hasta ahora nuestros ejemplos de PHP trabajaron con datos que existían solamente mientras se ejecutaba el script. En una aplicación real normalmente necesitamos guardar información de manera permanente.
Para eso vamos a conectar PHP con MySQL utilizando PDO. Esta guía se concentra en la conexión, las consultas de lectura y las consultas preparadas. En la siguiente veremos altas, modificaciones y eliminaciones.
¿Todavía no manejás SQL?
En esta guía utilizaremos instrucciones como CREATE TABLE, SELECT, WHERE y ORDER BY, pero no vamos a repetir toda su teoría. Para aprenderlas desde cero podés seguir nuestra ruta completa:
👉 Ruta de aprendizaje SQL / MySQL
¿Qué papel cumple MySQL?
PHP ejecuta la lógica de nuestra aplicación. MySQL se encarga de almacenar y consultar los datos.
Navegador
↓
PHP
↓
PDO
↓
MySQL
↓
PDO
↓
PHP
↓
HTML
↓
Navegador
PDO funciona como la capa que utilizaremos desde PHP para comunicarnos con la base de datos.
¿Qué es PDO?
PDO significa PHP Data Objects. Es una extensión de PHP que ofrece una interfaz consistente para trabajar con bases de datos.
Entre otras cosas nos permite:
- conectarnos a MySQL;
- ejecutar consultas;
- obtener resultados;
- utilizar consultas preparadas;
- manejar errores mediante excepciones;
- trabajar de una forma más segura con datos recibidos del usuario.
La tabla que utilizaremos
Para los ejemplos vamos a trabajar con una base llamada club_programador y una tabla usuarios.
CREATE DATABASE club_programador;
USE club_programador;
CREATE TABLE usuarios (
id INT AUTO_INCREMENT PRIMARY KEY,
nombre VARCHAR(100) NOT NULL,
email VARCHAR(150) NOT NULL UNIQUE,
edad INT
);
Recordatorio: si conceptos como clave primaria, AUTO_INCREMENT, VARCHAR o restricciones todavía no te resultan familiares, están desarrollados en la ruta SQL / MySQL.
Datos de ejemplo
INSERT INTO usuarios (nombre, email, edad)
VALUES
('Ana', 'ana@email.com', 25),
('Juan', 'juan@email.com', 31),
('Lucía', 'lucia@email.com', 22);
Conectarse a MySQL con PDO
Creamos un archivo conexion.php:
<?php
$host = 'localhost';
$base = 'club_programador';
$usuario = 'root';
$password = '';
$dsn = "mysql:host=$host;dbname=$base;charset=utf8mb4";
$pdo = new PDO(
$dsn,
$usuario,
$password
);
¿Qué es el DSN?
El DSN describe cómo debe conectarse PDO.
mysql:host=localhost;dbname=club_programador;charset=utf8mb4
Sus partes principales son:
mysql: controlador que utilizaremos;
host: servidor donde está MySQL;
dbname: nombre de la base;
charset: codificación utilizada en la conexión.
Usar utf8mb4
Para aplicaciones nuevas conviene trabajar con utf8mb4, ya que permite almacenar correctamente Unicode completo.
Manejar errores con excepciones
La conexión puede fallar por muchos motivos: servidor apagado, contraseña incorrecta, base inexistente, etc.
Podemos pedirle a PDO que lance excepciones:
$pdo->setAttribute(
PDO::ATTR_ERRMODE,
PDO::ERRMODE_EXCEPTION
);
Y manejar la conexión mediante try/catch:
<?php
$host = 'localhost';
$base = 'club_programador';
$usuario = 'root';
$password = '';
$dsn =
"mysql:host=$host;dbname=$base;charset=utf8mb4";
try {
$pdo = new PDO(
$dsn,
$usuario,
$password
);
$pdo->setAttribute(
PDO::ATTR_ERRMODE,
PDO::ERRMODE_EXCEPTION
);
echo 'Conexión correcta';
} catch (PDOException $e) {
echo 'No se pudo conectar a la base de datos';
}
En producción no conviene mostrar al usuario detalles internos de la excepción.
Configurar el modo de resultados
También podemos configurar PDO para que devuelva filas como arrays asociativos:
$pdo->setAttribute(
PDO::ATTR_DEFAULT_FETCH_MODE,
PDO::FETCH_ASSOC
);
Así una fila puede verse como:
[
'id' => 1,
'nombre' => 'Ana',
'email' => 'ana@email.com',
'edad' => 25
]
Archivo conexion.php recomendado
<?php
$host = 'localhost';
$base = 'club_programador';
$usuario = 'root';
$password = '';
$dsn =
"mysql:host=$host;dbname=$base;charset=utf8mb4";
try {
$pdo = new PDO(
$dsn,
$usuario,
$password,
[
PDO::ATTR_ERRMODE =>
PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE =>
PDO::FETCH_ASSOC
]
);
} catch (PDOException $e) {
exit('No se pudo conectar a la base de datos');
}
Primer SELECT desde PHP
Una vez conectados podemos ejecutar una consulta:
$sql = 'SELECT id, nombre, email, edad
FROM usuarios';
$stmt = $pdo->query($sql);
query() ejecuta la consulta y devuelve un objeto que representa el resultado.
La sentencia SELECT está explicada en detalle, junto con filtros, ordenamiento y agregaciones, dentro de la ruta de SQL / MySQL.
Obtener una fila con fetch()
$usuario = $stmt->fetch();
echo $usuario['nombre'];
fetch() obtiene una fila del resultado.
Obtener todas las filas con fetchAll()
$usuarios = $stmt->fetchAll();
Ahora $usuarios es un array que contiene todas las filas.
Recorrer los resultados
foreach ($usuarios as $usuario) {
echo $usuario['nombre'];
echo '<br>';
}
Mostrar resultados dentro de HTML
<?php
require 'conexion.php';
$sql =
'SELECT id, nombre, email, edad
FROM usuarios
ORDER BY nombre';
$stmt = $pdo->query($sql);
$usuarios = $stmt->fetchAll();
?>
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<title>Usuarios</title>
</head>
<body>
<h1>Usuarios</h1>
<ul>
<?php foreach ($usuarios as $usuario): ?>
<li>
<?php echo htmlspecialchars(
$usuario['nombre'],
ENT_QUOTES,
'UTF-8'
); ?>
-
<?php echo htmlspecialchars(
$usuario['email'],
ENT_QUOTES,
'UTF-8'
); ?>
</li>
<?php endforeach; ?>
</ul>
</body>
</html>
Separar conexión y consulta
Una estructura sencilla puede quedar así:
mi-proyecto/
│
├── conexion.php
├── usuarios.php
└── index.php
La conexión queda centralizada y los demás archivos pueden reutilizarla mediante:
require 'conexion.php';
¿Cuándo usar query()?
query() resulta práctico cuando la consulta es completamente fija y no contiene datos proporcionados por el usuario.
$stmt = $pdo->query(
'SELECT id, nombre
FROM usuarios
ORDER BY nombre'
);
El problema aparece cuando necesitamos incorporar valores dinámicos.
El error que debemos evitar
Supongamos que recibimos un ID desde la URL.
No debemos construir la consulta concatenando directamente ese valor:
$id = $_GET['id'];
$sql =
"SELECT *
FROM usuarios
WHERE id = " . $id;
Además de producir código frágil, concatenar datos externos dentro de SQL puede abrir la puerta a inyección SQL.
¿Qué es una inyección SQL?
Ocurre cuando un dato controlado por un usuario modifica la estructura de una consulta SQL de una forma que la aplicación no esperaba.
La solución habitual es utilizar consultas preparadas.
Consultas preparadas con prepare()
$sql =
'SELECT id, nombre, email, edad
FROM usuarios
WHERE id = :id';
$stmt = $pdo->prepare($sql);
:id es un placeholder: un lugar reservado para un valor.
Ejecutar la consulta preparada
$stmt->execute([
'id' => 1
]);
$usuario = $stmt->fetch();
PDO se encarga de enviar la consulta y el valor de forma apropiada.
Ejemplo completo: buscar por ID
<?php
require 'conexion.php';
$id = $_GET['id'] ?? null;
if ($id === null) {
exit('Falta el ID');
}
$sql =
'SELECT id, nombre, email, edad
FROM usuarios
WHERE id = :id';
$stmt = $pdo->prepare($sql);
$stmt->execute([
'id' => $id
]);
$usuario = $stmt->fetch();
if (!$usuario) {
exit('Usuario no encontrado');
}
?>
<h1>
<?php echo htmlspecialchars(
$usuario['nombre'],
ENT_QUOTES,
'UTF-8'
); ?>
</h1>
<p>
Email:
<?php echo htmlspecialchars(
$usuario['email'],
ENT_QUOTES,
'UTF-8'
); ?>
</p>
Filtrar por email
$sql =
'SELECT id, nombre, email
FROM usuarios
WHERE email = :email';
$stmt = $pdo->prepare($sql);
$stmt->execute([
'email' => $email
]);
$usuario = $stmt->fetch();
Buscar por nombre con LIKE
$sql =
'SELECT id, nombre, email
FROM usuarios
WHERE nombre LIKE :nombre
ORDER BY nombre';
$stmt = $pdo->prepare($sql);
$stmt->execute([
'nombre' => '%' . $busqueda . '%'
]);
$usuarios = $stmt->fetchAll();
Si querés entender mejor operadores como LIKE, filtros con WHERE, ordenamiento con ORDER BY y demás herramientas SQL, consultá la ruta SQL / MySQL. En este curso de PHP nos concentramos en cómo ejecutar esas consultas desde PHP.
query() frente a prepare()
Una regla sencilla para comenzar:
- si la consulta es fija y no incorpora datos externos,
query() puede ser suficiente;
- si la consulta contiene valores variables, especialmente datos del usuario, preferí
prepare() y execute().
PDO no reemplaza la validación
Las consultas preparadas ayudan a separar datos y SQL, pero todavía debemos validar los datos según las reglas de nuestra aplicación.
Por ejemplo, si esperamos un ID entero:
$id = filter_input(
INPUT_GET,
'id',
FILTER_VALIDATE_INT
);
if ($id === false || $id === null) {
exit('ID no válido');
}
Flujo completo de una consulta
1. El navegador solicita usuarios.php
2. PHP carga conexion.php
3. PDO se conecta a MySQL
4. PHP prepara o ejecuta un SELECT
5. MySQL procesa la consulta
6. MySQL devuelve las filas
7. PDO convierte los resultados
8. PHP trabaja con arrays
9. PHP genera HTML
10. El navegador recibe el HTML
Ejemplo práctico: listado con buscador
<?php
require 'conexion.php';
$busqueda = trim($_GET['q'] ?? '');
if ($busqueda === '') {
$stmt = $pdo->query(
'SELECT id, nombre, email
FROM usuarios
ORDER BY nombre'
);
} else {
$stmt = $pdo->prepare(
'SELECT id, nombre, email
FROM usuarios
WHERE nombre LIKE :busqueda
ORDER BY nombre'
);
$stmt->execute([
'busqueda' => '%' . $busqueda . '%'
]);
}
$usuarios = $stmt->fetchAll();
?>
<form method="get">
<input
type="text"
name="q"
value="<?php echo htmlspecialchars(
$busqueda,
ENT_QUOTES,
'UTF-8'
); ?>"
>
<button type="submit">
Buscar
</button>
</form>
<ul>
<?php foreach ($usuarios as $usuario): ?>
<li>
<?php echo htmlspecialchars(
$usuario['nombre'],
ENT_QUOTES,
'UTF-8'
); ?>
-
<?php echo htmlspecialchars(
$usuario['email'],
ENT_QUOTES,
'UTF-8'
); ?>
</li>
<?php endforeach; ?>
</ul>
Qué estamos reutilizando de guías anteriores
Este ejemplo combina muchos conceptos que ya aprendimos:
- formularios GET;
$_GET;
- arrays;
- condicionales;
foreach;
- funciones incorporadas;
htmlspecialchars();
- validación;
- HTML y PHP en un mismo archivo.
Ahora agregamos una nueva pieza: persistencia mediante MySQL.
Errores comunes
- escribir mal el host, base, usuario o contraseña;
- olvidar especificar
utf8mb4;
- mostrar detalles internos de excepciones a usuarios finales;
- concatenar valores recibidos del usuario dentro de SQL;
- confundir
fetch() con fetchAll();
- olvidar ejecutar una consulta preparada;
- suponer que una consulta siempre devuelve filas;
- mostrar resultados en HTML sin escaparlos;
- creer que PDO reemplaza la validación de los datos.
Buenas prácticas
- centralizar la conexión;
- utilizar
utf8mb4;
- trabajar con excepciones;
- preferir arrays asociativos para resultados;
- usar consultas preparadas cuando intervienen valores variables;
- validar entradas antes de utilizarlas;
- escapar datos cuando se imprimen en HTML;
- mantener separada la teoría SQL de la lógica PHP cuando sea posible.
Ejercicios
- crear la base
club_programador;
- crear la tabla
usuarios;
- insertar cinco usuarios de prueba;
- crear
conexion.php;
- listar todos los usuarios;
- mostrar solamente nombre y email;
- ordenar por nombre;
- buscar un usuario por ID mediante una consulta preparada;
- buscar por email;
- crear un buscador por nombre con
LIKE;
- mostrar un mensaje cuando no existen resultados;
- crear una página de detalle
usuario.php?id=1.
Qué aprendimos
- cómo se relacionan PHP y MySQL;
- qué es PDO;
- qué es un DSN;
- cómo conectarnos a MySQL;
- cómo manejar errores;
- cómo ejecutar un SELECT;
- cómo usar
fetch() y fetchAll();
- cómo recorrer resultados;
- qué son las consultas preparadas;
- qué son los placeholders;
- por qué debemos evitar concatenar datos del usuario dentro de SQL;
- cómo integrar consultas, arrays y HTML.
Continuá aprendiendo SQL / MySQL
En las próximas guías de PHP utilizaremos cada vez más SQL. Siempre que necesites profundizar en una sentencia o concepto de base de datos, podés volver a nuestra ruta específica:
👉 Ver la ruta completa de SQL / MySQL
¿Qué sigue?
En la próxima guía veremos INSERT, UPDATE y DELETE con PHP y PDO: cómo guardar formularios en MySQL, modificar registros existentes y eliminar datos utilizando consultas preparadas.
¿Te sirvió esta guía? ☕
Si este contenido te ayudó y querés apoyar a Club Programador, podés colaborar mediante:
☕ Apoyar con Mercado Pago
🌎 Apoyar con PayPal