20 ejercicios intermedios de PHP resueltos: arrays, funciones, formularios y PDO

Si ya dominás variables, condicionales, bucles y funciones, estos 20 ejercicios intermedios de PHP resueltos te van a ayudar a combinar conceptos y acercarte a problemas reales.

1. Filtrar números pares de un array

<?php
$numeros = [1,2,3,4,5,6,7,8];

$pares = array_filter($numeros, fn($n) => $n % 2 === 0);

print_r($pares);
?>

2. Ordenar productos por precio

<?php
$productos = [
    ['nombre' => 'Mouse', 'precio' => 15000],
    ['nombre' => 'Monitor', 'precio' => 180000],
    ['nombre' => 'Teclado', 'precio' => 25000]
];

usort($productos, fn($a, $b) => $a['precio'] <=> $b['precio']);

print_r($productos);
?>

3. Buscar un usuario por email

<?php
$usuarios = [
    ['nombre' => 'Ana', 'email' => 'ana@mail.com'],
    ['nombre' => 'Juan', 'email' => 'juan@mail.com']
];

$emailBuscado = 'juan@mail.com';

$resultado = array_filter($usuarios, fn($u) => $u['email'] === $emailBuscado);

print_r($resultado);
?>

4. Eliminar duplicados

<?php
$valores = [1,2,2,3,4,4,5];

$unicos = array_unique($valores);

print_r($unicos);
?>

5. Contar palabras de un texto

<?php
$texto = 'PHP es simple de aprender y muy utilizado';

echo str_word_count($texto);
?>

6. Validar un email

<?php
$email = 'usuario@email.com';

if (filter_var($email, FILTER_VALIDATE_EMAIL)) {
    echo 'Email válido';
} else {
    echo 'Email inválido';
}
?>

7. Validar un formulario POST

<?php
$nombre = trim($_POST['nombre'] ?? '');

if ($nombre === '') {
    echo 'El nombre es obligatorio';
}
?>

8. Mantener valores del formulario

<input
    type="text"
    name="nombre"
    value="<?= htmlspecialchars($_POST['nombre'] ?? '') ?>"
>

9. Generar una contraseña aleatoria

<?php
$caracteres = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
$password = '';

for ($i = 0; $i < 12; $i++) {
    $password .= $caracteres[random_int(0, strlen($caracteres) - 1)];
}

echo $password;
?>

10. Guardar una contraseña con password_hash()

<?php
$password = 'ClaveSegura123';

$hash = password_hash($password, PASSWORD_DEFAULT);

echo $hash;
?>

11. Comprobar una contraseña

<?php
if (password_verify($password, $hash)) {
    echo 'Contraseña correcta';
}
?>

12. Crear una sesión de usuario

<?php
session_start();

$_SESSION['usuario_id'] = 10;
$_SESSION['nombre'] = 'Ana';
?>

13. Proteger una página con sesión

<?php
session_start();

if (!isset($_SESSION['usuario_id'])) {
    header('Location: login.php');
    exit;
}
?>

14. Leer un archivo de texto

<?php
$contenido = file_get_contents('datos.txt');

echo nl2br(htmlspecialchars($contenido));
?>

15. Guardar datos en un archivo

<?php
$texto = "Nueva línea
";

file_put_contents(
    'datos.txt',
    $texto,
    FILE_APPEND
);
?>

16. Conectarse a MySQL con PDO

<?php
$pdo = new PDO(
    'mysql:host=localhost;dbname=tienda;charset=utf8mb4',
    'root',
    ''
);

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

17. Buscar un registro con una consulta preparada

<?php
$stmt = $pdo->prepare(
    'SELECT * FROM productos WHERE id = :id'
);

$stmt->execute([
    'id' => 5
]);

$producto = $stmt->fetch(PDO::FETCH_ASSOC);
?>

18. Insertar un producto con PDO

<?php
$stmt = $pdo->prepare(
    'INSERT INTO productos (nombre, precio)
     VALUES (:nombre, :precio)'
);

$stmt->execute([
    'nombre' => 'Teclado',
    'precio' => 25000
]);
?>

19. Crear una paginación básica

<?php
$pagina = max(1, (int) ($_GET['pagina'] ?? 1));
$porPagina = 10;
$offset = ($pagina - 1) * $porPagina;

$stmt = $pdo->prepare(
    'SELECT * FROM productos LIMIT :limite OFFSET :offset'
);

$stmt->bindValue(':limite', $porPagina, PDO::PARAM_INT);
$stmt->bindValue(':offset', $offset, PDO::PARAM_INT);
$stmt->execute();
?>

20. Crear un filtro de búsqueda

