Para facilitar la realización de los ejercicios, se proporcionan unas plantillas de los ejercicios Bases de datos (1) (Bases de datos) que incluyen parte de las páginas (los fragmentos HTML y las bibliotecas específicas de bases de datos). También puede descargar otras plantillas de los ejercicios Bases de datos (1) (Bases de datos) que incluyen únicamente el ejercicio 6, pero con más código ya incluido en las plantillas.
Puede descargar unas posibles soluciones de estos ejercicios en la página de soluciones.
El objetivo final de estos ejercicios es obtener una aplicación web capaz de gestionar una agenda. El título del ejercicio hace referencia a la funcionalidad añadida a la aplicación:
Estos ejercicios van encadenados. Cada ejercicio se puede hacer a partir del ejercicio anterior, añadiendo las páginas necesarias.
En estos ejercicios no se hace ninguna comprobación sobre los datos introducidos por el usuario, por lo que la aplicación no puede considerarse segura.
En cada ejercicio, se encuentran unos comentarios que incluyen un dibujo que indica las relaciones entre las diferentes páginas:
Escriba un programa que permita crear una base de datos.
// Base de datos utilizada por la aplicación
$cfg["dbMotor"] = SQLITE; // Valores posibles: MYSQL o SQLITE
// Configuración para SQLite
$cfg["sqliteDatabase"] = "/tmp/mclibre-base-datos-1-1.sqlite"; // Ubicación de la base de datos
// Configuración para MySQL
$cfg["mysqlHost"] = "localhost"; // Nombre de host
$cfg["mysqlUser"] = "mclibre_base_datos_1_1"; // Nombre de usuario
$cfg["mysqlPassword"] = ""; // Contraseña de usuario
$cfg["mysqlDatabase"] = "mclibre_base_datos_1_1"; // Nombre de la base de datos
// Tamaño de los campos en la tabla Personas
$cfg["tablaPersonasTamNombre"] = 40; // Tamaño de la columna Personas > Nombre
$cfg["tablaPersonasTamApellidos"] = 60; // Tamaño de la columna Personas > Apellidos
// SQLITE: Borrado y creación de tablas
$consulta = "DROP TABLE IF EXISTS $cfg[tablaPersonas]";
$consulta = "CREATE TABLE $cfg[tablaPersonas] (
id INTEGER PRIMARY KEY,
nombre VARCHAR($cfg[tablaPersonasTamNombre]) COLLATE NOCASE,
apellidos VARCHAR($cfg[tablaPersonasTamApellidos]) COLLATE NOCASE
)";
// MYSQL: Borrado y creación de base de datos y tablas
$consulta = "DROP DATABASE IF EXISTS $cfg[mysqlDatabase]";
$consulta = "CREATE DATABASE $cfg[mysqlDatabase]
CHARACTER SET utf8mb4
COLLATE utf8mb4_unicode_ci";
$consulta = "CREATE TABLE $cfg[tablaPersonas] (
id INTEGER UNSIGNED AUTO_INCREMENT,
nombre VARCHAR($cfg[tablaPersonasTamNombre]),
apellidos VARCHAR($cfg[tablaPersonasTamApellidos]),
PRIMARY KEY(id)
)";
Amplíe el ejercicio anterior de manera que permita incluir registros en la base de datos.
// Controles en el formulario
print " <td><input type=\"text\" name=\"nombre\" size=\"$cfg[formPersonasTamNombre]\" maxlength=\"$cfg[formPersonasTamNombre]\" autofocus></td>\n";
...
print " <td><input type=\"text\" name=\"apellidos\" size=\"$cfg[formPersonasTamApellidos]\" maxlength=\"$cfg[formPersonasTamApellidos]\"></td>\n";
Estas constantes se pueden definir en config.php y en este caso hacerlas coincidir con el tamaño de los campos en la base de datos:
// Tamaño de los campos en la tabla Personas
$cfg["tablaPersonasTamNombre"] = 40; // Tamaño de la columna Personas > Nombre
$cfg["tablaPersonasTamApellidos"] = 60; // Tamaño de la columna Personas > Apellidos
// Tamaño de los controles en los formularios
$cfg["formPersonasTamNombre"] = $cfg["tablaPersonasTamNombre"]; // Tamaño de la caja de texto Personas > Nombre
$cfg["formPersonasTamApellidos"] = $cfg["tablaPersonasTamApellidos"]; // Tamaño de la caja de texto Personas > Apellidos
// Comprueba la longitud del dato recibido
$nombre = recoge("nombre");
$nombreOk = false;
if (mb_strlen($nombre, "UTF-8") > $cfg["tablaPersonasTamNombre"]) {
print " <p class=\"aviso\">El nombre no puede tener más de $cfg[tablaPersonasTamNombre] caracteres.</p>\n";
print "\n";
} else {
$nombreOk = true;
}
// Añade un registro
$consulta = "INSERT INTO $cfg[tablaPersonas]
(nombre, apellidos)
VALUES (:nombre, :apellidos)";
$resultado = $pdo->prepare($consulta);
if (!$resultado) {
print " <p class=\"aviso\">Error al preparar la consulta. SQLSTATE[{$pdo->errorCode()}]: {$pdo->errorInfo()[2]}</p>\n";
} elseif (!$resultado->execute([":nombre" => $nombre, ":apellidos" => $apellidos])) {
print " <p class=\"aviso\">Error al ejecutar la consulta. SQLSTATE[{$pdo->errorCode()}]: {$pdo->errorInfo()[2]}</p>\n";
} else {
print " <p>Registro creado correctamente.</p>\n";
}
Amplíe el ejercicio anterior de manera que permita listar los registros de la base de datos.
// Selecciona y muestra todos los registros
$consulta = "SELECT * FROM $cfg[tablaPersonas]";
$resultado = $pdo->query($consulta);
if (!$resultado) {
print " <p class=\"aviso\">Error en la consulta. SQLSTATE[{$pdo->errorCode()}]: {$pdo->errorInfo()[2]}</p>\n";
} else {
print " <p>Listado completo de registros:</p>\n";
...
foreach ($resultado as $registro) {
print " <tr>\n";
print " <td>$registro[nombre]</td>\n";
print " <td>$registro[apellidos]</td>\n";
print " </tr>\n";
}
...
Amplíe el ejercicio anterior de manera que permita borrar individualmente los registros de la base de datos.
// Imprime casillas de verificación
$consulta = "SELECT * FROM $cfg[tablaPersonas]";
$resultado = $pdo->query($consulta);
if (!$resultado) {
print " <p class=\"aviso\">Error en la consulta. SQLSTATE[{$pdo->errorCode()}]: {$pdo->errorInfo()[2]}</p>\n";
} else {
print " <form action=\"borrar-2.php\" method=\"$cfg[formMethod]\">\n";
print " <p>Marque los registros que quiera borrar:</p>\n";
...
foreach ($resultado as $registro) {
print " <tr>\n";
print " <td class=\"centrado\"><input type=\"checkbox\" name=\"id[$registro[id]]\"></td>\n";
print " <td>$registro[nombre]</td>\n";
print " <td>$registro[apellidos]</td>\n";
print " </tr>\n";
}
...
// Recoge y borra los registros seleccionados
$id = recoge("id", []);
foreach ($id as $indice => $valor) {
$consulta = "DELETE FROM $cfg[tablaPersonas]
WHERE id = :indice";
$resultado = $pdo->prepare($consulta);
if (!$resultado) {
print " <p class=\"aviso\">Error al preparar la consulta. SQLSTATE[{$pdo->errorCode()}]: {$pdo->errorInfo()[2]}</p>\n";
} elseif (!$resultado->execute([":indice" => $indice])) {
print " <p class=\"aviso\">Error al ejecutar la consulta. SQLSTATE[{$pdo->errorCode()}]: {$pdo->errorInfo()[2]}</p>\n";
} else {
print " <p>Registro borrado correctamente (si existía).</p>\n";
}
}
Amplíe el ejercicio anterior de manera que permita buscar registros de la base de datos.
// Busca registros
$nombre = recoge("nombre");
$apellidos = recoge("apellidos");
$consulta = "SELECT * FROM $cfg[tablaPersonas]
WHERE nombre LIKE :nombre
AND apellidos LIKE :apellidos";
$resultado = $pdo->prepare($consulta);
if (!$resultado) {
print " <p class=\"aviso\">Error al preparar la consulta. SQLSTATE[{$pdo->errorCode()}]: {$pdo->errorInfo()[2]}</p>\n";
} elseif (!$resultado->execute([":nombre" => "%$nombre%", ":apellidos" => "%$apellidos%"])) {
print " <p class=\"aviso\">Error al ejecutar la consulta. SQLSTATE[{$pdo->errorCode()}]: {$pdo->errorInfo()[2]}</p>\n";
} else {
print " <p>Registros encontrados:</p>\n";
...
foreach ($resultado as $registro) {
print " <tr>\n";
print " <td>$registro[nombre]</td>\n";
print " <td>$registro[apellidos]</td>\n";
print " </tr>\n";
}
...
los comodines (%) deben incluirse al ejecutar la consulta, no se podrían incluir en la definición de $consulta.
Amplíe el ejercicio anterior de manera que permita modificar registros de la base de datos.
// Imprime botones radio
$consulta = "SELECT * FROM $cfg[tablaPersonas]";
$resultado = $pdo->query($consulta);
if (!$resultado) {
print " <p class=\"aviso\">Error en la consulta. SQLSTATE[{$pdo->errorCode()}]: {$pdo->errorInfo()[2]}</p>\n";
} else {
print " <form action=\"modificar-2.php\" method=\"$cfg[formMethod]\">\n";
print " <p>Indique el registro que quiera modificar:</p>\n";
...
foreach ($resultado as $registro) {
print " <tr>\n";
print " <td class=\"centrado\"><input type=\"radio\" name=\"id\" value=\"$registro[id]\"></td>\n";
print " <td>$registro[nombre]</td>\n";
print " <td>$registro[apellidos]</td>\n";
print " </tr>\n";
}
...
// Recoge el id del registro y selecciona el registro
$id = recoge("id");
if ($id == "") {
print " <p class=\"aviso\">No se ha seleccionado ningún registro.</p>\n";
} else {
$consulta = "SELECT * FROM $cfg[tablaPersonas]
WHERE id = :id";
$resultado = $pdo->prepare($consulta);
if (!$resultado) {
print " <p class=\"aviso\">Error al preparar la consulta. SQLSTATE[{$pdo->errorCode()}]: {$pdo->errorInfo()[2]}</p>\n";
} elseif (!$resultado->execute([":id" => $id])) {
print " <p class=\"aviso\">Error al ejecutar la consulta. SQLSTATE[{$pdo->errorCode()}]: {$pdo->errorInfo()[2]}</p>\n";
} else {
$registro = $resultado->fetch();
...
// Muestra los valores del registro en el formulario
print " <td><input type=\"text\" name=\"nombre\" size=\"$cfg[formPersonasTamNombre]\" maxlength=\"$cfg[formPersonasTamNombre]\" value=\"$registro[nombre]\" autofocus></td>\n";
...
print " <td><input type=\"text\" name=\"apellidos\" size=\"$cfg[formPersonasTamApellidos]\" maxlength=\"$cfg[formPersonasTamApellidos]\" value=\"$registro[apellidos]\"></td>\n";
// Control oculto con el id del registro modificado
print " <input type=\"hidden\" name=\"id\" value=\"$id\">\n";
// Actualiza el registro
$consulta = "UPDATE $cfg[tablaPersonas]
SET nombre = :nombre, apellidos = :apellidos
WHERE id = :id";
$resultado = $pdo->prepare($consulta);
if (!$resultado) {
print " <p class=\"aviso\">Error al preparar la consulta. SQLSTATE[{$pdo->errorCode()}]: {$pdo->errorInfo()[2]}</p>\n";
} elseif (!$resultado->execute([":nombre" => $nombre, ":apellidos" => $apellidos, ":id" => $id])) {
print " <p class=\"aviso\">Error al ejecutar la consulta. SQLSTATE[{$pdo->errorCode()}]: {$pdo->errorInfo()[2]}</p>\n";
} else {
print " <p>Registro modificado correctamente.</p>\n";
}