BD práctica 3 2023 CRUD con PHP y MySQL

 

Tutorial: Operaciones CRUD con PHP y MySQL (Usando MySQLi)



Las operaciones CRUD son los cuatro principios básicos de persistencia de datos: Crear (Create), Leer (Read), Actualizar (Update) y Eliminar (Delete). Son el pilar de la mayoría de las aplicaciones web que interactúan con una base de datos.

Para este tutorial, asumiremos que tienes un entorno de desarrollo web configurado (como XAMPP, WAMP, MAMP, o un servidor LAMP) con PHP y MySQL en funcionamiento.

1. Configuración de la Base de Datos

Primero, necesitamos una base de datos y una tabla para trabajar.

a. Crear la Base de Datos

Puedes usar phpMyAdmin (si usas XAMPP/WAMP) o la línea de comandos de MySQL.

SQL
CREATE DATABASE crud_db;
USE crud_db;
b. Crear la Tabla usuarios

Vamos a crear una tabla simple para almacenar información de usuarios.

SQL
CREATE TABLE usuarios (
    id INT AUTO_INCREMENT PRIMARY KEY,
    nombre VARCHAR(100) NOT NULL,
    email VARCHAR(100) NOT NULL UNIQUE,
    fecha_registro TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

Nota: UNIQUE en email asegura que no haya dos usuarios con el mismo correo electrónico. AUTO_INCREMENT y PRIMARY KEY en id aseguran un identificador único y automático para cada usuario.


2. Archivo de Conexión a la Base de Datos (db_config.php)

Es una buena práctica separar la lógica de conexión a la base de datos en un archivo aparte. Esto facilita la gestión y la seguridad.

Crea un archivo llamado db_config.php:

PHP
<?php

// Definición de las constantes de conexión
define('DB_HOST', 'localhost'); // O la IP de tu servidor de base de datos
define('DB_USER', 'root');     // Usuario de la base de datos (por defecto en XAMPP/WAMP es root)
define('DB_PASS', '');         // Contraseña del usuario (por defecto en XAMPP/WAMP es vacía)
define('DB_NAME', 'crud_db');  // Nombre de la base de datos que creamos

// Establecer la conexión
$conn = new mysqli(DB_HOST, DB_USER, DB_PASS, DB_NAME);

// Verificar la conexión
if ($conn->connect_error) {
    die("Error de conexión a la base de datos: " . $conn->connect_error);
}

// Establecer el juego de caracteres a UTF-8 (importante para caracteres especiales)
$conn->set_charset("utf8");

?>

Seguridad: Para entornos de producción, NUNCA uses root sin contraseña. Crea un usuario con privilegios específicos para tu base de datos y usa una contraseña segura.


3. Operaciones CRUD en PHP

Ahora crearemos archivos PHP separados para cada operación, y un archivo index.php para mostrarlas.

a. Operación CREAR (Create)

Crea un archivo crear.php. Este archivo contendrá un formulario para ingresar nuevos usuarios y la lógica para insertarlos en la base de datos.

PHP
<?php
include 'db_config.php'; // Incluye el archivo de conexión

$mensaje = ''; // Variable para mensajes de retroalimentación

if ($_SERVER["REQUEST_METHOD"] == "POST") {
    // Validar y sanear la entrada del usuario
    $nombre = $conn->real_escape_string($_POST['nombre']);
    $email = $conn->real_escape_string($_POST['email']);

    // Preparar la consulta SQL para inserción
    // Usamos sentencias preparadas para prevenir inyección SQL
    $stmt = $conn->prepare("INSERT INTO usuarios (nombre, email) VALUES (?, ?)");
    $stmt->bind_param("ss", $nombre, $email); // "ss" indica que ambos parámetros son strings

    if ($stmt->execute()) {
        $mensaje = "¡Usuario creado exitosamente!";
    } else {
        $mensaje = "Error al crear usuario: " . $stmt->error;
    }

    $stmt->close(); // Cerrar la sentencia preparada
}

// Cierre de la conexión a la base de datos
$conn->close();
?>

<!DOCTYPE html>
<html lang="es">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Crear Usuario</title>
    <style>
        body { font-family: Arial, sans-serif; margin: 20px; }
        .container { max-width: 600px; margin: auto; padding: 20px; border: 1px solid #ddd; border-radius: 8px; }
        .form-group { margin-bottom: 15px; }
        label { display: block; margin-bottom: 5px; }
        input[type="text"], input[type="email"] { width: calc(100% - 22px); padding: 10px; border: 1px solid #ccc; border-radius: 4px; }
        button { background-color: #4CAF50; color: white; padding: 10px 20px; border: none; border-radius: 4px; cursor: pointer; }
        button:hover { background-color: #45a049; }
        .message { margin-top: 15px; padding: 10px; border-radius: 4px; }
        .success { background-color: #d4edda; color: #155724; border-color: #c3e6cb; }
        .error { background-color: #f8d7da; color: #721c24; border-color: #f5c6cb; }
        a { text-decoration: none; color: #007bff; margin-top: 10px; display: inline-block; }
    </style>
</head>
<body>
    <div class="container">
        <h2>Crear Nuevo Usuario</h2>
        <?php if (!empty($mensaje)): ?>
            <div class="message <?php echo (strpos($mensaje, 'Error') !== false) ? 'error' : 'success'; ?>">
                <?php echo $mensaje; ?>
            </div>
        <?php endif; ?>
        <form action="crear.php" method="POST">
            <div class="form-group">
                <label for="nombre">Nombre:</label>
                <input type="text" id="nombre" name="nombre" required>
            </div>
            <div class="form-group">
                <label for="email">Email:</label>
                <input type="email" id="email" name="email" required>
            </div>
            <button type="submit">Guardar Usuario</button>
        </form>
        <p><a href="index.php">Volver al listado de usuarios</a></p>
    </div>
</body>
</html>

Importante: Las sentencias preparadas ($conn->prepare(), bind_param(), execute()) son cruciales para prevenir ataques de inyección SQL.

b. Operación LEER (Read)

Crea un archivo index.php. Este archivo listará todos los usuarios de la base de datos.

PHP
<?php
include 'db_config.php'; // Incluye el archivo de conexión

// Consulta SQL para seleccionar todos los usuarios
$sql = "SELECT id, nombre, email, fecha_registro FROM usuarios ORDER BY id DESC";
$resultado = $conn->query($sql); // Ejecutar la consulta

// Cierre de la conexión a la base de datos
$conn->close();
?>

<!DOCTYPE html>
<html lang="es">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Lista de Usuarios</title>
    <style>
        body { font-family: Arial, sans-serif; margin: 20px; }
        .container { max-width: 800px; margin: auto; padding: 20px; border: 1px solid #ddd; border-radius: 8px; }
        table { width: 100%; border-collapse: collapse; margin-top: 20px; }
        th, td { border: 1px solid #ddd; padding: 8px; text-align: left; }
        th { background-color: #f2f2f2; }
        .actions a { margin-right: 10px; text-decoration: none; color: #007bff; }
        .actions a.delete { color: #dc3545; }
        .add-button { background-color: #28a745; color: white; padding: 10px 15px; border-radius: 4px; text-decoration: none; display: inline-block; margin-bottom: 15px; }
        .add-button:hover { background-color: #218838; }
    </style>
</head>
<body>
    <div class="container">
        <h2>Listado de Usuarios</h2>
        <a href="crear.php" class="add-button">Crear Nuevo Usuario</a>
        <?php if ($resultado->num_rows > 0): ?>
            <table>
                <thead>
                    <tr>
                        <th>ID</th>
                        <th>Nombre</th>
                        <th>Email</th>
                        <th>Fecha de Registro</th>
                        <th>Acciones</th>
                    </tr>
                </thead>
                <tbody>
                    <?php while($row = $resultado->fetch_assoc()): ?>
                        <tr>
                            <td><?php echo htmlspecialchars($row["id"]); ?></td>
                            <td><?php echo htmlspecialchars($row["nombre"]); ?></td>
                            <td><?php echo htmlspecialchars($row["email"]); ?></td>
                            <td><?php echo htmlspecialchars($row["fecha_registro"]); ?></td>
                            <td class="actions">
                                <a href="editar.php?id=<?php echo htmlspecialchars($row["id"]); ?>">Editar</a>
                                <a href="eliminar.php?id=<?php echo htmlspecialchars($row["id"]); ?>" class="delete" onclick="return confirm('¿Estás seguro de que quieres eliminar este usuario?');">Eliminar</a>
                            </td>
                        </tr>
                    <?php endwhile; ?>
                </tbody>
            </table>
        <?php else: ?>
            <p>No hay usuarios registrados aún.</p>
        <?php endif; ?>
    </div>
</body>
</html>

Nota: htmlspecialchars() es una función vital para prevenir ataques de XSS (Cross-Site Scripting) al mostrar datos provenientes de la base de datos en el navegador.

c. Operación ACTUALIZAR (Update)

Crea un archivo editar.php. Este archivo cargará los datos de un usuario específico para su edición y los actualizará en la base de datos.

PHP
<?php
include 'db_config.php';

$mensaje = '';
$usuario = null; // Variable para almacenar los datos del usuario a editar

// Verificar si se ha enviado el ID del usuario por GET
if (isset($_GET['id']) && !empty($_GET['id'])) {
    $id = intval($_GET['id']); // Convertir a entero para seguridad

    // Preparar la consulta para obtener los datos del usuario
    $stmt = $conn->prepare("SELECT id, nombre, email FROM usuarios WHERE id = ?");
    $stmt->bind_param("i", $id); // "i" indica que el parámetro es un entero
    $stmt->execute();
    $resultado = $stmt->get_result(); // Obtener el resultado de la consulta

    if ($resultado->num_rows > 0) {
        $usuario = $resultado->fetch_assoc(); // Obtener los datos del usuario
    } else {
        $mensaje = "Usuario no encontrado.";
    }
    $stmt->close();
}

// Lógica para actualizar el usuario cuando se envía el formulario
if ($_SERVER["REQUEST_METHOD"] == "POST") {
    $id = intval($_POST['id']);
    $nombre = $conn->real_escape_string($_POST['nombre']);
    $email = $conn->real_escape_string($_POST['email']);

    // Preparar la consulta SQL para actualización
    $stmt = $conn->prepare("UPDATE usuarios SET nombre = ?, email = ? WHERE id = ?");
    $stmt->bind_param("ssi", $nombre, $email, $id); // "ssi" indica string, string, int

    if ($stmt->execute()) {
        $mensaje = "¡Usuario actualizado exitosamente!";
        // Después de actualizar, recargar los datos del usuario para mostrar los cambios
        $stmt_reload = $conn->prepare("SELECT id, nombre, email FROM usuarios WHERE id = ?");
        $stmt_reload->bind_param("i", $id);
        $stmt_reload->execute();
        $resultado_reload = $stmt_reload->get_result();
        $usuario = $resultado_reload->fetch_assoc();
        $stmt_reload->close();
    } else {
        $mensaje = "Error al actualizar usuario: " . $stmt->error;
    }
    $stmt->close();
}

$conn->close();
?>

<!DOCTYPE html>
<html lang="es">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Editar Usuario</title>
    <style>
        body { font-family: Arial, sans-serif; margin: 20px; }
        .container { max-width: 600px; margin: auto; padding: 20px; border: 1px solid #ddd; border-radius: 8px; }
        .form-group { margin-bottom: 15px; }
        label { display: block; margin-bottom: 5px; }
        input[type="text"], input[type="email"] { width: calc(100% - 22px); padding: 10px; border: 1px solid #ccc; border-radius: 4px; }
        button { background-color: #007bff; color: white; padding: 10px 20px; border: none; border-radius: 4px; cursor: pointer; }
        button:hover { background-color: #0056b3; }
        .message { margin-top: 15px; padding: 10px; border-radius: 4px; }
        .success { background-color: #d4edda; color: #155724; border-color: #c3e6cb; }
        .error { background-color: #f8d7da; color: #721c24; border-color: #f5c6cb; }
        a { text-decoration: none; color: #007bff; margin-top: 10px; display: inline-block; }
    </style>
</head>
<body>
    <div class="container">
        <h2>Editar Usuario</h2>
        <?php if (!empty($mensaje)): ?>
            <div class="message <?php echo (strpos($mensaje, 'Error') !== false) ? 'error' : 'success'; ?>">
                <?php echo $mensaje; ?>
            </div>
        <?php endif; ?>

        <?php if ($usuario): ?>
            <form action="editar.php" method="POST">
                <input type="hidden" name="id" value="<?php echo htmlspecialchars($usuario['id']); ?>">
                <div class="form-group">
                    <label for="nombre">Nombre:</label>
                    <input type="text" id="nombre" name="nombre" value="<?php echo htmlspecialchars($usuario['nombre']); ?>" required>
                </div>
                <div class="form-group">
                    <label for="email">Email:</label>
                    <input type="email" id="email" name="email" value="<?php echo htmlspecialchars($usuario['email']); ?>" required>
                </div>
                <button type="submit">Actualizar Usuario</button>
            </form>
        <?php else: ?>
            <p><?php echo $mensaje; ?></p>
        <?php endif; ?>
        <p><a href="index.php">Volver al listado de usuarios</a></p>
    </div>
</body>
</html>
d. Operación ELIMINAR (Delete)

Crea un archivo eliminar.php. Este archivo se encargará de eliminar un usuario basándose en su ID. No necesitará un formulario, ya que la eliminación se iniciará desde un enlace en index.php.

PHP
<?php
include 'db_config.php';

$mensaje = '';

if (isset($_GET['id']) && !empty($_GET['id'])) {
    $id = intval($_GET['id']); // Convertir a entero para seguridad

    // Preparar la consulta SQL para eliminación
    $stmt = $conn->prepare("DELETE FROM usuarios WHERE id = ?");
    $stmt->bind_param("i", $id); // "i" indica que el parámetro es un entero

    if ($stmt->execute()) {
        $mensaje = "¡Usuario eliminado exitosamente!";
    } else {
        $mensaje = "Error al eliminar usuario: " . $stmt->error;
    }

    $stmt->close();
} else {
    $mensaje = "ID de usuario no proporcionado.";
}

$conn->close();
?>

<!DOCTYPE html>
<html lang="es">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Eliminar Usuario</title>
    <style>
        body { font-family: Arial, sans-serif; margin: 20px; }
        .container { max-width: 600px; margin: auto; padding: 20px; border: 1px solid #ddd; border-radius: 8px; text-align: center; }
        .message { margin-top: 15px; padding: 10px; border-radius: 4px; display: inline-block; }
        .success { background-color: #d4edda; color: #155724; border-color: #c3e6cb; }
        .error { background-color: #f8d7da; color: #721c24; border-color: #f5c6cb; }
        a { text-decoration: none; color: #007bff; margin-top: 10px; display: inline-block; }
    </style>
</head>
<body>
    <div class="container">
        <h2>Eliminar Usuario</h2>
        <?php if (!empty($mensaje)): ?>
            <div class="message <?php echo (strpos($mensaje, 'Error') !== false) ? 'error' : 'success'; ?>">
                <?php echo $mensaje; ?>
            </div>
        <?php endif; ?>
        <p><a href="index.php">Volver al listado de usuarios</a></p>
    </div>
</body>
</html>

4. Ejecución del Tutorial

  1. Asegúrate de que tu servidor web (Apache) y MySQL estén en ejecución.
  2. Coloca todos los archivos (db_config.php, index.php, crear.php, editar.php, eliminar.php) en un directorio dentro de la carpeta htdocs (para XAMPP) o www (para WAMP). Por ejemplo, C:\xampp\htdocs\crud_php.
  3. Abre tu navegador y navega a la URL correspondiente, por ejemplo: http://localhost/crud_php/index.php.

Deberías ver la tabla de usuarios (inicialmente vacía). Desde allí, puedes:

  • Hacer clic en "Crear Nuevo Usuario" para añadir un registro.
  • Una vez que tengas usuarios, verás opciones para "Editar" y "Eliminar" cada uno.

Consideraciones Importantes y Seguridad

  • Sentencias Preparadas: Como se mencionó, el uso de sentencias preparadas ($conn->prepare(), bind_param(), execute()) es la defensa más efectiva contra la inyección SQL, que es uno de los ataques de seguridad web más comunes y peligrosos.
  • htmlspecialchars(): Siempre usa htmlspecialchars() al mostrar datos que provienen de la base de datos (o cualquier entrada de usuario) en HTML. Esto previene ataques de Cross-Site Scripting (XSS).
  • Validación de Entrada: Este tutorial realiza una validación básica (intval, real_escape_string), pero en una aplicación real, deberías implementar una validación de entrada más robusta para asegurar que los datos cumplen con el formato y las restricciones esperadas (ej. validar que el email sea un formato de email válido).
  • Manejo de Errores: En un entorno de producción, los errores de la base de datos no deben mostrarse directamente al usuario ($stmt->error). En su lugar, deben registrarse en un log del servidor para depuración y mostrar un mensaje genérico al usuario.
  • Control de Sesiones y Autenticación: Para una aplicación real, necesitarías implementar un sistema de autenticación y autorización para asegurar que solo los usuarios autorizados puedan realizar operaciones CRUD.
  • Transacciones: Para operaciones que involucran múltiples cambios en la base de datos que deben ser atómicos (o todo se completa o nada), considera usar transacciones MySQL.

Este tutorial te proporciona una base sólida para entender y aplicar las operaciones CRUD en PHP con MySQL. ¡Espero que te sea de gran utilidad!


Referencias Bibliográficas:

American Psychological Association. (2020). Publication Manual of the American Psychological Association (7th ed.). American Psychological Association.

PHP Manual. (s.f.). MySQLi. Obtenido de https://www.php.net/manual/es/book.mysqli.php

MySQL. (s.f.). MySQL 8.0 Reference Manual. Obtenido de https://dev.mysql.com/doc/refman/8.0/en/

Comentarios