<?php
$buscar = trim($_GET['buscar'] ?? '');

$stmt = $pdo->prepare(
    'SELECT * FROM productos
     WHERE nombre LIKE :buscar'
);

$stmt->execute([
    'buscar' => '%' . $buscar . '%'
]);

$productos = $stmt->fetchAll(PDO::FETCH_ASSOC);
?>

¿Qué sigue?

Si podés resolver estos ejercicios sin copiar las soluciones, ya estás listo para trabajar con desafíos avanzados: POO, seguridad, relaciones entre clases, arquitectura MVC y proyectos completos.

Serie de ejercicios de PHP

También podés ver todos los contenidos prácticos en la sección Ejercicios resueltos.

¿Te sirvió esta guía? ☕

☕ Apoyar con Mercado Pago

🌎 Apoyar con PayPal

CRUD completo con PHP, MySQL y PDO: crear, listar, editar, eliminar y buscar

Ya vimos cómo conectar PHP con MySQL mediante PDO, cómo leer datos y cómo ejecutar INSERT, UPDATE y DELETE.

Ahora vamos a integrar todo eso en una pequeña aplicación funcional. Construiremos un CRUD completo de usuarios con listado, búsqueda, alta, edición y eliminación.

CRUD es el acrónimo de:

Create  → crear
Read    → leer
Update  → actualizar
Delete  → eliminar

Para profundizar en SQL: en este proyecto utilizamos SELECT, WHERE, LIKE, INSERT, UPDATE y DELETE. La teoría de esas sentencias está desarrollada en nuestra ruta de aprendizaje SQL / MySQL.

Qué vamos a construir

La aplicación tendrá estas pantallas:

  • index.php: listado y búsqueda de usuarios;
  • crear.php: alta de usuarios;
  • editar.php: modificación;
  • eliminar.php: eliminación mediante POST;
  • conexion.php: conexión centralizada mediante PDO.

Estructura del proyecto

crud-usuarios/
│
├── conexion.php
├── index.php
├── crear.php
├── editar.php
└── eliminar.php

Base de datos

Seguiremos usando la base club_programador y la tabla usuarios:

CREATE TABLE usuarios (
    id INT AUTO_INCREMENT PRIMARY KEY,
    nombre VARCHAR(100) NOT NULL,
    email VARCHAR(150) NOT NULL UNIQUE,
    edad INT
);

1. conexion.php

Centralizamos la conexión para no repetirla en cada archivo.

<?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'
    );
}

2. index.php: listado y buscador

La página principal mostrará todos los usuarios y permitirá buscar por nombre o email.

<?php

require 'conexion.php';

$busqueda = trim($_GET['q'] ?? '');

if ($busqueda === '') {

    $sql =
        'SELECT id, nombre, email, edad
         FROM usuarios
         ORDER BY nombre';

    $stmt = $pdo->query($sql);

} else {

    $sql =
        'SELECT id, nombre, email, edad
         FROM usuarios
         WHERE nombre LIKE :busqueda
            OR email LIKE :busqueda
         ORDER BY nombre';

    $stmt = $pdo->prepare($sql);

    $stmt->execute([
        'busqueda' => '%' . $busqueda . '%'
    ]);
}

$usuarios = $stmt->fetchAll();

$mensaje = $_GET['mensaje'] ?? '';

?>

<!DOCTYPE html>
<html lang="es">
<head>
    <meta charset="UTF-8">
    <title>Usuarios</title>
</head>
<body>

<h1>Usuarios</h1>

<p>
    <a href="crear.php">
        Crear usuario
    </a>
</p>

<?php if ($mensaje === 'creado'): ?>
    <p>Usuario creado correctamente.</p>
<?php elseif ($mensaje === 'actualizado'): ?>
    <p>Usuario actualizado correctamente.</p>
<?php elseif ($mensaje === 'eliminado'): ?>
    <p>Usuario eliminado correctamente.</p>
<?php endif; ?>

<form method="get">

    <input
        type="text"
        name="q"
        placeholder="Buscar..."
        value="<?php echo htmlspecialchars(
            $busqueda,
            ENT_QUOTES,
            'UTF-8'
        ); ?>"
    >

    <button type="submit">
        Buscar
    </button>

</form>

<hr>

<?php if (empty($usuarios)): ?>

    <p>No se encontraron usuarios.</p>

