Por qué PDO
Hasta ahora el catálogo vive en archivos y arrays. En un sistema real vive en una base de datos: sobrevive a reinicios, la consultan varias personas a la vez y respeta reglas (claves únicas, claves foráneas).
PDO (PHP Data Objects) es la forma estándar de hablar con la base desde PHP. No es la única, pero sí la recomendada: sirve para MariaDB, MySQL, PostgreSQL y SQLite con el mismo código, y su API de consultas preparadas es la que protege contra la inyección SQL.
Todo el curso usa MariaDB, y PDO la trata con el driver mysql. La diferencia entre «MySQL» y «MariaDB» es de motor; desde PHP se conectan igual.
Conectar
La conexión se hace una sola vez, en su propio archivo, y se trae con require:
<?php
// conexion.php
$host = 'localhost';
$db = 'tienda';
$user = 'root';
$pass = '';
try {
$pdo = new PDO(
"mysql:host=$host;dbname=$db;charset=utf8mb4",
$user,
$pass,
[
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
]
);
} catch (PDOException $e) {
die('No se pudo conectar: ' . $e->getMessage());
}
El DSN dice gestor, servidor, base y codificación. ERRMODE_EXCEPTION hace que los errores avisen con una excepción en lugar de pasar en silencio, y FETCH_ASSOC devuelve cada fila como array con nombres de columna.
Consultas preparadas
Nunca se arma el SQL pegando valores del usuario. Se usa un marcador y el valor viaja aparte:
// MAL: el valor se pega en el texto
$sql = "SELECT * FROM productos WHERE nombre = '$nombre'";
// BIEN: marcador posicional
$consulta = $pdo->prepare('SELECT * FROM productos WHERE nombre = ?');
$consulta->execute([$nombre]);
// BIEN: marcador con nombre
$consulta = $pdo->prepare('SELECT * FROM productos WHERE nombre = :nombre');
$consulta->execute(['nombre' => $nombre]);
La consulta preparada le dice a la base «esto es código, esto es un valor», y el valor nunca se interpreta como SQL. Es la defensa real contra la inyección SQL.
Leer datos
Tres formas de leer, según lo que se espere:
$consulta = $pdo->query('SELECT * FROM productos');
$uno = $consulta->fetch(); // una fila, o false
$todos = $consulta->fetchAll(); // todas las filas
foreach ($todos as $producto) {
echo $producto['nombre'];
}
fetch() avanza el cursor. Si se llama dos veces seguidas, la segunda devuelve la fila siguiente. Para recorrer todo se usa fetchAll() o un while ($fila = $consulta->fetch()).
Insertar y transacciones
Para guardar se usa el mismo prepare + execute, y el id del registro nuevo lo da lastInsertId():
$alta = $pdo->prepare('INSERT INTO productos (nombre, precio, stock) VALUES (?, ?, ?)');
$alta->execute(['Yerba mate 1 kg', 1850.50, 24]);
$idNuevo = $pdo->lastInsertId();
Una transacción agrupa varias operaciones: o se guardan todas, o no se guarda ninguna. Es lo que hace falta al registrar una venta, porque hay que insertar la venta y descontar el stock:
$pdo->beginTransaction();
try {
$venta = $pdo->prepare('INSERT INTO ventas (fecha, cliente_id, total) VALUES (?, ?, ?)');
$venta->execute([date('Y-m-d'), $clienteId, $total]);
$ventaId = $pdo->lastInsertId();
foreach ($items as $item) {
$detalle = $pdo->prepare('INSERT INTO venta_detalle (venta_id, producto_id, cantidad) VALUES (?, ?, ?)');
$detalle->execute([$ventaId, $item['producto_id'], $item['cantidad']]);
$baja = $pdo->prepare('UPDATE productos SET stock = stock - ? WHERE id = ?');
$baja->execute([$item['cantidad'], $item['producto_id']]);
}
$pdo->commit();
} catch (Throwable $e) {
$pdo->rollBack();
throw $e;
}
Resumen
- PDO es la forma estándar de hablar con la base; el DSN indica gestor, servidor, base y codificación.
ERRMODE_EXCEPTIONhace que los errores avisen;FETCH_ASSOCdevuelve filas con nombres de columna.- Siempre consultas preparadas: el valor viaja aparte del SQL.
fetch()una fila,fetchAll()todas; el cursor avanza con cadafetch.lastInsertId()da el id del registro insertado.- Una transacción agrupa operaciones:
commitsi todo salió bien,rollBacksi algo falló.
En la próxima lección el proyecto se ordena: separar datos, lógica y presentación, y un único punto de entrada para todas las páginas.
Licencia. El material de esta página es elaboración propia de Prof Mauro Bobyk —a partir de las fuentes que detalla el pie del sitio— y se publica bajo una licencia Creative Commons BY-NC-SA 4.0.