PHP y MySQL CRUD: Tu primera base de datos conectada paso a paso

Aprende a conectar PHP con MySQL creando un sistema CRUD completo (Crear, Leer, Actualizar, Eliminar) desde cero con ejemplos que realmente funcionan

¿Has aprendido PHP básico pero no sabes cómo conectarlo con una base de datos? ¿Te confunde todo eso de MySQL, PDO, mysqli, y no sabes por dónde empezar?

Este tutorial está hecho para ti. Vamos a crear un sistema CRUD completo (Crear, Leer, Actualizar, Eliminar) que realmente funcione. Al final tendrás una aplicación web donde puedes gestionar datos como un profesional.

Lo mejor: Sin tecnicismos innecesarios. Solo código que funciona y explicaciones que entiendes.

🤔 ¿Qué vamos a aprender?

🎯 Al terminar este tutorial sabrás:

Configurar MySQL en tu ordenador (XAMPP)
Conectar PHP con MySQL de forma segura
Crear una tabla en MySQL
Insertar datos desde un formulario PHP
Mostrar datos de la base de datos
Actualizar registros existentes
Eliminar datos de forma segura
Crear tu primera aplicación web completa

📦 ¿Qué es CRUD?

CRUD son las 4 operaciones básicas que puedes hacer con datos:

C Create - Crear nuevos datos
R Read - Leer datos existentes
U Update - Actualizar datos
D Delete - Eliminar datos

Es como tener una agenda de contactos: puedes agregar nuevos contactos, ver los contactos, editar información, y eliminar contactos que no necesitas.

🛠️ Preparando el entorno

1. Instalar XAMPP

XAMPP es tu mejor amigo - instala PHP, MySQL y Apache de una vez:

  1. Descarga XAMPP desde https://www.apachefriends.org
  2. Instala XAMPP (siguiente, siguiente, instalar)
  3. Abre el Control Panel de XAMPP
  4. Inicia Apache y MySQL (botones Start)

💡 Consejo pro:

Si Apache no inicia, probablemente tienes Skype abierto. Cierra Skype y vuelve a intentar.

2. Crear la base de datos

Abre tu navegador y ve a http://localhost/phpmyadmin

  1. Haz clic en "Nueva" (a la izquierda)
  2. Nombre de la base: mi_primer_crud
  3. Haz clic en "Crear"

¡Perfecto! Ya tienes tu base de datos.

🗄️ Creando la tabla

Vamos a crear una tabla para gestionar usuarios. En PhpMyAdmin, dentro de tu base de datos:

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

Pega este código en la pestaña "SQL" y haz clic en "Ejecutar".

❌ Error común:

Si ves un error de sintaxis, revisa que hayas copiado TODO el código, incluyendo el punto y coma final.

🔌 Conectando PHP con MySQL

⚠️ IMPORTANTE - Seguridad de credenciales:

NUNCA pongas credenciales de base de datos directamente en tu código. Te voy a enseñar la forma correcta usando variables de entorno.

🔒 Método SEGURO: Variables de entorno

1. Instalar vlucas/phpdotenv (manejador de variables de entorno para PHP):

// En la terminal, dentro de tu carpeta mi_crud:
composer require vlucas/phpdotenv

2. Crear archivo .env (aquí van las credenciales reales):

# Configuración de base de datos
DB_HOST=localhost
DB_USERNAME=root
DB_PASSWORD=
DB_DATABASE=mi_primer_crud
DB_CHARSET=utf8

3. Crear archivo .env.example (plantilla para otros desarrolladores):

# Copia este archivo como .env y completa con tus credenciales reales
DB_HOST=localhost
DB_USERNAME=tu_usuario
DB_PASSWORD=tu_password
DB_DATABASE=nombre_base_datos
DB_CHARSET=utf8

4. Crear archivo .gitignore (evitar subir credenciales):

# Nunca subir credenciales a Git
.env
vendor/

5. Crear archivo conexion.php SEGURO:

<?php
// ✅ Cargar variables de entorno
require_once 'vendor/autoload.php';

$dotenv = Dotenv\Dotenv::createImmutable(__DIR__);
$dotenv->load();

// ✅ Configuración SEGURA desde variables de entorno
$host = $_ENV['DB_HOST'];
$usuario_db = $_ENV['DB_USERNAME'];
$password_db = $_ENV['DB_PASSWORD'];
$nombre_db = $_ENV['DB_DATABASE'];
$charset = $_ENV['DB_CHARSET'];