<?php else: ?>

    <table border="1" cellpadding="8">

        <thead>
        <tr>
            <th>ID</th>
            <th>Nombre</th>
            <th>Email</th>
            <th>Edad</th>
            <th>Acciones</th>
        </tr>
        </thead>

        <tbody>

        <?php foreach ($usuarios as $usuario): ?>

            <tr>

                <td>
                    <?php echo $usuario['id']; ?>
                </td>

                <td>
                    <?php echo htmlspecialchars(
                        $usuario['nombre'],
                        ENT_QUOTES,
                        'UTF-8'
                    ); ?>
                </td>

                <td>
                    <?php echo htmlspecialchars(
                        $usuario['email'],
                        ENT_QUOTES,
                        'UTF-8'
                    ); ?>
                </td>

                <td>
                    <?php echo htmlspecialchars(
                        $usuario['edad'],
                        ENT_QUOTES,
                        'UTF-8'
                    ); ?>
                </td>

                <td>

                    <a href="editar.php?id=<?php
                        echo $usuario['id'];
                    ?>">
                        Editar
                    </a>

                    <form
                        method="post"
                        action="eliminar.php"
                        style="display:inline"
                        onsubmit="return confirm(
                            '¿Eliminar este usuario?'
                        );"
                    >

                        <input
                            type="hidden"
                            name="id"
                            value="<?php
                                echo $usuario['id'];
                            ?>"
                        >

                        <button type="submit">
                            Eliminar
                        </button>

                    </form>

                </td>

            </tr>

        <?php endforeach; ?>

        </tbody>

    </table>

<?php endif; ?>

</body>
</html>

Qué hace index.php

La página combina varias cosas que ya aprendimos:

  • recibe un filtro mediante GET;
  • usa una consulta fija cuando no existe búsqueda;
  • usa una consulta preparada cuando existe un valor dinámico;
  • recupera los resultados con fetchAll();
  • recorre los registros con foreach;
  • escapa los datos antes de mostrarlos en HTML;
  • ofrece acciones de editar y eliminar.

Para profundizar en SQL: en este proyecto utilizamos SELECT, WHERE, LIKE, INSERT, UPDATE y DELETE. La teoría de esas sentencias está desarrollada en nuestra ruta de aprendizaje SQL / MySQL.

3. crear.php

Esta pantalla permitirá agregar usuarios nuevos.

<?php

require 'conexion.php';

$nombre = '';
$email = '';
$edad = '';

$errores = [];

if ($_SERVER['REQUEST_METHOD'] === 'POST') {

    $nombre = trim($_POST['nombre'] ?? '');
    $email = trim($_POST['email'] ?? '');
    $edad = trim($_POST['edad'] ?? '');

    if ($nombre === '') {
        $errores[] =
            'El nombre es obligatorio';
    }

    if (!filter_var(
        $email,
        FILTER_VALIDATE_EMAIL
    )) {
        $errores[] =
            'El email no es válido';
    }

    if (
        $edad !== '' &&
        filter_var(
            $edad,
            FILTER_VALIDATE_INT
        ) === false
    ) {
        $errores[] =
            'La edad debe ser un número entero';
    }

    if (empty($errores)) {

        $sql =
            'INSERT INTO usuarios
                (nombre, email, edad)
             VALUES
                (:nombre, :email, :edad)';

        $stmt = $pdo->prepare($sql);

        $stmt->execute([
            'nombre' => $nombre,
            'email' => $email,
            'edad' =>
                $edad === ''
                ? null
                : (int) $edad
        ]);

        header(
            'Location: index.php?mensaje=creado'
        );

        exit;
    }
}

?>

<!DOCTYPE html>
<html lang="es">
<head>
    <meta charset="UTF-8">
    <title>Crear usuario</title>
</head>
<body>

<h1>Crear usuario</h1>

<?php if (!empty($errores)): ?>

    <ul>
        <?php foreach ($errores as $error): ?>
            <li>
                <?php echo htmlspecialchars(
                    $error,
                    ENT_QUOTES,
                    'UTF-8'
                ); ?>
            </li>
        <?php endforeach; ?>
    </ul>

<?php endif; ?>

<form method="post">

    <label for="nombre">
        Nombre:
    </label>

    <input
        type="text"
        id="nombre"
        name="nombre"
        value="<?php echo htmlspecialchars(
            $nombre,
            ENT_QUOTES,
            'UTF-8'
        ); ?>"
    >

    <br><br>

    <label for="email">
        Email:
    </label>

    <input
        type="email"
        id="email"
        name="email"
        value="<?php echo htmlspecialchars(
            $email,
            ENT_QUOTES,
            'UTF-8'
        ); ?>"
    >

    <br><br>

    <label for="edad">
        Edad:
    </label>

    <input
        type="number"
        id="edad"
        name="edad"
        value="<?php echo htmlspecialchars(
            $edad,
            ENT_QUOTES,
            'UTF-8'
        ); ?>"
    >

    <br><br>

    <button type="submit">
        Guardar
    </button>

    <a href="index.php">
        Cancelar
    </a>

