jerkjs 2.2.0 → 2.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +26 -0
- package/LICENSE +201 -0
- package/README.md +4 -1
- package/README_EN.md +230 -0
- package/README_PT.md +230 -0
- package/docs/ARQUITECTURA_ROUTES.md +186 -0
- package/docs/EXTENSION_MANUAL.md +955 -0
- package/docs/FIREWALL_MANUAL.md +416 -0
- package/docs/HOOK-2.0.md +512 -0
- package/docs/HOOKS_REFERENCE_IMPROVED.md +596 -0
- package/docs/JERK_FRAMEWORK_DIAGRAM.txt +492 -0
- package/docs/JERK_FRAMEWORK_DIAGRAM_MERMAID.mmd +124 -0
- package/docs/JERK_FRAMEWORK_DOCUMENTATION.md +553 -0
- package/docs/JERK_MODELOS_HOWTO.md +566 -0
- package/docs/MANUAL_API_SDK.md +536 -0
- package/docs/MARIADB_TOKENS_IMPLEMENTATION.md +110 -0
- package/docs/MIDDLEWARE_MANUAL.md +518 -0
- package/docs/OAUTH2_GOOGLE_MANUAL.md +405 -0
- package/docs/ROUTING_WITHOUT_JSON_GUIDE.md +454 -0
- package/docs/frontend-and-sessions.md +353 -0
- package/docs/guia_inicio_rapido_jerkjs.md +113 -0
- package/examples/examples.arj +0 -0
- package/index.js +12 -1
- package/jerk-qbuilder/CHANGELOG.md +71 -0
- package/jerk-qbuilder/HOWTO.md +325 -0
- package/jerk-qbuilder/README.md +52 -0
- package/lib/mvc/controllerBase.js +31 -14
- package/lib/query/MariaDBAdapter.js +78 -0
- package/lib/query/consoleAdapter.js +184 -0
- package/lib/query/queryBuilder.js +953 -0
- package/lib/query/queryBuilderHooks.js +455 -0
- package/lib/query/queryBuilderMiddleware.js +332 -0
- package/package.json +2 -2
- package/utils/find_file_path.sh +36 -0
|
@@ -0,0 +1,566 @@
|
|
|
1
|
+
# Guía de Uso de Modelos en JERK Framework
|
|
2
|
+
|
|
3
|
+
## Tabla de Contenidos
|
|
4
|
+
1. [Introducción](#introducción)
|
|
5
|
+
2. [Conceptos Básicos](#conceptos-básicos)
|
|
6
|
+
3. [Creación de Modelos](#creación-de-modelos)
|
|
7
|
+
4. [Operaciones CRUD](#operaciones-crud)
|
|
8
|
+
5. [Validación de Datos](#validación-de-datos)
|
|
9
|
+
6. [Comunicación con Controladores](#comunicación-con-controladores)
|
|
10
|
+
7. [Uso de Adaptadores](#uso-de-adaptadores)
|
|
11
|
+
8. [Hooks en Modelos](#hooks-en-modelos)
|
|
12
|
+
9. [Ejemplos Prácticos](#ejemplos-prácticos)
|
|
13
|
+
|
|
14
|
+
## Introducción
|
|
15
|
+
|
|
16
|
+
Los modelos en JERK Framework representan la capa de datos de tu aplicación y forman parte integral del patrón MVC (Modelo-Vista-Controlador). Proporcionan una interfaz para interactuar con diferentes fuentes de datos, ya sea memoria, bases de datos SQL o NoSQL.
|
|
17
|
+
|
|
18
|
+
## Conceptos Básicos
|
|
19
|
+
|
|
20
|
+
### Componentes Principales
|
|
21
|
+
|
|
22
|
+
- **ModelBase**: Clase base para todos los modelos
|
|
23
|
+
- **ModelManager**: Gestor centralizado para administrar instancias de modelos
|
|
24
|
+
- **Adaptadores**: Interfaces para diferentes tipos de almacenamiento
|
|
25
|
+
- **Hooks**: Sistema de extensibilidad para modelos
|
|
26
|
+
|
|
27
|
+
### Características
|
|
28
|
+
|
|
29
|
+
- Comunicación bidireccional con controladores
|
|
30
|
+
- Soporte para múltiples adaptadores de almacenamiento
|
|
31
|
+
- Validación de datos integrada
|
|
32
|
+
- Sistema de hooks para extensibilidad
|
|
33
|
+
- Integración con el sistema de autenticación y autorización
|
|
34
|
+
|
|
35
|
+
## Creación de Modelos
|
|
36
|
+
|
|
37
|
+
### Extender ModelBase
|
|
38
|
+
|
|
39
|
+
Para crear un modelo personalizado, extiende la clase `ModelBase`:
|
|
40
|
+
|
|
41
|
+
```javascript
|
|
42
|
+
const ModelBase = require('jerkjs').ModelBase;
|
|
43
|
+
|
|
44
|
+
class ProductoModel extends ModelBase {
|
|
45
|
+
constructor(options = {}) {
|
|
46
|
+
super({
|
|
47
|
+
...options,
|
|
48
|
+
tableName: options.tableName || 'productos'
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
// Definir campos del modelo
|
|
52
|
+
this.fields = {
|
|
53
|
+
id: { type: 'integer', primaryKey: true, autoIncrement: true },
|
|
54
|
+
nombre: { type: 'string', required: true },
|
|
55
|
+
descripcion: { type: 'text' },
|
|
56
|
+
precio: { type: 'decimal', required: true },
|
|
57
|
+
categoria: { type: 'string' },
|
|
58
|
+
createdAt: { type: 'datetime', default: 'CURRENT_TIMESTAMP' },
|
|
59
|
+
updatedAt: { type: 'datetime', default: 'CURRENT_TIMESTAMP' }
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Métodos personalizados
|
|
64
|
+
async getProductosPorCategoria(categoria) {
|
|
65
|
+
return await this.find({ categoria });
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
async getProductosConPrecioMayor(precioMinimo) {
|
|
69
|
+
// Implementación personalizada según el adaptador
|
|
70
|
+
return await this.find({ precio: { $gte: precioMinimo } });
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### Uso de ModelManager
|
|
76
|
+
|
|
77
|
+
El `ModelManager` te permite administrar tus modelos y sus adaptadores:
|
|
78
|
+
|
|
79
|
+
```javascript
|
|
80
|
+
const { ModelManager, MemoryAdapter } = require('jerkjs');
|
|
81
|
+
|
|
82
|
+
// Crear instancia del gestor de modelos
|
|
83
|
+
const modelManager = new ModelManager();
|
|
84
|
+
|
|
85
|
+
// Registrar un adaptador
|
|
86
|
+
const memoryAdapter = new MemoryAdapter();
|
|
87
|
+
modelManager.registerAdapter('memory', memoryAdapter);
|
|
88
|
+
|
|
89
|
+
// Crear instancia de modelo
|
|
90
|
+
const productoModel = modelManager.createModel('Producto', {
|
|
91
|
+
adapterName: 'memory',
|
|
92
|
+
modelOptions: { tableName: 'productos' }
|
|
93
|
+
});
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## Operaciones CRUD
|
|
97
|
+
|
|
98
|
+
Los modelos proporcionan métodos estándar para operaciones CRUD:
|
|
99
|
+
|
|
100
|
+
### Crear Registros
|
|
101
|
+
|
|
102
|
+
```javascript
|
|
103
|
+
// Crear un nuevo registro
|
|
104
|
+
const nuevoProducto = await productoModel.create({
|
|
105
|
+
nombre: 'Laptop',
|
|
106
|
+
descripcion: 'Laptop de alta gama',
|
|
107
|
+
precio: 1200.00,
|
|
108
|
+
categoria: 'Electrónica'
|
|
109
|
+
});
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
### Leer Registros
|
|
113
|
+
|
|
114
|
+
```javascript
|
|
115
|
+
// Encontrar todos los registros
|
|
116
|
+
const productos = await productoModel.find({});
|
|
117
|
+
|
|
118
|
+
// Encontrar un registro específico
|
|
119
|
+
const producto = await productoModel.findOne({ id: 1 });
|
|
120
|
+
|
|
121
|
+
// Encontrar con condiciones
|
|
122
|
+
const electronicos = await productoModel.find({ categoria: 'Electrónica' });
|
|
123
|
+
|
|
124
|
+
// Encontrar con opciones (orden, límites)
|
|
125
|
+
const productosOrdenados = await productoModel.find(
|
|
126
|
+
{ categoria: 'Electrónica' },
|
|
127
|
+
{ orderBy: 'precio DESC', limit: 10 }
|
|
128
|
+
);
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
### Actualizar Registros
|
|
132
|
+
|
|
133
|
+
```javascript
|
|
134
|
+
// Actualizar un registro
|
|
135
|
+
const filasAfectadas = await productoModel.update(
|
|
136
|
+
{ id: 1 }, // condiciones
|
|
137
|
+
{ precio: 1100.00 } // datos a actualizar
|
|
138
|
+
);
|
|
139
|
+
|
|
140
|
+
// Actualizar con validación personalizada
|
|
141
|
+
const resultado = await productoModel.updateUser(
|
|
142
|
+
{ id: 1 },
|
|
143
|
+
{ nombre: 'Laptop Actualizada' }
|
|
144
|
+
);
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
### Eliminar Registros
|
|
148
|
+
|
|
149
|
+
```javascript
|
|
150
|
+
// Eliminar registros que coincidan con condiciones
|
|
151
|
+
const filasEliminadas = await productoModel.delete({ categoria: 'Obsoleto' });
|
|
152
|
+
|
|
153
|
+
// Eliminar un registro específico
|
|
154
|
+
const filasEliminadas = await productoModel.delete({ id: 1 });
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
## Validación de Datos
|
|
158
|
+
|
|
159
|
+
Los modelos incluyen un sistema de validación flexible:
|
|
160
|
+
|
|
161
|
+
### Validación Personalizada
|
|
162
|
+
|
|
163
|
+
```javascript
|
|
164
|
+
class ProductoModel extends ModelBase {
|
|
165
|
+
validate(operation, data) {
|
|
166
|
+
const result = { isValid: true, errors: [] };
|
|
167
|
+
|
|
168
|
+
// Validaciones específicas para creación
|
|
169
|
+
if (operation === 'create') {
|
|
170
|
+
if (!data.nombre) {
|
|
171
|
+
result.isValid = false;
|
|
172
|
+
result.errors.push('Nombre es requerido');
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
if (!data.precio || data.precio <= 0) {
|
|
176
|
+
result.isValid = false;
|
|
177
|
+
result.errors.push('Precio debe ser positivo');
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// Validaciones para actualización
|
|
182
|
+
if (operation === 'update') {
|
|
183
|
+
if (data.hasOwnProperty('nombre') && !data.nombre) {
|
|
184
|
+
result.isValid = false;
|
|
185
|
+
result.errors.push('Nombre no puede estar vacío si se proporciona');
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
if (data.hasOwnProperty('precio') && (data.precio <= 0)) {
|
|
189
|
+
result.isValid = false;
|
|
190
|
+
result.errors.push('Precio debe ser positivo');
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
return result;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
### Validación Automática
|
|
200
|
+
|
|
201
|
+
Los modelos realizan validaciones automáticas según las reglas definidas en el método `validate`.
|
|
202
|
+
|
|
203
|
+
## Comunicación con Controladores
|
|
204
|
+
|
|
205
|
+
### Registro de Controladores
|
|
206
|
+
|
|
207
|
+
Los modelos pueden comunicarse con controladores registrándolos:
|
|
208
|
+
|
|
209
|
+
```javascript
|
|
210
|
+
// En el modelo
|
|
211
|
+
class ProductoModel extends ModelBase {
|
|
212
|
+
// ...
|
|
213
|
+
|
|
214
|
+
async notifyControllerOnCreate(producto) {
|
|
215
|
+
// Notificar a todos los controladores registrados
|
|
216
|
+
for (const controller of this.getControllers()) {
|
|
217
|
+
if (typeof controller.onProductoCreated === 'function') {
|
|
218
|
+
controller.onProductoCreated(producto);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
// En el controlador
|
|
225
|
+
class ProductoController extends ControllerBase {
|
|
226
|
+
constructor() {
|
|
227
|
+
super();
|
|
228
|
+
|
|
229
|
+
// Registrar este controlador con el modelo
|
|
230
|
+
productoModel.registerController(this);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
onProductoCreated(producto) {
|
|
234
|
+
console.log('Nuevo producto creado:', producto);
|
|
235
|
+
// Lógica adicional cuando se crea un producto
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
async crearProducto(req, res) {
|
|
239
|
+
try {
|
|
240
|
+
const producto = await productoModel.create(req.body);
|
|
241
|
+
res.status(201).json({ success: true, data: producto });
|
|
242
|
+
} catch (error) {
|
|
243
|
+
res.status(500).json({ success: false, error: error.message });
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
### Comunicación Bidireccional
|
|
250
|
+
|
|
251
|
+
La comunicación es bidireccional, permitiendo que tanto modelos como controladores se comuniquen entre sí.
|
|
252
|
+
|
|
253
|
+
## Uso de Adaptadores
|
|
254
|
+
|
|
255
|
+
### Adaptador de Memoria
|
|
256
|
+
|
|
257
|
+
Útil para pruebas o aplicaciones pequeñas:
|
|
258
|
+
|
|
259
|
+
```javascript
|
|
260
|
+
const { MemoryAdapter } = require('jerkjs');
|
|
261
|
+
|
|
262
|
+
const memoryAdapter = new MemoryAdapter();
|
|
263
|
+
productoModel.setAdapter(memoryAdapter);
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
### Adaptadores Personalizados
|
|
267
|
+
|
|
268
|
+
Puedes crear adaptadores personalizados extendiendo `GenericAdapter`:
|
|
269
|
+
|
|
270
|
+
```javascript
|
|
271
|
+
const { GenericAdapter } = require('jerkjs');
|
|
272
|
+
|
|
273
|
+
class MiAdaptadorPersonalizado extends GenericAdapter {
|
|
274
|
+
constructor(options = {}) {
|
|
275
|
+
super({ ...options, type: 'mi_adaptador' });
|
|
276
|
+
// Inicializar conexión
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
async create(tableName, data) {
|
|
280
|
+
// Implementar lógica de creación
|
|
281
|
+
return { id: 1, ...data }; // Ejemplo
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
async find(tableName, conditions, options) {
|
|
285
|
+
// Implementar lógica de búsqueda
|
|
286
|
+
return []; // Ejemplo
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
// Implementar otros métodos...
|
|
290
|
+
}
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
## Hooks en Modelos
|
|
294
|
+
|
|
295
|
+
Los modelos pueden participar en el sistema de hooks del framework:
|
|
296
|
+
|
|
297
|
+
### Hooks Disponibles
|
|
298
|
+
|
|
299
|
+
- `model_before_create`: Antes de crear un registro
|
|
300
|
+
- `model_after_create`: Después de crear un registro
|
|
301
|
+
- `model_before_find`: Antes de buscar registros
|
|
302
|
+
- `model_after_find`: Después de buscar registros
|
|
303
|
+
- `model_before_update`: Antes de actualizar registros
|
|
304
|
+
- `model_after_update`: Después de actualizar registros
|
|
305
|
+
- `model_before_delete`: Antes de eliminar registros
|
|
306
|
+
- `model_after_delete`: Después de eliminar registros
|
|
307
|
+
|
|
308
|
+
### Uso de Hooks
|
|
309
|
+
|
|
310
|
+
```javascript
|
|
311
|
+
// Registrar un hook
|
|
312
|
+
hooks.addAction('model_after_create', (result, modelName) => {
|
|
313
|
+
console.log(`Registro creado en modelo ${modelName}:`, result);
|
|
314
|
+
});
|
|
315
|
+
|
|
316
|
+
// Filtrar datos antes de crear
|
|
317
|
+
hooks.addFilter('model_before_create', (data, modelName) => {
|
|
318
|
+
// Agregar marca de tiempo
|
|
319
|
+
return {
|
|
320
|
+
...data,
|
|
321
|
+
createdAt: new Date(),
|
|
322
|
+
updatedAt: new Date()
|
|
323
|
+
};
|
|
324
|
+
});
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
## Ejemplos Prácticos
|
|
328
|
+
|
|
329
|
+
### Ejemplo Completo de Modelo de Usuario
|
|
330
|
+
|
|
331
|
+
```javascript
|
|
332
|
+
const { ModelBase } = require('jerkjs');
|
|
333
|
+
|
|
334
|
+
class UsuarioModel extends ModelBase {
|
|
335
|
+
constructor(options = {}) {
|
|
336
|
+
super({
|
|
337
|
+
...options,
|
|
338
|
+
tableName: options.tableName || 'usuarios'
|
|
339
|
+
});
|
|
340
|
+
|
|
341
|
+
this.fields = {
|
|
342
|
+
id: { type: 'integer', primaryKey: true, autoIncrement: true },
|
|
343
|
+
username: { type: 'string', required: true, unique: true },
|
|
344
|
+
email: { type: 'string', required: true, unique: true },
|
|
345
|
+
password: { type: 'string', required: true },
|
|
346
|
+
rol: { type: 'string', default: 'usuario' },
|
|
347
|
+
activo: { type: 'boolean', default: true },
|
|
348
|
+
createdAt: { type: 'datetime', default: 'CURRENT_TIMESTAMP' },
|
|
349
|
+
updatedAt: { type: 'datetime', default: 'CURRENT_TIMESTAMP' }
|
|
350
|
+
};
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
validate(operation, data) {
|
|
354
|
+
const result = { isValid: true, errors: [] };
|
|
355
|
+
|
|
356
|
+
if (operation === 'create' || operation === 'update') {
|
|
357
|
+
if (operation === 'create' && !data.username) {
|
|
358
|
+
result.isValid = false;
|
|
359
|
+
result.errors.push('Username es requerido');
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
if (operation === 'create' && !data.email) {
|
|
363
|
+
result.isValid = false;
|
|
364
|
+
result.errors.push('Email es requerido');
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
if (operation === 'create' && !data.password) {
|
|
368
|
+
result.isValid = false;
|
|
369
|
+
result.errors.push('Password es requerido');
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
// Validar formato de email
|
|
373
|
+
if (data.email && !this.isValidEmail(data.email)) {
|
|
374
|
+
result.isValid = false;
|
|
375
|
+
result.errors.push('Formato de email inválido');
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
return result;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
isValidEmail(email) {
|
|
383
|
+
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
384
|
+
return emailRegex.test(email);
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
async findByUsername(username) {
|
|
388
|
+
return await this.findOne({ username });
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
async findByEmail(email) {
|
|
392
|
+
return await this.findOne({ email });
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
async crearUsuario(datos) {
|
|
396
|
+
// Validar datos
|
|
397
|
+
const validacion = this.validate('create', datos);
|
|
398
|
+
if (!validacion.isValid) {
|
|
399
|
+
throw new Error(`Validación fallida: ${validacion.errors.join(', ')}`);
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
// Encriptar contraseña
|
|
403
|
+
if (datos.password) {
|
|
404
|
+
datos.password = await this.encriptarPassword(datos.password);
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
return await this.create(datos);
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
async encriptarPassword(password) {
|
|
411
|
+
const bcrypt = require('bcrypt');
|
|
412
|
+
const saltRounds = 10;
|
|
413
|
+
return await bcrypt.hash(password, saltRounds);
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
async verificarPassword(password, hashedPassword) {
|
|
417
|
+
const bcrypt = require('bcrypt');
|
|
418
|
+
return await bcrypt.compare(password, hashedPassword);
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
```
|
|
422
|
+
|
|
423
|
+
### Uso en un Controlador
|
|
424
|
+
|
|
425
|
+
```javascript
|
|
426
|
+
const ControllerBase = require('jerkjs').ControllerBase;
|
|
427
|
+
const UsuarioModel = require('./models/UsuarioModel');
|
|
428
|
+
const { ModelManager, MemoryAdapter } = require('jerkjs');
|
|
429
|
+
|
|
430
|
+
class UsuarioController extends ControllerBase {
|
|
431
|
+
constructor() {
|
|
432
|
+
super();
|
|
433
|
+
|
|
434
|
+
// Configurar modelo de usuario
|
|
435
|
+
this.modelManager = new ModelManager();
|
|
436
|
+
const memoryAdapter = new MemoryAdapter();
|
|
437
|
+
this.modelManager.registerAdapter('memory', memoryAdapter);
|
|
438
|
+
|
|
439
|
+
this.usuarioModel = new UsuarioModel({
|
|
440
|
+
adapter: memoryAdapter,
|
|
441
|
+
tableName: 'usuarios'
|
|
442
|
+
});
|
|
443
|
+
|
|
444
|
+
// Registrar este controlador con el modelo
|
|
445
|
+
this.usuarioModel.registerController(this);
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
async crearUsuario(req, res) {
|
|
449
|
+
try {
|
|
450
|
+
const usuario = await this.usuarioModel.crearUsuario(req.body);
|
|
451
|
+
res.status(201).json({ success: true, data: usuario });
|
|
452
|
+
} catch (error) {
|
|
453
|
+
res.status(400).json({ success: false, error: error.message });
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
async obtenerUsuarioPorUsername(req, res) {
|
|
458
|
+
try {
|
|
459
|
+
const username = req.params.username;
|
|
460
|
+
const usuario = await this.usuarioModel.findByUsername(username);
|
|
461
|
+
|
|
462
|
+
if (!usuario) {
|
|
463
|
+
res.status(404).json({ success: false, error: 'Usuario no encontrado' });
|
|
464
|
+
return;
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
res.status(200).json({ success: true, data: usuario });
|
|
468
|
+
} catch (error) {
|
|
469
|
+
res.status(500).json({ success: false, error: error.message });
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
async listarUsuarios(req, res) {
|
|
474
|
+
try {
|
|
475
|
+
const usuarios = await this.usuarioModel.find({});
|
|
476
|
+
res.status(200).json({
|
|
477
|
+
success: true,
|
|
478
|
+
data: usuarios,
|
|
479
|
+
count: usuarios.length
|
|
480
|
+
});
|
|
481
|
+
} catch (error) {
|
|
482
|
+
res.status(500).json({ success: false, error: error.message });
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
```
|
|
487
|
+
|
|
488
|
+
## Uso de Modelos en Controladores con loadModel
|
|
489
|
+
|
|
490
|
+
JERK Framework proporciona un helper `loadModel` en el ControllerBase para facilitar la carga y uso de modelos en los controladores:
|
|
491
|
+
|
|
492
|
+
```javascript
|
|
493
|
+
const ControllerBase = require('jerkjs').ControllerBase;
|
|
494
|
+
|
|
495
|
+
class ProductoController extends ControllerBase {
|
|
496
|
+
constructor() {
|
|
497
|
+
super();
|
|
498
|
+
|
|
499
|
+
// Cargar el modelo de producto
|
|
500
|
+
this.productoModel = this.loadModel('ProductoModel', {
|
|
501
|
+
// Opciones del modelo
|
|
502
|
+
});
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
async listarProductos(req, res) {
|
|
506
|
+
try {
|
|
507
|
+
// Usar el modelo para obtener productos
|
|
508
|
+
const productos = await this.productoModel.find({});
|
|
509
|
+
|
|
510
|
+
res.status(200).json({ success: true, data: productos });
|
|
511
|
+
} catch (error) {
|
|
512
|
+
res.status(500).json({ success: false, error: error.message });
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
async obtenerProductoPorId(req, res) {
|
|
517
|
+
try {
|
|
518
|
+
const productId = req.params.id;
|
|
519
|
+
|
|
520
|
+
// Usar el modelo para encontrar un producto específico
|
|
521
|
+
const producto = await this.productoModel.findOne({ id: productId });
|
|
522
|
+
|
|
523
|
+
if (!producto) {
|
|
524
|
+
res.status(404).json({ success: false, error: 'Producto no encontrado' });
|
|
525
|
+
return;
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
res.status(200).json({ success: true, data: producto });
|
|
529
|
+
} catch (error) {
|
|
530
|
+
res.status(500).json({ success: false, error: error.message });
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
async crearProducto(req, res) {
|
|
535
|
+
try {
|
|
536
|
+
// Usar el modelo para crear un nuevo producto
|
|
537
|
+
const nuevoProducto = await this.productoModel.create(req.body);
|
|
538
|
+
|
|
539
|
+
res.status(201).json({ success: true, data: nuevoProducto });
|
|
540
|
+
} catch (error) {
|
|
541
|
+
res.status(400).json({ success: false, error: error.message });
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
module.exports = new ProductoController();
|
|
547
|
+
```
|
|
548
|
+
|
|
549
|
+
### Métodos Disponibles
|
|
550
|
+
|
|
551
|
+
- `loadModel(modelName, options)`: Carga un modelo para su uso en el controlador
|
|
552
|
+
- `getModel(modelName)`: Obtiene un modelo previamente cargado
|
|
553
|
+
|
|
554
|
+
### Rutas de Carga de Modelos
|
|
555
|
+
|
|
556
|
+
El helper `loadModel` busca modelos en las siguientes ubicaciones (en orden):
|
|
557
|
+
|
|
558
|
+
1. `../../../models/${modelName}` (relativo a la carpeta actual)
|
|
559
|
+
2. `../models/${modelName}` (relativo al controlador)
|
|
560
|
+
3. `../../models/${modelName}` (relativo a la raíz del proyecto)
|
|
561
|
+
|
|
562
|
+
## Conclusión
|
|
563
|
+
|
|
564
|
+
Los modelos en JERK Framework proporcionan una capa de abstracción potente y flexible para interactuar con datos. Con soporte para múltiples adaptadores, validación integrada, comunicación bidireccional con controladores y hooks para extensibilidad, los modelos permiten construir aplicaciones robustas y escalables.
|
|
565
|
+
|
|
566
|
+
La arquitectura modular permite adaptar los modelos a diferentes necesidades de almacenamiento y lógica de negocio, manteniendo al mismo tiempo una interfaz consistente y fácil de usar.
|