outlet-orm 2.5.0 → 2.5.1
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/README.md +24 -0
- package/package.json +1 -1
- package/src/Model.js +683 -659
- package/src/QueryBuilder.js +712 -710
- package/types/index.d.ts +4 -0
package/src/Model.js
CHANGED
|
@@ -1,659 +1,683 @@
|
|
|
1
|
-
const QueryBuilder = require('./QueryBuilder');
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Base Model class inspired by Laravel Eloquent
|
|
5
|
-
*/
|
|
6
|
-
class Model {
|
|
7
|
-
static table = '';
|
|
8
|
-
static primaryKey = 'id';
|
|
9
|
-
static timestamps = true;
|
|
10
|
-
static fillable = [];
|
|
11
|
-
static hidden = [];
|
|
12
|
-
static casts = {};
|
|
13
|
-
static connection = null;
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* Ensure a default database connection exists.
|
|
17
|
-
* If none is set, it will be initialized from environment (.env) lazily.
|
|
18
|
-
*/
|
|
19
|
-
static ensureConnection() {
|
|
20
|
-
if (!this.connection) {
|
|
21
|
-
// Lazy require to avoid circular dependencies
|
|
22
|
-
const DatabaseConnection = require('./DatabaseConnection');
|
|
23
|
-
this.connection = new DatabaseConnection();
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* Set the default database connection for all models
|
|
29
|
-
* @param {DatabaseConnection} connection
|
|
30
|
-
*/
|
|
31
|
-
static setConnection(connection) {
|
|
32
|
-
this.connection = connection;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* Set the morph map for polymorphic relations
|
|
37
|
-
* @param {Object} map
|
|
38
|
-
*/
|
|
39
|
-
static setMorphMap(map) {
|
|
40
|
-
this.morphMap = map;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
constructor(attributes = {}) {
|
|
44
|
-
// Auto-initialize connection on first model instantiation if missing
|
|
45
|
-
this.constructor.ensureConnection();
|
|
46
|
-
this.attributes = {};
|
|
47
|
-
this.original = {};
|
|
48
|
-
this.relations = {};
|
|
49
|
-
this.touches = [];
|
|
50
|
-
this.exists = false;
|
|
51
|
-
this.
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
*
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
*
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
*
|
|
77
|
-
* @
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
*
|
|
86
|
-
* @
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
*
|
|
95
|
-
* @param {string
|
|
96
|
-
* @param {any}
|
|
97
|
-
* @
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
*
|
|
110
|
-
* @
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
*
|
|
120
|
-
* @
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
*
|
|
130
|
-
* @
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
*
|
|
139
|
-
* @param {
|
|
140
|
-
* @param {
|
|
141
|
-
* @
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
*
|
|
153
|
-
* @param {
|
|
154
|
-
* @
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
*
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
*
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
*
|
|
179
|
-
* @param {string}
|
|
180
|
-
* @
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
*
|
|
189
|
-
* @
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
*
|
|
198
|
-
* @
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
*
|
|
207
|
-
* @param {number}
|
|
208
|
-
* @
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
*
|
|
217
|
-
* @param {
|
|
218
|
-
* @
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
*
|
|
227
|
-
* @
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
*
|
|
236
|
-
* @
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
*
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
*
|
|
253
|
-
* @
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
*
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
*
|
|
300
|
-
* @param {
|
|
301
|
-
* @
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
return
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
this.
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
*
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
this.setAttribute('
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
return
|
|
448
|
-
}
|
|
449
|
-
|
|
450
|
-
/**
|
|
451
|
-
*
|
|
452
|
-
* @returns {Object}
|
|
453
|
-
*/
|
|
454
|
-
|
|
455
|
-
const
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
}
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
const
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
const
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
*
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
*
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
*
|
|
628
|
-
* @param {
|
|
629
|
-
* @
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
*
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
1
|
+
const QueryBuilder = require('./QueryBuilder');
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Base Model class inspired by Laravel Eloquent
|
|
5
|
+
*/
|
|
6
|
+
class Model {
|
|
7
|
+
static table = '';
|
|
8
|
+
static primaryKey = 'id';
|
|
9
|
+
static timestamps = true;
|
|
10
|
+
static fillable = [];
|
|
11
|
+
static hidden = [];
|
|
12
|
+
static casts = {};
|
|
13
|
+
static connection = null;
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Ensure a default database connection exists.
|
|
17
|
+
* If none is set, it will be initialized from environment (.env) lazily.
|
|
18
|
+
*/
|
|
19
|
+
static ensureConnection() {
|
|
20
|
+
if (!this.connection) {
|
|
21
|
+
// Lazy require to avoid circular dependencies
|
|
22
|
+
const DatabaseConnection = require('./DatabaseConnection');
|
|
23
|
+
this.connection = new DatabaseConnection();
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Set the default database connection for all models
|
|
29
|
+
* @param {DatabaseConnection} connection
|
|
30
|
+
*/
|
|
31
|
+
static setConnection(connection) {
|
|
32
|
+
this.connection = connection;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Set the morph map for polymorphic relations
|
|
37
|
+
* @param {Object} map
|
|
38
|
+
*/
|
|
39
|
+
static setMorphMap(map) {
|
|
40
|
+
this.morphMap = map;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
constructor(attributes = {}) {
|
|
44
|
+
// Auto-initialize connection on first model instantiation if missing
|
|
45
|
+
this.constructor.ensureConnection();
|
|
46
|
+
this.attributes = {};
|
|
47
|
+
this.original = {};
|
|
48
|
+
this.relations = {};
|
|
49
|
+
this.touches = [];
|
|
50
|
+
this.exists = false;
|
|
51
|
+
this._showHidden = false;
|
|
52
|
+
this.fill(attributes);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// ==================== Query Builder ====================
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Begin querying the model
|
|
59
|
+
* @returns {QueryBuilder}
|
|
60
|
+
*/
|
|
61
|
+
static query() {
|
|
62
|
+
// Ensure a connection exists even when using static APIs without instantiation
|
|
63
|
+
this.ensureConnection();
|
|
64
|
+
return new QueryBuilder(this);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Get all records
|
|
69
|
+
* @returns {Promise<Array<Model>>}
|
|
70
|
+
*/
|
|
71
|
+
static all() {
|
|
72
|
+
return this.query().get();
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Find a model by its primary key
|
|
77
|
+
* @param {any} id
|
|
78
|
+
* @returns {Promise<Model|null>}
|
|
79
|
+
*/
|
|
80
|
+
static find(id) {
|
|
81
|
+
return this.query().where(this.primaryKey, id).first();
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Find a model by its primary key or throw an error
|
|
86
|
+
* @param {any} id
|
|
87
|
+
* @returns {Promise<Model>}
|
|
88
|
+
*/
|
|
89
|
+
static findOrFail(id) {
|
|
90
|
+
return this.query().where(this.primaryKey, id).firstOrFail();
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Add a where clause
|
|
95
|
+
* @param {string} column
|
|
96
|
+
* @param {string|any} operator
|
|
97
|
+
* @param {any} value
|
|
98
|
+
* @returns {QueryBuilder}
|
|
99
|
+
*/
|
|
100
|
+
static where(column, operator, value) {
|
|
101
|
+
if (arguments.length === 2) {
|
|
102
|
+
value = operator;
|
|
103
|
+
operator = '=';
|
|
104
|
+
}
|
|
105
|
+
return this.query().where(column, operator, value);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Create a new model and save it
|
|
110
|
+
* @param {Object} attributes
|
|
111
|
+
* @returns {Promise<Model>}
|
|
112
|
+
*/
|
|
113
|
+
static create(attributes) {
|
|
114
|
+
const instance = new this(attributes);
|
|
115
|
+
return instance.save();
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Insert data without creating model instances
|
|
120
|
+
* @param {Object|Array<Object>} data
|
|
121
|
+
* @returns {Promise<any>}
|
|
122
|
+
*/
|
|
123
|
+
static async insert(data) {
|
|
124
|
+
const query = this.query();
|
|
125
|
+
return query.insert(data);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Update records
|
|
130
|
+
* @param {Object} attributes
|
|
131
|
+
* @returns {Promise<any>}
|
|
132
|
+
*/
|
|
133
|
+
static async update(attributes) {
|
|
134
|
+
return this.query().update(attributes);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Update by primary key and fetch the updated model (optionally with relations)
|
|
139
|
+
* @param {any} id
|
|
140
|
+
* @param {Object} attributes
|
|
141
|
+
* @param {string[]} [relations]
|
|
142
|
+
* @returns {Promise<Model|null>}
|
|
143
|
+
*/
|
|
144
|
+
static async updateAndFetchById(id, attributes, relations = []) {
|
|
145
|
+
await this.query().where(this.primaryKey, id).update(attributes);
|
|
146
|
+
const qb = this.query().where(this.primaryKey, id);
|
|
147
|
+
if (relations && relations.length) qb.with(...relations);
|
|
148
|
+
return qb.first();
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Update by primary key only (convenience)
|
|
153
|
+
* @param {any} id
|
|
154
|
+
* @param {Object} attributes
|
|
155
|
+
* @returns {Promise<any>}
|
|
156
|
+
*/
|
|
157
|
+
static async updateById(id, attributes) {
|
|
158
|
+
return this.query().where(this.primaryKey, id).update(attributes);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Delete records
|
|
163
|
+
* @returns {Promise<any>}
|
|
164
|
+
*/
|
|
165
|
+
static async delete() {
|
|
166
|
+
return this.query().delete();
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Get the first record
|
|
171
|
+
* @returns {Promise<Model|null>}
|
|
172
|
+
*/
|
|
173
|
+
static first() {
|
|
174
|
+
return this.query().first();
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Add an order by clause
|
|
179
|
+
* @param {string} column
|
|
180
|
+
* @param {string} direction
|
|
181
|
+
* @returns {QueryBuilder}
|
|
182
|
+
*/
|
|
183
|
+
static orderBy(column, direction = 'asc') {
|
|
184
|
+
return this.query().orderBy(column, direction);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Limit the number of results
|
|
189
|
+
* @param {number} value
|
|
190
|
+
* @returns {QueryBuilder}
|
|
191
|
+
*/
|
|
192
|
+
static limit(value) {
|
|
193
|
+
return this.query().limit(value);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Offset the results
|
|
198
|
+
* @param {number} value
|
|
199
|
+
* @returns {QueryBuilder}
|
|
200
|
+
*/
|
|
201
|
+
static offset(value) {
|
|
202
|
+
return this.query().offset(value);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Paginate the results
|
|
207
|
+
* @param {number} page
|
|
208
|
+
* @param {number} perPage
|
|
209
|
+
* @returns {Promise<Object>}
|
|
210
|
+
*/
|
|
211
|
+
static paginate(page = 1, perPage = 15) {
|
|
212
|
+
return this.query().paginate(page, perPage);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* Add a where in clause
|
|
217
|
+
* @param {string} column
|
|
218
|
+
* @param {Array} values
|
|
219
|
+
* @returns {QueryBuilder}
|
|
220
|
+
*/
|
|
221
|
+
static whereIn(column, values) {
|
|
222
|
+
return this.query().whereIn(column, values);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* Add a where null clause
|
|
227
|
+
* @param {string} column
|
|
228
|
+
* @returns {QueryBuilder}
|
|
229
|
+
*/
|
|
230
|
+
static whereNull(column) {
|
|
231
|
+
return this.query().whereNull(column);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* Add a where not null clause
|
|
236
|
+
* @param {string} column
|
|
237
|
+
* @returns {QueryBuilder}
|
|
238
|
+
*/
|
|
239
|
+
static whereNotNull(column) {
|
|
240
|
+
return this.query().whereNotNull(column);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* Count records
|
|
245
|
+
* @returns {Promise<number>}
|
|
246
|
+
*/
|
|
247
|
+
static count() {
|
|
248
|
+
return this.query().count();
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* Eager load relations on the query
|
|
253
|
+
* @param {...string} relations
|
|
254
|
+
* @returns {QueryBuilder}
|
|
255
|
+
*/
|
|
256
|
+
static with(...relations) {
|
|
257
|
+
return this.query().with(...relations);
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
/**
|
|
261
|
+
* Include hidden attributes in query results
|
|
262
|
+
* @returns {QueryBuilder}
|
|
263
|
+
*/
|
|
264
|
+
static withHidden() {
|
|
265
|
+
const query = this.query();
|
|
266
|
+
query._showHidden = true;
|
|
267
|
+
return query;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
/**
|
|
271
|
+
* Control visibility of hidden attributes in query results
|
|
272
|
+
* @param {boolean} show - If false (default), hidden attributes will be hidden. If true, they will be shown.
|
|
273
|
+
* @returns {QueryBuilder}
|
|
274
|
+
*/
|
|
275
|
+
static withoutHidden(show = false) {
|
|
276
|
+
const query = this.query();
|
|
277
|
+
query._showHidden = show;
|
|
278
|
+
return query;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
// ==================== Instance Methods ====================
|
|
282
|
+
|
|
283
|
+
/**
|
|
284
|
+
* Fill the model with attributes
|
|
285
|
+
* @param {Object} attributes
|
|
286
|
+
* @returns {this}
|
|
287
|
+
*/
|
|
288
|
+
fill(attributes) {
|
|
289
|
+
for (const [key, value] of Object.entries(attributes)) {
|
|
290
|
+
if (this.constructor.fillable.length === 0 || this.constructor.fillable.includes(key)) {
|
|
291
|
+
this.setAttribute(key, value);
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
return this;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
/**
|
|
298
|
+
* Set an attribute
|
|
299
|
+
* @param {string} key
|
|
300
|
+
* @param {any} value
|
|
301
|
+
* @returns {this}
|
|
302
|
+
*/
|
|
303
|
+
setAttribute(key, value) {
|
|
304
|
+
this.attributes[key] = this.castAttribute(key, value);
|
|
305
|
+
return this;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
/**
|
|
309
|
+
* Get an attribute
|
|
310
|
+
* @param {string} key
|
|
311
|
+
* @returns {any}
|
|
312
|
+
*/
|
|
313
|
+
getAttribute(key) {
|
|
314
|
+
if (this.relations[key]) {
|
|
315
|
+
return this.relations[key];
|
|
316
|
+
}
|
|
317
|
+
return this.castAttribute(key, this.attributes[key]);
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
/**
|
|
321
|
+
* Cast an attribute to the proper type
|
|
322
|
+
* @param {string} key
|
|
323
|
+
* @param {any} value
|
|
324
|
+
* @returns {any}
|
|
325
|
+
*/
|
|
326
|
+
castAttribute(key, value) {
|
|
327
|
+
const cast = this.constructor.casts[key];
|
|
328
|
+
if (!cast || value === null || value === undefined) return value;
|
|
329
|
+
|
|
330
|
+
switch (cast) {
|
|
331
|
+
case 'int':
|
|
332
|
+
case 'integer':
|
|
333
|
+
return parseInt(value, 10);
|
|
334
|
+
case 'float':
|
|
335
|
+
case 'double':
|
|
336
|
+
return parseFloat(value);
|
|
337
|
+
case 'string':
|
|
338
|
+
return String(value);
|
|
339
|
+
case 'bool':
|
|
340
|
+
case 'boolean':
|
|
341
|
+
return Boolean(value);
|
|
342
|
+
case 'array':
|
|
343
|
+
case 'json':
|
|
344
|
+
return typeof value === 'string' ? JSON.parse(value) : value;
|
|
345
|
+
case 'date':
|
|
346
|
+
return value instanceof Date ? value : new Date(value);
|
|
347
|
+
default:
|
|
348
|
+
return value;
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
/**
|
|
353
|
+
* Save the model
|
|
354
|
+
* @returns {Promise<this>}
|
|
355
|
+
*/
|
|
356
|
+
async save() {
|
|
357
|
+
if (this.exists) {
|
|
358
|
+
return this.performUpdate();
|
|
359
|
+
}
|
|
360
|
+
return this.performInsert();
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
/**
|
|
364
|
+
* Perform an insert operation
|
|
365
|
+
* @returns {Promise<this>}
|
|
366
|
+
*/
|
|
367
|
+
async performInsert() {
|
|
368
|
+
if (this.constructor.timestamps) {
|
|
369
|
+
const now = new Date();
|
|
370
|
+
this.setAttribute('created_at', now);
|
|
371
|
+
this.setAttribute('updated_at', now);
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
const data = this.attributes;
|
|
375
|
+
const result = await this.constructor.connection.insert(this.constructor.table, data);
|
|
376
|
+
|
|
377
|
+
this.setAttribute(this.constructor.primaryKey, result.insertId);
|
|
378
|
+
this.exists = true;
|
|
379
|
+
this.original = { ...this.attributes };
|
|
380
|
+
|
|
381
|
+
await this.touchParents();
|
|
382
|
+
|
|
383
|
+
return this;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
/**
|
|
387
|
+
* Perform an update operation
|
|
388
|
+
* @returns {Promise<this>}
|
|
389
|
+
*/
|
|
390
|
+
async performUpdate() {
|
|
391
|
+
if (this.constructor.timestamps) {
|
|
392
|
+
this.setAttribute('updated_at', new Date());
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
const dirty = this.getDirty();
|
|
396
|
+
if (Object.keys(dirty).length === 0) {
|
|
397
|
+
return this;
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
await this.constructor.connection.update(
|
|
401
|
+
this.constructor.table,
|
|
402
|
+
dirty,
|
|
403
|
+
{ [this.constructor.primaryKey]: this.getAttribute(this.constructor.primaryKey) }
|
|
404
|
+
);
|
|
405
|
+
|
|
406
|
+
this.original = { ...this.attributes };
|
|
407
|
+
|
|
408
|
+
await this.touchParents();
|
|
409
|
+
|
|
410
|
+
return this;
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
/**
|
|
414
|
+
* Touch parent models for belongsTo relations with touches enabled
|
|
415
|
+
* @returns {Promise<void>}
|
|
416
|
+
*/
|
|
417
|
+
async touchParents() {
|
|
418
|
+
for (const relation of this.touches) {
|
|
419
|
+
if (relation.touchesParent) {
|
|
420
|
+
const foreignKeyValue = this.getAttribute(relation.foreignKey);
|
|
421
|
+
if (foreignKeyValue) {
|
|
422
|
+
await this.constructor.connection.update(
|
|
423
|
+
relation.related.table,
|
|
424
|
+
{ updated_at: new Date() },
|
|
425
|
+
{ [relation.ownerKey]: foreignKeyValue }
|
|
426
|
+
);
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
/**
|
|
433
|
+
* Delete the model
|
|
434
|
+
* @returns {Promise<boolean>}
|
|
435
|
+
*/
|
|
436
|
+
async destroy() {
|
|
437
|
+
if (!this.exists) {
|
|
438
|
+
return false;
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
await this.constructor.connection.delete(
|
|
442
|
+
this.constructor.table,
|
|
443
|
+
{ [this.constructor.primaryKey]: this.getAttribute(this.constructor.primaryKey) }
|
|
444
|
+
);
|
|
445
|
+
|
|
446
|
+
this.exists = false;
|
|
447
|
+
return true;
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
/**
|
|
451
|
+
* Get the attributes that have been changed
|
|
452
|
+
* @returns {Object}
|
|
453
|
+
*/
|
|
454
|
+
getDirty() {
|
|
455
|
+
const dirty = {};
|
|
456
|
+
for (const [key, value] of Object.entries(this.attributes)) {
|
|
457
|
+
if (JSON.stringify(value) !== JSON.stringify(this.original[key])) {
|
|
458
|
+
dirty[key] = value;
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
return dirty;
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
/**
|
|
465
|
+
* Check if the model has been modified
|
|
466
|
+
* @returns {boolean}
|
|
467
|
+
*/
|
|
468
|
+
isDirty() {
|
|
469
|
+
return Object.keys(this.getDirty()).length > 0;
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
/**
|
|
473
|
+
* Convert the model to JSON
|
|
474
|
+
* @returns {Object}
|
|
475
|
+
*/
|
|
476
|
+
toJSON() {
|
|
477
|
+
const json = { ...this.attributes };
|
|
478
|
+
|
|
479
|
+
// Hide specified attributes unless _showHidden is true
|
|
480
|
+
if (!this._showHidden) {
|
|
481
|
+
this.constructor.hidden.forEach(key => {
|
|
482
|
+
delete json[key];
|
|
483
|
+
});
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
// Add relations
|
|
487
|
+
Object.assign(json, this.relations);
|
|
488
|
+
|
|
489
|
+
return json;
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
/**
|
|
493
|
+
* Load one or multiple relations on this model instance.
|
|
494
|
+
* Supports dot-notation for nested relations (e.g., 'posts.comments').
|
|
495
|
+
* @param {...string|Array<string>} relations
|
|
496
|
+
* @returns {Promise<this>}
|
|
497
|
+
*/
|
|
498
|
+
async load(...relations) {
|
|
499
|
+
const list = relations.length === 1 && Array.isArray(relations[0])
|
|
500
|
+
? relations[0]
|
|
501
|
+
: relations;
|
|
502
|
+
|
|
503
|
+
for (const rel of list) {
|
|
504
|
+
if (typeof rel !== 'string' || !rel) continue;
|
|
505
|
+
await this._loadRelationPath(rel);
|
|
506
|
+
}
|
|
507
|
+
return this;
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
/**
|
|
511
|
+
* Internal: load a relation path with optional nesting (a.b.c)
|
|
512
|
+
* @param {string} path
|
|
513
|
+
* @private
|
|
514
|
+
*/
|
|
515
|
+
async _loadRelationPath(path) {
|
|
516
|
+
const segments = path.split('.');
|
|
517
|
+
const head = segments[0];
|
|
518
|
+
const tail = segments.slice(1).join('.');
|
|
519
|
+
|
|
520
|
+
const relationFn = this[head];
|
|
521
|
+
if (typeof relationFn !== 'function') return;
|
|
522
|
+
|
|
523
|
+
const relation = relationFn.call(this);
|
|
524
|
+
if (!relation || typeof relation.get !== 'function') return;
|
|
525
|
+
|
|
526
|
+
const value = await relation.get();
|
|
527
|
+
this.relations[head] = value;
|
|
528
|
+
|
|
529
|
+
if (tail) {
|
|
530
|
+
if (Array.isArray(value)) {
|
|
531
|
+
await Promise.all(
|
|
532
|
+
value.map(v => (v && typeof v.load === 'function') ? v.load(tail) : null)
|
|
533
|
+
);
|
|
534
|
+
} else if (value && typeof value.load === 'function') {
|
|
535
|
+
await value.load(tail);
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
// ==================== Relationships ====================
|
|
541
|
+
|
|
542
|
+
/**
|
|
543
|
+
* Define a one-to-one relationship
|
|
544
|
+
* @param {typeof Model} related
|
|
545
|
+
* @param {string} foreignKey
|
|
546
|
+
* @param {string} localKey
|
|
547
|
+
* @returns {HasOneRelation}
|
|
548
|
+
*/
|
|
549
|
+
hasOne(related, foreignKey, localKey) {
|
|
550
|
+
const HasOneRelation = require('./Relations/HasOneRelation');
|
|
551
|
+
localKey = localKey || this.constructor.primaryKey;
|
|
552
|
+
foreignKey = foreignKey || `${this.constructor.table.slice(0, -1)}_id`;
|
|
553
|
+
|
|
554
|
+
return new HasOneRelation(this, related, foreignKey, localKey);
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
/**
|
|
558
|
+
* Define a one-to-many relationship
|
|
559
|
+
* @param {typeof Model} related
|
|
560
|
+
* @param {string} foreignKey
|
|
561
|
+
* @param {string} localKey
|
|
562
|
+
* @returns {HasManyRelation}
|
|
563
|
+
*/
|
|
564
|
+
hasMany(related, foreignKey, localKey) {
|
|
565
|
+
const HasManyRelation = require('./Relations/HasManyRelation');
|
|
566
|
+
localKey = localKey || this.constructor.primaryKey;
|
|
567
|
+
foreignKey = foreignKey || `${this.constructor.table.slice(0, -1)}_id`;
|
|
568
|
+
|
|
569
|
+
return new HasManyRelation(this, related, foreignKey, localKey);
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
/**
|
|
573
|
+
* Define an inverse one-to-one or many relationship
|
|
574
|
+
* @param {typeof Model} related
|
|
575
|
+
* @param {string} foreignKey
|
|
576
|
+
* @param {string} ownerKey
|
|
577
|
+
* @returns {BelongsToRelation}
|
|
578
|
+
*/
|
|
579
|
+
belongsTo(related, foreignKey, ownerKey) {
|
|
580
|
+
const BelongsToRelation = require('./Relations/BelongsToRelation');
|
|
581
|
+
ownerKey = ownerKey || related.primaryKey;
|
|
582
|
+
foreignKey = foreignKey || `${related.table.slice(0, -1)}_id`;
|
|
583
|
+
|
|
584
|
+
return new BelongsToRelation(this, related, foreignKey, ownerKey);
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
/**
|
|
588
|
+
* Define a many-to-many relationship
|
|
589
|
+
* @param {typeof Model} related
|
|
590
|
+
* @param {string} pivot
|
|
591
|
+
* @param {string} foreignPivotKey
|
|
592
|
+
* @param {string} relatedPivotKey
|
|
593
|
+
* @param {string} parentKey
|
|
594
|
+
* @param {string} relatedKey
|
|
595
|
+
* @returns {BelongsToManyRelation}
|
|
596
|
+
*/
|
|
597
|
+
belongsToMany(related, pivot, foreignPivotKey, relatedPivotKey, parentKey, relatedKey) {
|
|
598
|
+
const BelongsToManyRelation = require('./Relations/BelongsToManyRelation');
|
|
599
|
+
return new BelongsToManyRelation(
|
|
600
|
+
this, related, pivot, foreignPivotKey, relatedPivotKey, parentKey, relatedKey
|
|
601
|
+
);
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
/**
|
|
605
|
+
* Define a has-many-through relationship
|
|
606
|
+
* @param {typeof Model} relatedFinal
|
|
607
|
+
* @param {typeof Model} through
|
|
608
|
+
* @param {string} [foreignKeyOnThrough]
|
|
609
|
+
* @param {string} [throughKeyOnFinal]
|
|
610
|
+
* @param {string} [localKey]
|
|
611
|
+
* @param {string} [throughLocalKey]
|
|
612
|
+
* @returns {HasManyThroughRelation}
|
|
613
|
+
*/
|
|
614
|
+
hasManyThrough(relatedFinal, through, foreignKeyOnThrough, throughKeyOnFinal, localKey, throughLocalKey) {
|
|
615
|
+
const HasManyThroughRelation = require('./Relations/HasManyThroughRelation');
|
|
616
|
+
return new HasManyThroughRelation(
|
|
617
|
+
this, relatedFinal, through, foreignKeyOnThrough, throughKeyOnFinal, localKey, throughLocalKey
|
|
618
|
+
);
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
/**
|
|
622
|
+
* Define a has-one-through relationship
|
|
623
|
+
* @param {typeof Model} relatedFinal
|
|
624
|
+
* @param {typeof Model} through
|
|
625
|
+
* @param {string} [foreignKeyOnThrough]
|
|
626
|
+
* @param {string} [throughKeyOnFinal]
|
|
627
|
+
* @param {string} [localKey]
|
|
628
|
+
* @param {string} [throughLocalKey]
|
|
629
|
+
* @returns {HasOneThroughRelation}
|
|
630
|
+
*/
|
|
631
|
+
hasOneThrough(relatedFinal, through, foreignKeyOnThrough, throughKeyOnFinal, localKey, throughLocalKey) {
|
|
632
|
+
const HasOneThroughRelation = require('./Relations/HasOneThroughRelation');
|
|
633
|
+
return new HasOneThroughRelation(
|
|
634
|
+
this, relatedFinal, through, foreignKeyOnThrough, throughKeyOnFinal, localKey, throughLocalKey
|
|
635
|
+
);
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
/**
|
|
639
|
+
* Define a polymorphic inverse relationship
|
|
640
|
+
* @param {string} name
|
|
641
|
+
* @param {string} [typeColumn]
|
|
642
|
+
* @param {string} [idColumn]
|
|
643
|
+
* @returns {MorphToRelation}
|
|
644
|
+
*/
|
|
645
|
+
morphTo(name, typeColumn, idColumn) {
|
|
646
|
+
const MorphToRelation = require('./Relations/MorphToRelation');
|
|
647
|
+
return new MorphToRelation(this, name, typeColumn, idColumn);
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
/**
|
|
651
|
+
* Define a polymorphic one-to-one relationship
|
|
652
|
+
* @param {typeof Model} related
|
|
653
|
+
* @param {string} morphType
|
|
654
|
+
* @param {string} [foreignKey]
|
|
655
|
+
* @param {string} [localKey]
|
|
656
|
+
* @returns {MorphOneRelation}
|
|
657
|
+
*/
|
|
658
|
+
morphOne(related, morphType, foreignKey, localKey) {
|
|
659
|
+
const MorphOneRelation = require('./Relations/MorphOneRelation');
|
|
660
|
+
localKey = localKey || this.constructor.primaryKey;
|
|
661
|
+
foreignKey = foreignKey || `${morphType}_id`;
|
|
662
|
+
|
|
663
|
+
return new MorphOneRelation(this, related, morphType, foreignKey, localKey);
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
/**
|
|
667
|
+
* Define a polymorphic one-to-many relationship
|
|
668
|
+
* @param {typeof Model} related
|
|
669
|
+
* @param {string} morphType
|
|
670
|
+
* @param {string} [foreignKey]
|
|
671
|
+
* @param {string} [localKey]
|
|
672
|
+
* @returns {MorphManyRelation}
|
|
673
|
+
*/
|
|
674
|
+
morphMany(related, morphType, foreignKey, localKey) {
|
|
675
|
+
const MorphManyRelation = require('./Relations/MorphManyRelation');
|
|
676
|
+
localKey = localKey || this.constructor.primaryKey;
|
|
677
|
+
foreignKey = foreignKey || `${morphType}_id`;
|
|
678
|
+
|
|
679
|
+
return new MorphManyRelation(this, related, morphType, foreignKey, localKey);
|
|
680
|
+
}
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
module.exports = Model;
|