10-DELETE de Película
DELETE de Película (CRUD Completo)
📌 Introducción al Método DELETE
En este tutorial cerraremos el ciclo CRUD (Create, Read, Update, Delete) implementando el método DELETE para eliminar registros de películas. Ya hemos visto cómo:
Obtener todos los registros (GET)
Obtener un registro específico por ID (GET)
Crear nuevos registros (POST)
Actualizar registros existentes (PUT)
Ahora implementaremos la funcionalidad para eliminar películas.
🚀 Paso 1: Configurar el Endpoint DELETE
Vamos a happy.php (o tu archivo principal de rutas) y agregamos una nueva ruta para DELETE:
// Copiamos la estructura de otros métodos HTTP
$app->delete('/movies/{id}', function ($request, $response, $args) {
$id = $args['id'];
// Llamamos al controlador para eliminar
$movieController = new MovieController();
return $movieController->delete($id, $response);
});🛠️ Paso 2: Crear el Método DELETE en el Controller
En MovieController.php, creamos el método delete:
public function delete($id, $response)
{
try {
// 1. Validar que el ID sea numérico
if (!is_numeric($id) || $id <= 0) {
$errorResponse = [
'status' => 'error',
'message' => 'ID inválido'
];
return $response->withJson($errorResponse, 400);
}
// 2. Obtener la película existente
$movieModel = new Movie();
$existingMovie = $movieModel->find($id);
if (!$existingMovie) {
// Si no existe, retornar 404
$errorResponse = [
'status' => 'error',
'message' => 'Película no encontrada'
];
return $response->withJson($errorResponse, 404);
}
// 3. Eliminar la película
$deleted = $movieModel->destroy($id);
if ($deleted) {
// Retornar 204 No Content (éxito sin contenido)
return $response->withStatus(204);
} else {
// Error en la eliminación
$errorResponse = [
'status' => 'error',
'message' => 'Error al eliminar la película'
];
return $response->withJson($errorResponse, 500);
}
} catch (Exception $e) {
$errorResponse = [
'status' => 'error',
'message' => 'Error interno del servidor: ' . $e->getMessage()
];
return $response->withJson($errorResponse, 500);
}
}📝 Paso 3: Implementar el Método destroy en el Modelo
En tu modelo Movie.php, agrega el método destroy:
public function destroy($id)
{
try {
// Preparar la consulta DELETE
$sql = "DELETE FROM movies WHERE id = :id";
$stmt = $this->db->prepare($sql);
// Vincular parámetros
$stmt->bindParam(':id', $id, PDO::PARAM_INT);
// Ejecutar la consulta
return $stmt->execute();
} catch (PDOException $e) {
// Log del error (en producción usarías un logger)
error_log("Error al eliminar película: " . $e->getMessage());
return false;
}
}🔍 Paso 4: Probar el Endpoint DELETE
Usando Postman:
Configuración de la petición:
Método:
DELETEURL:
http://tu-dominio.com/movies/1Headers:
Content-Type: application/json
Ejemplos de respuesta:
Éxito (204 No Content):
Status: 204 No Content Body: (vacío)Película no encontrada (404):
{ "status": "error", "message": "Película no encontrada" }ID inválido (400):
{ "status": "error", "message": "ID inválido" }
🎯 Paso 5: Consideraciones de Seguridad
5.1 Validación de Autorización (si es necesario)
// Verificar si el usuario tiene permisos para eliminar
if (!$this->auth->canDelete()) {
$errorResponse = [
'status' => 'error',
'message' => 'No autorizado para eliminar recursos'
];
return $response->withJson($errorResponse, 403);
}5.2 Eliminación Lógica vs Física
Considera implementar "soft delete" (eliminación lógica) en lugar de borrado físico:
// En lugar de DELETE, hacer UPDATE con un campo 'deleted_at'
public function softDestroy($id)
{
$sql = "UPDATE movies SET deleted_at = NOW(), active = 0 WHERE id = :id";
$stmt = $this->db->prepare($sql);
$stmt->bindParam(':id', $id, PDO::PARAM_INT);
return $stmt->execute();
}📋 Paso 6: Pruebas Unitarias (Ejemplo)
class MovieDeleteTest extends TestCase
{
public function testDeleteMovieSuccess()
{
// Crear una película de prueba
$movieId = $this->createTestMovie();
// Eliminar la película
$response = $this->delete("/movies/{$movieId}");
// Verificar respuesta
$this->assertEquals(204, $response->getStatusCode());
// Verificar que ya no existe
$response = $this->get("/movies/{$movieId}");
$this->assertEquals(404, $response->getStatusCode());
}
public function testDeleteNonExistentMovie()
{
$response = $this->delete("/movies/999999");
$this->assertEquals(404, $response->getStatusCode());
}
}🔄 Paso 7: Integración con Frontend
Ejemplo con JavaScript (Fetch API):
async function deleteMovie(movieId) {
if (!confirm('¿Estás seguro de eliminar esta película?')) {
return;
}
try {
const response = await fetch(`/api/movies/${movieId}`, {
method: 'DELETE',
headers: {
'Authorization': `Bearer ${localStorage.getItem('token')}`
}
});
if (response.status === 204) {
// Eliminar el elemento del DOM
document.getElementById(`movie-${movieId}`).remove();
showNotification('Película eliminada correctamente', 'success');
} else if (response.status === 404) {
showNotification('La película no existe', 'error');
} else {
const error = await response.json();
showNotification(error.message, 'error');
}
} catch (error) {
showNotification('Error de conexión', 'error');
}
}📊 Resumen del CRUD Completo
| Método | Endpoint | Descripción | Códigos HTTP comunes |
|---|---|---|---|
| GET | /movies | Obtener todas las películas | 200, 500 |
| GET | /movies/{id} | Obtener una película específica | 200, 404, 400 |
| POST | /movies | Crear nueva película | 201, 400, 422 |
| PUT | /movies/{id} | Actualizar película existente | 200, 404, 400 |
| DELETE | /movies/{id} | Eliminar película | 204, 404, 400 |
🎉 Conclusión
¡Felicidades! Has completado la implementación del CRUD completo:
✅ Create - POST
✅ Read - GET (todos y por ID)
✅ Update - PUT
✅ Delete - DELETE
Con esto has construido una API RESTful completa para gestionar películas. En la siguiente parte del curso integraremos un frontend que consumirá esta API, permitiéndonos decir adiós a Postman y probar todo desde una interfaz gráfica.
📚 Buenas Prácticas Adicionales
Logging: Implementa un sistema de logging para registrar eliminaciones
Backup: Considera crear backups de datos importantes antes de eliminaciones
Rate Limiting: Limita la cantidad de peticiones DELETE por usuario
CORS: Configura adecuadamente los encabezados CORS
Documentación: Documenta tu API con OpenAPI/Swagger
¡Nos vemos en la siguiente parte donde integraremos el frontend
Comentarios
Publicar un comentario