try {
    // ✅ PDO con configuración desde variables de entorno
    $pdo = new PDO(
        "mysql:host=$host;dbname=$nombre_db;charset=$charset",
        $usuario_db,
        $password_db,
        [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
            PDO::ATTR_EMULATE_PREPARES => false
        ]
    );
    
    echo "✅ Conexión segura establecida!";
    
} catch(PDOException $e) {
    error_log("Error de conexión: " . $e->getMessage());
    die("Error de conexión a la base de datos");
}
?>

🔒 ¿Por qué usar variables de entorno?

  • Seguridad: Las credenciales nunca aparecen en el código
  • Flexibilidad: Diferentes credenciales por entorno (desarrollo/producción)
  • Control de versiones: .env no se sube a Git
  • Colaboración: Cada desarrollador tiene sus propias credenciales
  • Producción: El servidor puede inyectar variables sin tocar código

🚀 Alternativa simple para principiantes

Si no puedes usar Composer, aquí tienes una versión básica (solo para aprendizaje local):

<?php
// ❌ SOLO para aprendizaje local - NUNCA en producción
$host = "localhost";
$usuario_db = "root";
$password_db = "";
$nombre_db = "mi_primer_crud";

try {
    $pdo = new PDO(
        "mysql:host=$host;dbname=$nombre_db;charset=utf8",
        $usuario_db,
        $password_db
    );
    
    $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
    
    echo "Conexión exitosa!";
    
} catch(PDOException $e) {
    die("Error de conexión: " . $e->getMessage());
}
?>

⚠️ Importante:

La versión simple es SOLO para aprender en tu ordenador local. Para cualquier proyecto real, usa SIEMPRE variables de entorno.

Verifica que funcione: Ve a http://localhost/mi_crud/conexion.php

Si ves "Conexión exitosa!" - ¡perfecto! Si no, revisa que MySQL esté ejecutándose en XAMPP.

📝 CREATE: Insertar datos

Crear archivo insertar.php:

<!DOCTYPE html>
<html lang="es">
<head>
    <meta charset="UTF-8">
    <title>Agregar Usuario</title>
    <style>
        body { font-family: Arial; margin: 20px; }
        form { max-width: 400px; }
        input, button { margin: 10px 0; padding: 8px; width: 100%; }
        button { background: #007cba; color: white; border: none; }
    </style>
</head>
<body>
    <h2>Agregar Nuevo Usuario</h2>

    <?php
    // ✅ Procesar el formulario
    if ($_POST) {
        include 'conexion.php';
        
        $nombre = $_POST['nombre'];
        $email = $_POST['email'];
        $edad = $_POST['edad'];
        
        try {
            // ✅ Prepared statements = SEGURIDAD
            $sql = "INSERT INTO usuarios (nombre, email, edad) VALUES (?, ?, ?)";
            $stmt = $pdo->prepare($sql);
            $stmt->execute([$nombre, $email, $edad]);
            
            echo "<p style='color: green;'>✅ Usuario agregado correctamente!</p>";
            
        } catch(PDOException $e) {
            echo "<p style='color: red;'>❌ Error: " . $e->getMessage() . "</p>";
        }
    }
    ?>

    <form method="POST">
        <label>Nombre:</label>
        <input type="text" name="nombre" required>
        
        <label>Email:</label>
        <input type="email" name="email" required>
        
        <label>Edad:</label>
        <input type="number" name="edad" min="1">
        
        <button type="submit">Agregar Usuario</button>
    </form>

    <p><a href="mostrar.php">Ver todos los usuarios</a></p>
</body>
</html>

💡 ¿Por qué usar Prepared Statements?

  • Seguridad: Previene inyección SQL
  • Limpio: PHP se encarga de escapar los datos
  • Rápido: MySQL puede reutilizar la consulta

👀 READ: Mostrar datos

Crear archivo mostrar.php:

<!DOCTYPE html>
<html lang="es">
<head>
    <meta charset="UTF-8">
    <title>Lista de Usuarios</title>
    <style>
        body { font-family: Arial; margin: 20px; }
        table { border-collapse: collapse; width: 100%; }
        th, td { border: 1px solid #ddd; padding: 12px; text-align: left; }
        th { background-color: #f2f2f2; }
        .acciones a { margin-right: 10px; padding: 5px 10px; text-decoration: none; border-radius: 3px; }
        .editar { background: #28a745; color: white; }
        .eliminar { background: #dc3545; color: white; }
    </style>
</head>
<body>
    <h2>Lista de Usuarios</h2>

    <?php
    include 'conexion.php';
    
    try {
        // ✅ Obtener todos los usuarios
        $sql = "SELECT * FROM usuarios ORDER BY id DESC";
        $stmt = $pdo->query($sql);
        $usuarios = $stmt->fetchAll(PDO::FETCH_ASSOC);
        
        if (count($usuarios) > 0) {
            echo "<table>";
            echo "<tr><th>ID</th><th>Nombre</th><th>Email</th><th>Edad</th><th>Fecha Registro</th><th>Acciones</th></tr>";
            
            foreach ($usuarios as $usuario) {
                echo "<tr>";
                echo "<td>" . $usuario['id'] . "</td>";
                echo "<td>" . htmlspecialchars($usuario['nombre']) . "</td>";
                echo "<td>" . htmlspecialchars($usuario['email']) . "</td>";
                echo "<td>" . $usuario['edad'] . "</td>";
                echo "<td>" . $usuario['fecha_registro'] . "</td>";
                echo "<td class='acciones'>";
                echo "<a href='editar.php?id=" . $usuario['id'] . "' class='editar'>Editar</a>";
                echo "<a href='eliminar.php?id=" . $usuario['id'] . "' class='eliminar' onclick='return confirm(\"¿Seguro que quieres eliminar?\");'>Eliminar</a>";
                echo "</td>";
                echo "</tr>";
            }
            
            echo "</table>";
        } else {
            echo "<p>No hay usuarios registrados.</p>";
        }
        
    } catch(PDOException $e) {
        echo "❌ Error: " . $e->getMessage();
    }
    ?>

    <p><a href="insertar.php">Agregar nuevo usuario</a></p>
</body>
</html>

💡 ¿Por qué htmlspecialchars()?

Protege contra XSS (Cross-Site Scripting). Si alguien pone código malicioso en el nombre, htmlspecialchars() lo convierte en texto plano inofensivo.

✏️ UPDATE: Editar datos

Crear archivo editar.php:

<!DOCTYPE html>
<html lang="es">
<head>
    <meta charset="UTF-8">
    <title>Editar Usuario</title>
    <style>
        body { font-family: Arial; margin: 20px; }
        form { max-width: 400px; }
        input, button { margin: 10px 0; padding: 8px; width: 100%; }
        button { background: #28a745; color: white; border: none; }
    </style>
</head>
<body>
    <h2>Editar Usuario</h2>

    <?php
    include 'conexion.php';
    
    // ✅ Verificar que existe el ID
    if (!isset($_GET['id'])) {
        die("❌ ID no especificado");
    }
    
    $id = $_GET['id'];
    
    // ✅ Procesar actualización
    if ($_POST) {
        $nombre = $_POST['nombre'];
        $email = $_POST['email'];
        $edad = $_POST['edad'];
        
        try {
            $sql = "UPDATE usuarios SET nombre = ?, email = ?, edad = ? WHERE id = ?";
            $stmt = $pdo->prepare($sql);
            $stmt->execute([$nombre, $email, $edad, $id]);
            
            echo "<p style='color: green;'>✅ Usuario actualizado correctamente!</p>";
            echo "<p><a href='mostrar.php'>Volver a la lista</a></p>";
            
        } catch(PDOException $e) {
            echo "<p style='color: red;'>❌ Error: " . $e->getMessage() . "</p>";
        }
    }
    
    // ✅ Obtener datos actuales del usuario
    try {
        $sql = "SELECT * FROM usuarios WHERE id = ?";
        $stmt = $pdo->prepare($sql);
        $stmt->execute([$id]);
        $usuario = $stmt->fetch(PDO::FETCH_ASSOC);
        
        if (!$usuario) {
            die("❌ Usuario no encontrado");
        }
        
    } catch(PDOException $e) {
        die("❌ Error: " . $e->getMessage());
    }
    ?>

    <form method="POST">
        <label>Nombre:</label>
        <input type="text" name="nombre" value="<?= htmlspecialchars($usuario['nombre']) ?>" required>
        
        <label>Email:</label>
        <input type="email" name="email" value="<?= htmlspecialchars($usuario['email']) ?>" required>
        
        <label>Edad:</label>
        <input type="number" name="edad" value="<?= $usuario['edad'] ?>" min="1">
        
        <button type="submit">Actualizar Usuario</button>
    </form>

    <p><a href="mostrar.php">Volver a la lista</a></p>
</body>
</html>

🗑️ DELETE: Eliminar datos

Crear archivo eliminar.php:

<?php
include 'conexion.php';

// ✅ Verificar que existe el ID
if (!isset($_GET['id'])) {
    header("Location: mostrar.php?error=id_no_especificado");
    exit;
}

$id = $_GET['id'];

try {
    // ✅ Verificar que el usuario existe antes de eliminar
    $sql_check = "SELECT COUNT(*) FROM usuarios WHERE id = ?";
    $stmt_check = $pdo->prepare($sql_check);
    $stmt_check->execute([$id]);
    
    if ($stmt_check->fetchColumn() == 0) {
        header("Location: mostrar.php?error=usuario_no_existe");
        exit;
    }
    
    // ✅ Eliminar el usuario
    $sql = "DELETE FROM usuarios WHERE id = ?";
    $stmt = $pdo->prepare($sql);
    $stmt->execute([$id]);
    
    // ✅ Redirigir con mensaje de éxito
    header("Location: mostrar.php?mensaje=usuario_eliminado");
    exit;
    
} catch(PDOException $e) {
    header("Location: mostrar.php?error=database_error");
    exit;
}
?>

🏠 Página principal del CRUD

Crear archivo index.php para tener una página principal:

<!DOCTYPE html>
<html lang="es">
<head>
    <meta charset="UTF-8">
    <title>Mi Primer CRUD con PHP y MySQL</title>
    <style>
        body { font-family: Arial; text-align: center; margin: 50px; }
        .menu { display: flex; justify-content: center; gap: 20px; margin-top: 30px; }
        .menu a { padding: 15px 25px; background: #007cba; color: white; text-decoration: none; border-radius: 5px; }
        .menu a:hover { background: #005a87; }
        .stats { margin-top: 40px; background: #f8f9fa; padding: 20px; border-radius: 10px; }
    </style>
</head>
<body>
    <h1>🎉 Mi Primer CRUD con PHP y MySQL</h1>
    <p>Sistema completo para gestionar usuarios</p>

    <div class="menu">
        <a href="insertar.php">➕ Agregar Usuario</a>
        <a href="mostrar.php">👥 Ver Usuarios</a>
    </div>

    <div class="stats">
        <h3>📊 Estadísticas</h3>
        <?php
        include 'conexion.php';
        
        try {
            $sql = "SELECT COUNT(*) as total FROM usuarios";
            $stmt = $pdo->query($sql);
            $total = $stmt->fetchColumn();
            
            echo "<p>👤 Total de usuarios registrados: <strong>$total</strong></p>";
            
        } catch(PDOException $e) {
            echo "<p>Error al obtener estadísticas</p>";
        }
        ?>
    </div>
</body>
</html>

🎯 Probando tu aplicación CRUD

Sigue estos pasos para probar que todo funcione:

  1. Ve a http://localhost/mi_crud/
  2. Haz clic en "Agregar Usuario"
  3. Llena el formulario con datos de prueba
  4. Verifica que aparezca "Usuario agregado correctamente"
  5. Ve a "Ver Usuarios" - deberías ver tu usuario en la tabla
  6. Prueba editar el usuario
  7. Finalmente, prueba eliminar (¡con cuidado!)

🔒 Seguridad implementada

100% Prepared Statements - Previene inyección SQL
htmlspecialchars() - Previene XSS
🛡️ Validaciones de datos en formularios

💡 Siguientes pasos

Ahora que dominas CRUD básico, puedes mejorar tu aplicación:

🚀 Mejoras que puedes agregar:

  • Paginación: Si tienes muchos usuarios, mostrar de 10 en 10
  • Búsqueda: Filtrar usuarios por nombre o email
  • Validaciones avanzadas: Verificar que el email sea único
  • Subida de imágenes: Avatar para cada usuario
  • Sistema de login: Solo usuarios autenticados pueden acceder
  • Mejor diseño: CSS con Bootstrap o un framework

⚠️ Importante para producción:

  • Variables de entorno: NUNCA credenciales en el código
  • HTTPS: Siempre usar conexiones encriptadas
  • Usuarios únicos: Nunca usar "root" en producción
  • Validaciones: Validar TODOS los datos de entrada
  • Autenticación: Sistema de login y permisos
  • Rate limiting: Evitar ataques de fuerza bruta
  • Logs de seguridad: Registrar intentos de acceso

🤔 ¿Dudas comunes?

P: ¿Por qué usar PDO en lugar de mysqli?
R: PDO funciona con múltiples bases de datos (MySQL, PostgreSQL, SQLite) y tiene sintaxis más limpia.

P: ¿Es seguro este código?
R: Para aprendizaje, sí. Para producción necesitas más validaciones, HTTPS, y manejo de sesiones.

P: ¿Puedo usar este código en mi proyecto?
R: ¡Por supuesto! Es tuyo. Solo recuerda añadir las mejoras de seguridad mencionadas.

🎯 ¿Listo para el siguiente nivel?

Ya dominas CRUD con PHP y MySQL. Es hora de crear aplicaciones más avanzadas con autenticación y APIs.

👉 PHP Sessions y Login: Tu primer sistema de autenticación

Aprende a crear un sistema de login completo con sesiones, cookies y seguridad profesional