8-Versión MVC con post-SIN routes--ejs
Versión MVC SIN routes (todo en app.js)
Aquí tienes el ejercicio refactorizado a MVC sin carpeta de rutas:
📁 ESTRUCTURA DEL PROYECTO
mi-app-mvc/
├── models/
│ └── Producto.js
├── controllers/
│ └── productoController.js
├── views/
│ ├── index.ejs
│ └── productos.ejs
├── public/
│ └── (archivos estáticos)
└── app.js📄 CÓDIGO COMPLETO
1️⃣ models/Producto.js (Modelo - Datos)
// Modelo: Maneja toda la lógica de datos
class Producto {
constructor() {
this.productos = [
{ id: 1, nombre: 'Laptop', precio: 1000 },
{ id: 2, nombre: 'Mouse', precio: 25 },
{ id: 3, nombre: 'Teclado', precio: 50 }
];
this.contadorId = 4;
}
// Obtener todos los productos
obtenerTodos() {
return this.productos;
}
// Obtener producto por ID
obtenerPorId(id) {
return this.productos.find(p => p.id === id);
}
// Crear nuevo producto
crear(nombre, precio) {
const nuevoProducto = {
id: this.contadorId++,
nombre: nombre.trim(),
precio: parseFloat(precio)
};
this.productos.push(nuevoProducto);
return nuevoProducto;
}
// Obtener cantidad total
obtenerTotal() {
return this.productos.length;
}
}
module.exports = new Producto();2️⃣ controllers/productoController.js (Controlador - Lógica)
const Producto = require('../models/Producto');
class ProductoController {
// GET / - Renderizar vista principal (index)
mostrarInicio(req, res) {
const productos = Producto.obtenerTodos();
res.render('index', {
title: 'Mi Tienda',
productos: productos
});
}
// GET /productos-vista - Renderizar vista de productos
mostrarProductosVista(req, res) {
const productos = Producto.obtenerTodos();
const total = Producto.obtenerTotal();
res.render('productos', {
title: 'Lista de Productos',
productos: productos,
total: total
});
}
// GET /productos - API: Devolver todos (JSON)
obtenerTodosAPI(req, res) {
const productos = Producto.obtenerTodos();
res.json(productos);
}
// GET /productos/:id - API: Devolver uno (JSON)
obtenerUnoAPI(req, res) {
const id = parseInt(req.params.id);
const producto = Producto.obtenerPorId(id);
if (!producto) {
return res.status(404).json({
error: 'Producto no encontrado'
});
}
res.json(producto);
}
// POST /productos-formulario - Procesar formulario
procesarFormulario(req, res) {
const { nombre, precio } = req.body;
// Validaciones
if (!nombre || !precio) {
return res.status(400).send(`
<h1>Error</h1>
<p>Faltan datos: nombre y precio son obligatorios</p>
<a href="/">Volver al inicio</a>
`);
}
if (nombre.trim().length < 3) {
return res.status(400).send(`
<h1>Error</h1>
<p>El nombre debe tener al menos 3 caracteres</p>
<a href="/">Volver al inicio</a>
`);
}
if (isNaN(precio) || parseFloat(precio) <= 0) {
return res.status(400).send(`
<h1>Error</h1>
<p>El precio debe ser un número mayor a 0</p>
<a href="/">Volver al inicio</a>
`);
}
// Crear producto
const nuevoProducto = Producto.crear(nombre, precio);
// Redirigir a la página principal
res.redirect('/');
}
}
module.exports = new ProductoController();3️⃣ app.js (Configuración principal SIN router)
const express = require('express');
const path = require('path');
const productoController = require('./controllers/productoController');
const app = express();
const PORT = 3000;
// ============ CONFIGURACIÓN EJS ============
app.set('view engine', 'ejs');
app.set('views', path.join(__dirname, 'views'));
// ============ MIDDLEWARES ============
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
app.use(express.static('public'));
app.use('/css', express.static('node_modules/bootstrap/dist/css'));
app.use('/js', express.static('node_modules/bootstrap/dist/js'));
// ============ RUTAS (usando el controlador directamente) ============
// Rutas Web (EJS)
app.get('/', productoController.mostrarInicio);
app.get('/productos-vista', productoController.mostrarProductosVista);
// Rutas API (JSON)
app.get('/productos', productoController.obtenerTodosAPI);
app.get('/productos/:id', productoController.obtenerUnoAPI);
// Ruta para formulario
app.post('/productos-formulario', productoController.procesarFormulario);
// ============ INICIAR SERVIDOR ============
app.listen(PORT, () => {
console.log(`=================================`);
console.log(`🚀 Servidor MVC corriendo en http://localhost:${PORT}`);
console.log(`=================================`);
console.log(`📌 Vistas EJS:`);
console.log(` 🏠 Inicio: http://localhost:${PORT}/`);
console.log(` 📦 Productos: http://localhost:${PORT}/productos-vista`);
console.log(``);
console.log(`📌 API JSON:`);
console.log(` GET /productos`);
console.log(` GET /productos/1`);
console.log(``);
console.log(`📌 Formulario (POST):`);
console.log(` POST /productos-formulario`);
console.log(`=================================`);
});4️⃣ views/index.ejs (Vista principal)
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title><%= title %></title>
<link rel="stylesheet" href="/css/bootstrap.min.css">
</head>
<body>
<div class="container mt-5">
<h1 class="text-center">📦 <%= title %></h1>
<div class="row mt-4">
<div class="col-md-8 offset-md-2">
<div class="card">
<div class="card-header bg-primary text-white">
<h4 class="mb-0">Bienvenido a tu Tienda</h4>
</div>
<div class="card-body">
<p>Gestiona tus productos fácilmente</p>
<div class="row">
<div class="col-md-6">
<div class="card text-center">
<div class="card-body">
<h5 class="card-title">📋 Ver Productos</h5>
<p class="card-text">Consulta todos los productos disponibles</p>
<a href="/productos-vista" class="btn btn-primary">Ver Productos</a>
</div>
</div>
</div>
<div class="col-md-6">
<div class="card text-center">
<div class="card-body">
<h5 class="card-title">➕ API REST</h5>
<p class="card-text">Accede a los datos via JSON</p>
<a href="/productos" class="btn btn-success">Ver API</a>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Formulario para agregar productos -->
<div class="row mt-4">
<div class="col-md-8 offset-md-2">
<div class="card">
<div class="card-header bg-success text-white">
<h4 class="mb-0">➕ Agregar Nuevo Producto</h4>
</div>
<div class="card-body">
<form action="/productos-formulario" method="POST">
<div class="mb-3">
<label for="nombre" class="form-label">Nombre del producto</label>
<input type="text" class="form-control" id="nombre" name="nombre" required>
</div>
<div class="mb-3">
<label for="precio" class="form-label">Precio</label>
<input type="number" class="form-control" id="precio" name="precio" step="0.01" required>
</div>
<button type="submit" class="btn btn-success">💾 Guardar Producto</button>
</form>
</div>
</div>
</div>
</div>
<!-- Info de arquitectura -->
<div class="row mt-4">
<div class="col-md-8 offset-md-2">
<div class="alert alert-info">
<strong>📐 Arquitectura MVC</strong><br>
✅ Modelo: Producto.js (datos)<br>
✅ Vista: index.ejs y productos.ejs (interfaz)<br>
✅ Controlador: productoController.js (lógica)<br>
✅ Sin carpeta routes (todo en app.js)
</div>
</div>
</div>
</div>
<script src="/js/bootstrap.min.js"></script>
</body>
</html>5️⃣ views/productos.ejs (Lista de productos)
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title><%= title %></title>
<link rel="stylesheet" href="/css/bootstrap.min.css">
</head>
<body>
<div class="container mt-5">
<h1 class="text-center">📦 <%= title %></h1>
<div class="row mt-4">
<div class="col-md-8 offset-md-2">
<div class="card">
<div class="card-header bg-primary text-white d-flex justify-content-between align-items-center">
<h4 class="mb-0">Productos disponibles</h4>
<span class="badge bg-light text-dark">Total: <%= total %></span>
</div>
<div class="card-body">
<% if (productos.length === 0) { %>
<div class="alert alert-warning">
No hay productos disponibles
</div>
<% } else { %>
<div class="table-responsive">
<table class="table table-striped">
<thead>
<tr>
<th>ID</th>
<th>Nombre</th>
<th>Precio</th>
<th>Acciones</th>
</tr>
</thead>
<tbody>
<% productos.forEach(producto => { %>
<tr>
<td><%= producto.id %></td>
<td><%= producto.nombre %></td>
<td>$<%= producto.precio %></td>
<td>
<a href="/productos/<%= producto.id %>" class="btn btn-sm btn-info">Ver JSON</a>
</td>
</tr>
<% }) %>
</tbody>
</table>
</div>
<% } %>
</div>
</div>
<div class="text-center mt-3">
<a href="/" class="btn btn-secondary">← Volver al inicio</a>
<a href="/productos" class="btn btn-success">Ver API JSON</a>
</div>
</div>
</div>
</div>
<script src="/js/bootstrap.min.js"></script>
</body>
</html>🚀 CÓMO EJECUTAR
# 1. Instalar dependencias
npm install express ejs bootstrap
# 2. Crear estructura de carpetas
mkdir models controllers views public
# 3. Crear los archivos con el código anterior
# 4. Ejecutar
node app.js🎯 ENDPOINTS DISPONIBLES
| Método | URL | Descripción | Tipo |
|---|---|---|---|
| GET | / | Página principal | Web |
| GET | /productos-vista | Lista de productos | Web |
| GET | /productos | Todos los productos | API JSON |
| GET | /productos/1 | Un producto específico | API JSON |
| POST | /productos-formulario | Crear producto | Formulario |
📊 COMPARATIVA: CON routes vs SIN routes
| Característica | CON routes | SIN routes |
|---|---|---|
| Archivos | 4 (models, controllers, views, routes) | 3 (models, controllers, views) |
| Complejidad | Media | Baja |
| Ideal para | Proyectos medianos/grandes | Proyectos pequeños |
| Mantenimiento | Más organizado | Más directo |
¡Listo! Ahora tienes MVC completo SIN carpeta de rutas
Comentarios
Publicar un comentario