</form>

</body>
</html>

Post/Redirect/Get en el alta

Después de insertar el usuario no mostramos directamente una página de éxito.

En cambio hacemos:

header(
    'Location: index.php?mensaje=creado'
);

exit;

Esto evita que el navegador repita el INSERT si el usuario actualiza la página.

4. editar.php

La edición tiene dos etapas:

GET  → cargar el usuario
POST → validar y actualizar

Primero obtenemos y validamos el ID.

$id = filter_input(
    INPUT_GET,
    'id',
    FILTER_VALIDATE_INT
);

if ($id === false || $id === null) {
    exit('ID no válido');
}

Cargar el usuario

$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');
}

editar.php completo

<?php

require 'conexion.php';

$id = filter_input(
    INPUT_GET,
    'id',
    FILTER_VALIDATE_INT
);

if ($id === false || $id === null) {
    exit('ID no válido');
}

$stmt = $pdo->prepare(
    'SELECT id, nombre, email, edad
     FROM usuarios
     WHERE id = :id'
);

$stmt->execute([
    'id' => $id
]);

$usuario = $stmt->fetch();

if (!$usuario) {
    exit('Usuario no encontrado');
}

$nombre = $usuario['nombre'];
$email = $usuario['email'];
$edad = $usuario['edad'];

$errores = [];

if ($_SERVER['REQUEST_METHOD'] === 'POST') {

    $nombre = trim($_POST['nombre'] ?? '');
    $email = trim($_POST['email'] ?? '');
    $edad = trim($_POST['edad'] ?? '');

    if ($nombre === '') {
        $errores[] =
            'El nombre es obligatorio';
    }

    if (!filter_var(
        $email,
        FILTER_VALIDATE_EMAIL
    )) {
        $errores[] =
            'El email no es válido';
    }

    if (
        $edad !== '' &&
        filter_var(
            $edad,
            FILTER_VALIDATE_INT
        ) === false
    ) {
        $errores[] =
            'La edad no es válida';
    }

    if (empty($errores)) {

        $sql =
            'UPDATE usuarios
             SET nombre = :nombre,
                 email = :email,
                 edad = :edad
             WHERE id = :id';

        $stmt = $pdo->prepare($sql);

        $stmt->execute([
            'nombre' => $nombre,
            'email' => $email,
            'edad' =>
                $edad === ''
                ? null
                : (int) $edad,
            'id' => $id
        ]);

        header(
            'Location: index.php?mensaje=actualizado'
        );

        exit;
    }
}

?>

<!DOCTYPE html>
<html lang="es">
<head>
    <meta charset="UTF-8">
    <title>Editar usuario</title>
</head>
<body>

<h1>Editar usuario</h1>

<?php if (!empty($errores)): ?>

    <ul>
        <?php foreach ($errores as $error): ?>
            <li>
                <?php echo htmlspecialchars(
                    $error,
                    ENT_QUOTES,
                    'UTF-8'
                ); ?>
            </li>
        <?php endforeach; ?>
    </ul>

<?php endif; ?>

<form method="post">

    <label for="nombre">
        Nombre:
    </label>

    <input
        type="text"
        id="nombre"
        name="nombre"
        value="<?php echo htmlspecialchars(
            $nombre,
            ENT_QUOTES,
            'UTF-8'
        ); ?>"
    >

    <br><br>

    <label for="email">
        Email:
    </label>

    <input
        type="email"
        id="email"
        name="email"
        value="<?php echo htmlspecialchars(
            $email,
            ENT_QUOTES,
            'UTF-8'
        ); ?>"
    >

    <br><br>

    <label for="edad">
        Edad:
    </label>

    <input
        type="number"
        id="edad"
        name="edad"
        value="<?php echo htmlspecialchars(
            $edad,
            ENT_QUOTES,
            'UTF-8'
        ); ?>"
    >

    <br><br>

    <button type="submit">
        Guardar cambios
    </button>

    <a href="index.php">
        Cancelar
    </a>

</form>

</body>
</html>

5. eliminar.php

La eliminación se ejecutará solamente mediante POST.

<?php

require 'conexion.php';

if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
    exit('Método no permitido');
}

$id = filter_input(
    INPUT_POST,
    'id',
    FILTER_VALIDATE_INT
);

if ($id === false || $id === null) {
    exit('ID no válido');
}

$stmt = $pdo->prepare(
    'DELETE FROM usuarios
     WHERE id = :id'
);

$stmt->execute([
    'id' => $id
]);

