¿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. Eso sí, aquí damos por hecho que ya conoces PHP fundamentos: require, POST y GET, porque vamos a usarlo constantemente.
Lo mejor: Sin tecnicismos innecesarios. Solo código que funciona y explicaciones que entiendes.
🤔 ¿Qué vamos a aprender?
🎯 Al terminar este tutorial sabrás:
📦 ¿Qué es CRUD?
CRUD son las 4 operaciones básicas que puedes hacer con 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:
- Descarga XAMPP desde https://www.apachefriends.org
- Instala XAMPP (siguiente, siguiente, instalar)
- Abre el Control Panel de XAMPP
- 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
- Haz clic en "Nueva" (a la izquierda)
- Nombre de la base: mi_primer_crud
- 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:
- Ve a http://localhost/mi_crud/
- Haz clic en "Agregar Usuario"
- Llena el formulario con datos de prueba
- Verifica que aparezca "Usuario agregado correctamente"
- Ve a "Ver Usuarios" - deberías ver tu usuario en la tabla
- Prueba editar el usuario
- Finalmente, prueba eliminar (¡con cuidado!)
🔒 Seguridad implementada
💡 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
La subida de imágenes que mencionamos arriba tiene más trampas de las que parece: validar tipo de archivo, tamaño máximo, y que nadie suba un script disfrazado de imagen. Lo cubrimos en subir archivos en PHP con validación segura.
⚠️ 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
Si quieres ir más allá de las consultas SQL básicas y meter lógica dentro de la propia base de datos (validaciones, cálculos, procesos automáticos), el siguiente paso son los procedimientos almacenados con PL/SQL.
🤔 ¿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ónAprende a crear un sistema de login completo con sesiones, cookies y seguridad profesional