Ir al contenido principal

Lección 6 · Base de datos con PDO

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'];
}

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_EXCEPTION hace que los errores avisen; FETCH_ASSOC devuelve filas con nombres de columna.
  • Siempre consultas preparadas: el valor viaja aparte del SQL.
  • fetch() una fila, fetchAll() todas; el cursor avanza con cada fetch.
  • lastInsertId() da el id del registro insertado.
  • Una transacción agrupa operaciones: commit si todo salió bien, rollBack si 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.

Autoevaluación de la lección 6

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.