header(
    'Location: index.php?mensaje=eliminado'
);

exit;

Por qué DELETE usa POST

Eliminar información cambia el estado de la aplicación. No debería ocurrir simplemente al visitar una URL.

Por eso en index.php utilizamos:

<form
    method="post"
    action="eliminar.php"
>

    <input
        type="hidden"
        name="id"
        value="<?php echo $usuario['id']; ?>"
    >

    <button type="submit">
        Eliminar
    </button>

</form>

Flujo completo del CRUD

LISTAR
index.php
    ↓
SELECT
    ↓
tabla HTML


CREAR
crear.php
    ↓
formulario POST
    ↓
validación
    ↓
INSERT
    ↓
redirect a index.php


EDITAR
editar.php?id=5
    ↓
SELECT
    ↓
formulario precargado
    ↓
POST
    ↓
UPDATE
    ↓
redirect


ELIMINAR
index.php
    ↓
formulario POST
    ↓
eliminar.php
    ↓
DELETE
    ↓
redirect

Buscar forma parte del Read

En un CRUD real, leer datos no significa solamente listar todos los registros.

También suele incluir:

  • búsqueda;
  • filtros;
  • ordenamiento;
  • paginación;
  • vista de detalle.

En nuestro ejemplo incorporamos búsqueda mediante LIKE.

Para profundizar en SQL: en este proyecto utilizamos SELECT, WHERE, LIKE, INSERT, UPDATE y DELETE. La teoría de esas sentencias está desarrollada en nuestra ruta de aprendizaje SQL / MySQL.

Separar responsabilidades

Aunque esta estructura es correcta para aprender, vemos que crear.php y editar.php comienzan a tener bastante código.

En proyectos mayores normalmente separamos:

acceso a datos
validación
lógica de negocio
controladores
vistas

Ese tipo de organización nos prepara para comprender posteriormente frameworks como Laravel.

Un posible siguiente paso: reutilizar el formulario

Crear y editar contienen HTML muy parecido.

Podríamos extraerlo a un archivo común, por ejemplo:

formulario.php

y reutilizarlo desde ambas pantallas.

Ese tipo de mejoras reduce duplicación, aunque en esta guía mantuvimos los archivos completos para que pueda entenderse cada flujo por separado.

Seguridad: lo que ya estamos haciendo

Nuestro CRUD aplica varias prácticas importantes:

  • consultas preparadas;
  • validación de formularios;
  • validación de IDs;
  • escapado de salida con htmlspecialchars();
  • POST para eliminaciones;
  • Post/Redirect/Get.

Lo que todavía falta en una aplicación real

Este CRUD es didáctico. Un sistema de producción todavía debería considerar, entre otras cosas:

  • autenticación de usuarios;
  • autorización y permisos;
  • protección CSRF;
  • registro controlado de errores;
  • validaciones de negocio más completas;
  • manejo de duplicados, por ejemplo emails únicos;
  • paginación para grandes cantidades de registros;
  • separación más clara de responsabilidades.

Qué ocurre si el email ya existe

La base tiene una restricción UNIQUE sobre email. Si intentamos insertar un email duplicado, MySQL rechazará la operación.

Esto muestra algo importante: la validación de PHP y las restricciones de la base de datos trabajan juntas.

La aplicación puede validar primero, pero la base debe seguir protegiendo la integridad de los datos.

Mejoras propuestas para practicar

  • agregar una página de detalle;
  • agregar teléfono;
  • agregar estado activo/inactivo;
  • permitir ordenar por nombre;
  • agregar filtro por edad;
  • agregar paginación;
  • mejorar el diseño con CSS;
  • reutilizar el formulario de crear/editar;
  • manejar el error de email duplicado;
  • agregar un campo de fecha de creación.

Ejercicios

  1. crear los cinco archivos del proyecto;
  2. probar el listado sin usuarios;
  3. crear tres usuarios;
  4. buscar por nombre;
  5. buscar por email;
  6. editar un usuario;
  7. eliminar un usuario;
  8. comprobar que actualizar la página no repite una operación POST;
  9. mostrar un mensaje cuando una búsqueda no devuelve resultados;
  10. agregar una página de detalle;
  11. agregar un campo nuevo a la tabla y a todos los formularios;
  12. implementar una mejora propia al CRUD.

Qué aprendimos

  • qué significa CRUD;
  • cómo organizar un CRUD sencillo en varios archivos;
  • cómo listar datos desde MySQL;
  • cómo implementar búsqueda;
  • cómo crear registros;
  • cómo precargar y editar registros;
  • cómo eliminar mediante POST;
  • cómo utilizar Post/Redirect/Get;
  • cómo integrar formularios, PDO, MySQL y HTML;
  • qué diferencias existen entre un ejemplo didáctico y una aplicación de producción.

Continuá aprendiendo SQL / MySQL

Este CRUD utiliza varias consultas SQL, pero el objetivo de esta guía es integrarlas con PHP. Para profundizar en consultas, filtros, relaciones, índices, normalización y otros temas de base de datos:

👉 Ver la ruta completa de SQL / MySQL

¿Qué sigue?

Después de construir nuestro primer CRUD, el siguiente paso natural es aprender a trabajar con sesiones y autenticación en PHP: iniciar sesión, mantener un usuario autenticado, cerrar sesión y proteger páginas privadas.

¿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

INSERT, UPDATE y DELETE con PHP y PDO: guardar, editar y eliminar datos

En la guía anterior conectamos PHP con MySQL utilizando PDO y aprendimos a leer datos con SELECT.

Ahora vamos a avanzar con las operaciones que permiten crear, modificar y eliminar registros. Es decir, vamos a empezar a trabajar con la parte operativa de una aplicación real.

¿Querés profundizar en la parte SQL?

En esta guía vamos a utilizar INSERT, UPDATE, DELETE y WHERE. La teoría completa de estas sentencias está desarrollada en nuestra ruta de aprendizaje SQL / MySQL.

Las tres operaciones que veremos

  • INSERT: crear registros nuevos;
  • UPDATE: modificar registros existentes;
  • DELETE: eliminar registros.

Las tres operaciones se realizarán con consultas preparadas mediante PDO.

Tabla de ejemplo

Seguimos trabajando con la tabla usuarios:

usuarios
-------------------------
id
nombre
email
edad

Insertar un usuario

La consulta SQL básica sería:

INSERT INTO usuarios (nombre, email, edad)
VALUES ('Ana', 'ana@email.com', 25);

Pero desde PHP no debemos concatenar directamente valores recibidos del usuario.

Utilizamos placeholders:

$sql =
    'INSERT INTO usuarios
        (nombre, email, edad)
     VALUES
        (:nombre, :email, :edad)';

$stmt = $pdo->prepare($sql);

Ejecutar el INSERT

$stmt->execute([
    'nombre' => $nombre,
    'email' => $email,
    'edad' => $edad
]);

Formulario de alta

<form method="post">

    <label for="nombre">Nombre:</label>
    <input
        type="text"
        id="nombre"
        name="nombre"
    >

    <br><br>

    <label for="email">Email:</label>
    <input
        type="email"
        id="email"
        name="email"
    >

    <br><br>

    <label for="edad">Edad:</label>
    <input
        type="number"
        id="edad"
        name="edad"
    >

    <br><br>

    <button type="submit">
        Guardar
    </button>

</form>

Procesar el formulario

<?php

require 'conexion.php';

$errores = [];

if ($_SERVER['REQUEST_METHOD'] === 'POST') {

    $nombre = trim($_POST['nombre'] ?? '');
    $email = trim($_POST['email'] ?? '');
    $edad = trim($_POST['edad'] ?? '');

    if ($nombre === '') {
        $errores[] = 'El nombre es obligatorio';
    }

    if (!filter_var(
        $email,
        FILTER_VALIDATE_EMAIL
    )) {
        $errores[] = 'El email no es válido';
    }

    if ($edad === '' || !is_numeric($edad)) {
        $errores[] = 'La edad no es válida';
    }

    if (empty($errores)) {

        $sql =
            'INSERT INTO usuarios
                (nombre, email, edad)
             VALUES
                (:nombre, :email, :edad)';

        $stmt = $pdo->prepare($sql);

        $stmt->execute([
            'nombre' => $nombre,
            'email' => $email,
            'edad' => $edad
        ]);

        echo 'Usuario guardado correctamente';
    }
}

Recuperar el ID insertado

Si la tabla utiliza una clave autoincremental, PDO puede devolver el último ID generado:

$id = $pdo->lastInsertId();

echo 'Nuevo usuario: ' . $id;

Esto puede ser útil si después queremos redirigir al detalle del registro recién creado.

Modificar un registro con UPDATE

Supongamos que queremos cambiar nombre y email de un usuario.

$sql =
    'UPDATE usuarios
     SET nombre = :nombre,
         email = :email,
         edad = :edad
     WHERE id = :id';

$stmt = $pdo->prepare($sql);

$stmt->execute([
    'nombre' => $nombre,
    'email' => $email,
    'edad' => $edad,
    'id' => $id
]);

¿Querés profundizar en la parte SQL?

En esta guía vamos a utilizar INSERT, UPDATE, DELETE y WHERE. La teoría completa de estas sentencias está desarrollada en nuestra ruta de aprendizaje SQL / MySQL.

¿Por qué WHERE es fundamental en UPDATE?

La cláusula WHERE indica qué registro debe modificarse.

Una consulta como:

UPDATE usuarios
SET nombre = 'Ana';

podría modificar todos los registros de la tabla.

Por eso, antes de ejecutar un UPDATE, debemos revisar con especial cuidado la condición utilizada.

Editar un usuario: primer paso

Normalmente recibimos el ID mediante GET:

editar.php?id=5

Lo validamos:

$id = filter_input(
    INPUT_GET,
    'id',
    FILTER_VALIDATE_INT
);

if ($id === false || $id === null) {
    exit('ID no válido');
}

Buscar el usuario antes de editar

$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');
}

Precargar el formulario

<form method="post">

    <input
        type="text"
        name="nombre"
        value="<?php echo htmlspecialchars(
            $usuario['nombre'],
            ENT_QUOTES,
            'UTF-8'
        ); ?>"
    >

    <input
        type="email"
        name="email"
        value="<?php echo htmlspecialchars(
            $usuario['email'],
            ENT_QUOTES,
            'UTF-8'
        ); ?>"
    >

    <input
        type="number"
        name="edad"
        value="<?php echo htmlspecialchars(
            $usuario['edad'],
            ENT_QUOTES,
            'UTF-8'
        ); ?>"
    >

    <button type="submit">
        Guardar cambios
    </button>

</form>

Procesar la edición

if ($_SERVER['REQUEST_METHOD'] === 'POST') {

    $nombre = trim($_POST['nombre'] ?? '');
    $email = trim($_POST['email'] ?? '');
    $edad = trim($_POST['edad'] ?? '');

    if ($nombre === '') {
        $errores[] = 'El nombre es obligatorio';
    }

    if (!filter_var(
        $email,
        FILTER_VALIDATE_EMAIL
    )) {
        $errores[] = 'El email no es válido';
    }

    if (empty($errores)) {

        $sql =
            'UPDATE usuarios
             SET nombre = :nombre,
                 email = :email,
                 edad = :edad
             WHERE id = :id';

        $stmt = $pdo->prepare($sql);

        $stmt->execute([
            'nombre' => $nombre,
            'email' => $email,
            'edad' => $edad,
            'id' => $id
        ]);

        echo 'Usuario actualizado';
    }
}

Eliminar un registro

La operación DELETE también debe incluir una condición:

DELETE FROM usuarios
WHERE id = 5;

Desde PHP:

$sql =
    'DELETE FROM usuarios
     WHERE id = :id';

$stmt = $pdo->prepare($sql);

$stmt->execute([
    'id' => $id
]);

No eliminar mediante enlaces GET

No conviene hacer algo como:

<a href="eliminar.php?id=5">
    Eliminar
</a>

Un enlace representa naturalmente una navegación. Para una acción destructiva es preferible utilizar un formulario con POST.

Formulario para eliminar

<form
    method="post"
    action="eliminar.php"
>

    <input
        type="hidden"
        name="id"
        value="5"
    >

    <button type="submit">
        Eliminar
    </button>

</form>

Procesar la eliminación

<?php

require 'conexion.php';

if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
    exit('Método no permitido');
}

$id = filter_input(
    INPUT_POST,
    'id',
    FILTER_VALIDATE_INT
);

if ($id === false || $id === null) {
    exit('ID no válido');
}

$sql =
    'DELETE FROM usuarios
     WHERE id = :id';

$stmt = $pdo->prepare($sql);

$stmt->execute([
    'id' => $id
]);

echo 'Usuario eliminado';

Validar que el registro exista

Antes de modificar o eliminar, podemos comprobar que el registro exista.

$stmt = $pdo->prepare(
    'SELECT id
     FROM usuarios
     WHERE id = :id'
);

$stmt->execute([
    'id' => $id
]);

if (!$stmt->fetch()) {
    exit('Usuario no encontrado');
}

Estructura sencilla de archivos

usuarios/
│
├── conexion.php
├── index.php
├── crear.php
├── editar.php
└── eliminar.php

Por ahora es una estructura simple y didáctica. Más adelante veremos cómo organizar aplicaciones más grandes.

index.php

Puede mostrar el listado de usuarios y los botones de acción:

<?php foreach ($usuarios as $usuario): ?>

    <p>

        <?php echo htmlspecialchars(
            $usuario['nombre'],
            ENT_QUOTES,
            'UTF-8'
        ); ?>

        <a href="editar.php?id=<?php
            echo $usuario['id'];
        ?>">
            Editar
        </a>

        <form
            method="post"
            action="eliminar.php"
            style="display:inline"
        >
            <input
                type="hidden"
                name="id"
                value="<?php
                    echo $usuario['id'];
                ?>"
            >

            <button type="submit">
                Eliminar
            </button>
        </form>

    </p>

<?php endforeach; ?>

Mensajes después de una operación

En una aplicación real suele ser más cómodo redirigir después de insertar, actualizar o eliminar.

header(
    'Location: index.php?mensaje=creado'
);

exit;

Luego podemos mostrar un mensaje:

$mensaje = $_GET['mensaje'] ?? '';

if ($mensaje === 'creado') {
    echo 'Usuario creado correctamente';
}

Por qué redirigir después de POST

Después de procesar un formulario, una redirección evita que el navegador intente repetir la operación si el usuario actualiza la página.

Este patrón se conoce habitualmente como:

POST
 ↓
procesamiento
 ↓
redirect
 ↓
GET

También se lo conoce como Post/Redirect/Get.

Una mejora importante: confirmar eliminaciones

Podemos pedir confirmación en el navegador:

<form
    method="post"
    action="eliminar.php"
    onsubmit="return confirm(
        '¿Seguro que querés eliminar este usuario?'
    );"
>

Esto mejora la experiencia, aunque la autorización real de la operación siempre debe controlarse también en el servidor.

Consultas preparadas en todas las operaciones

En las tres operaciones mantenemos el mismo patrón:

$sql = '... :valor ...';

$stmt = $pdo->prepare($sql);

$stmt->execute([
    'valor' => $dato
]);

Esto evita construir SQL concatenando datos externos.

Errores comunes

  • concatenar valores del usuario dentro de SQL;
  • olvidar el WHERE en un UPDATE;
  • olvidar el WHERE en un DELETE;
  • usar GET para ejecutar una eliminación;
  • no validar IDs;
  • no comprobar si el registro existe;
  • no validar el formulario antes de guardar;
  • no utilizar consultas preparadas;
  • actualizar o eliminar registros sin verificar permisos;
  • repetir un POST accidentalmente al recargar la página.

Buenas prácticas

  • validar siempre los datos antes de escribir en la base;
  • utilizar prepare() y execute();
  • validar IDs con filtros apropiados;
  • comprobar la existencia del registro;
  • usar POST para acciones que modifican datos;
  • redireccionar después de operaciones exitosas;
  • escapar datos al mostrarlos en HTML;
  • controlar permisos antes de modificar o eliminar;
  • revisar cuidadosamente las condiciones de UPDATE y DELETE.

Ejercicios

  1. crear un formulario para insertar usuarios;
  2. validar nombre, email y edad;
  3. guardar el usuario mediante PDO;
  4. mostrar el ID generado con lastInsertId();
  5. crear una página de edición;
  6. precargar el formulario con los datos actuales;
  7. actualizar mediante una consulta preparada;
  8. crear un formulario POST para eliminar;
  9. validar el ID recibido;
  10. mostrar mensajes después de cada operación;
  11. aplicar Post/Redirect/Get;
  12. agregar confirmación antes de eliminar.

Qué aprendimos

  • cómo insertar registros con PDO;
  • cómo recuperar el ID insertado;
  • cómo modificar registros con UPDATE;
  • cómo precargar un formulario;
  • cómo eliminar registros;
  • por qué debemos validar IDs;
  • por qué las operaciones destructivas no deberían ejecutarse por GET;
  • cómo combinar formularios, PDO y consultas preparadas;
  • qué es Post/Redirect/Get.

Continuá aprendiendo SQL / MySQL

Para profundizar en INSERT, UPDATE, DELETE, restricciones, relaciones y demás conceptos propios de la base de datos, podés seguir nuestra ruta completa:

👉 Ver la ruta SQL / MySQL

¿Qué sigue?

En la próxima guía vamos a integrar todo lo aprendido en un CRUD completo con PHP, MySQL y PDO: listado, alta, edición, eliminación y búsqueda dentro de una pequeña aplicación funcional.

¿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

PHP y MySQL con PDO: conexión, consultas preparadas y lectura de datos

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

  1. crear la base club_programador;
  2. crear la tabla usuarios;
  3. insertar cinco usuarios de prueba;
  4. crear conexion.php;
  5. listar todos los usuarios;
  6. mostrar solamente nombre y email;
  7. ordenar por nombre;
  8. buscar un usuario por ID mediante una consulta preparada;
  9. buscar por email;
  10. crear un buscador por nombre con LIKE;
  11. mostrar un mensaje cuando no existen resultados;
  12. 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