@spinajs/orm 2.0.180 → 2.0.181

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.
Files changed (91) hide show
  1. package/lib/cjs/builders.d.ts +643 -643
  2. package/lib/cjs/builders.js +1602 -1602
  3. package/lib/cjs/builders.js.map +1 -1
  4. package/lib/cjs/converters.d.ts +34 -34
  5. package/lib/cjs/converters.js +104 -104
  6. package/lib/cjs/decorators.d.ts +152 -152
  7. package/lib/cjs/decorators.js +449 -449
  8. package/lib/cjs/dehydrators.d.ts +10 -10
  9. package/lib/cjs/dehydrators.js +47 -47
  10. package/lib/cjs/driver.d.ts +82 -82
  11. package/lib/cjs/driver.js +102 -102
  12. package/lib/cjs/driver.js.map +1 -1
  13. package/lib/cjs/enums.d.ts +116 -116
  14. package/lib/cjs/enums.js +126 -126
  15. package/lib/cjs/enums.js.map +1 -1
  16. package/lib/cjs/exceptions.d.ts +6 -6
  17. package/lib/cjs/exceptions.js +10 -10
  18. package/lib/cjs/hydrators.d.ts +19 -19
  19. package/lib/cjs/hydrators.js +132 -132
  20. package/lib/cjs/hydrators.js.map +1 -1
  21. package/lib/cjs/index.d.ts +17 -17
  22. package/lib/cjs/index.js +33 -33
  23. package/lib/cjs/interfaces.d.ts +919 -919
  24. package/lib/cjs/interfaces.js +279 -279
  25. package/lib/cjs/interfaces.js.map +1 -1
  26. package/lib/cjs/middlewares.d.ts +62 -62
  27. package/lib/cjs/middlewares.js +258 -258
  28. package/lib/cjs/model.d.ts +284 -284
  29. package/lib/cjs/model.js +810 -810
  30. package/lib/cjs/orm.d.ts +61 -61
  31. package/lib/cjs/orm.js +333 -333
  32. package/lib/cjs/orm.js.map +1 -1
  33. package/lib/cjs/relation-objects.d.ts +108 -108
  34. package/lib/cjs/relation-objects.js +221 -221
  35. package/lib/cjs/relations.d.ts +61 -61
  36. package/lib/cjs/relations.js +194 -194
  37. package/lib/cjs/relations.js.map +1 -1
  38. package/lib/cjs/statements.d.ts +143 -143
  39. package/lib/cjs/statements.js +309 -309
  40. package/lib/cjs/statements.js.map +1 -1
  41. package/lib/cjs/types.d.ts +32 -32
  42. package/lib/cjs/types.js +2 -2
  43. package/lib/cjs/wrappers.d.ts +5 -5
  44. package/lib/cjs/wrappers.js +12 -12
  45. package/lib/mjs/builders.d.ts +643 -643
  46. package/lib/mjs/builders.js +1594 -1594
  47. package/lib/mjs/builders.js.map +1 -1
  48. package/lib/mjs/converters.d.ts +34 -34
  49. package/lib/mjs/converters.js +96 -96
  50. package/lib/mjs/decorators.d.ts +152 -152
  51. package/lib/mjs/decorators.js +422 -422
  52. package/lib/mjs/dehydrators.d.ts +10 -10
  53. package/lib/mjs/dehydrators.js +41 -41
  54. package/lib/mjs/driver.d.ts +82 -82
  55. package/lib/mjs/driver.js +98 -98
  56. package/lib/mjs/driver.js.map +1 -1
  57. package/lib/mjs/enums.d.ts +116 -116
  58. package/lib/mjs/enums.js +123 -123
  59. package/lib/mjs/enums.js.map +1 -1
  60. package/lib/mjs/exceptions.d.ts +6 -6
  61. package/lib/mjs/exceptions.js +6 -6
  62. package/lib/mjs/hydrators.d.ts +19 -19
  63. package/lib/mjs/hydrators.js +128 -128
  64. package/lib/mjs/hydrators.js.map +1 -1
  65. package/lib/mjs/index.d.ts +17 -17
  66. package/lib/mjs/index.js +17 -17
  67. package/lib/mjs/interfaces.d.ts +919 -919
  68. package/lib/mjs/interfaces.js +267 -267
  69. package/lib/mjs/interfaces.js.map +1 -1
  70. package/lib/mjs/middlewares.d.ts +62 -62
  71. package/lib/mjs/middlewares.js +249 -249
  72. package/lib/mjs/model.d.ts +284 -284
  73. package/lib/mjs/model.js +800 -800
  74. package/lib/mjs/orm.d.ts +61 -61
  75. package/lib/mjs/orm.js +326 -326
  76. package/lib/mjs/orm.js.map +1 -1
  77. package/lib/mjs/relation-objects.d.ts +108 -108
  78. package/lib/mjs/relation-objects.js +211 -211
  79. package/lib/mjs/relations.d.ts +61 -61
  80. package/lib/mjs/relations.js +191 -191
  81. package/lib/mjs/relations.js.map +1 -1
  82. package/lib/mjs/statements.d.ts +143 -143
  83. package/lib/mjs/statements.js +301 -301
  84. package/lib/mjs/statements.js.map +1 -1
  85. package/lib/mjs/types.d.ts +32 -32
  86. package/lib/mjs/types.js +1 -1
  87. package/lib/mjs/wrappers.d.ts +5 -5
  88. package/lib/mjs/wrappers.js +9 -9
  89. package/lib/tsconfig.cjs.tsbuildinfo +1 -1
  90. package/lib/tsconfig.mjs.tsbuildinfo +1 -1
  91. package/package.json +5 -5
package/lib/cjs/model.js CHANGED
@@ -1,811 +1,811 @@
1
- "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.MODEL_STATIC_MIXINS = exports.createQuery = exports.HistoricalModel = exports.ModelBase = exports.extractModelDescriptor = void 0;
7
- /* eslint-disable prettier/prettier */
8
- const enums_js_1 = require("./enums.js");
9
- const decorators_js_1 = require("./decorators.js");
10
- const interfaces_js_1 = require("./interfaces.js");
11
- const builders_js_1 = require("./builders.js");
12
- const di_1 = require("@spinajs/di");
13
- const orm_js_1 = require("./orm.js");
14
- const hydrators_js_1 = require("./hydrators.js");
15
- const lodash_1 = __importDefault(require("lodash"));
16
- const uuid_1 = require("uuid");
17
- const exceptions_js_1 = require("./exceptions.js");
18
- const dehydrators_js_1 = require("./dehydrators.js");
19
- const luxon_1 = require("luxon");
20
- const relation_objects_js_1 = require("./relation-objects.js");
21
- const middlewares_js_1 = require("./middlewares.js");
22
- function extractModelDescriptor(targetOrForward) {
23
- const target = !(0, di_1.isConstructor)(targetOrForward) && targetOrForward ? targetOrForward() : targetOrForward;
24
- if (!target) {
25
- return null;
26
- }
27
- let descriptor = null;
28
- _reduce(target);
29
- return descriptor;
30
- function _reduce(t) {
31
- if (!t) {
32
- return;
33
- }
34
- if (t[decorators_js_1.MODEL_DESCTRIPTION_SYMBOL]) {
35
- descriptor = descriptor ?? {};
36
- lodash_1.default.mergeWith(descriptor, t[decorators_js_1.MODEL_DESCTRIPTION_SYMBOL], (a, b) => {
37
- if (!a) {
38
- return b;
39
- }
40
- if (Array.isArray(a)) {
41
- return a.concat(b);
42
- }
43
- return a;
44
- });
45
- }
46
- _reduce(t.prototype);
47
- _reduce(t.__proto__);
48
- }
49
- }
50
- exports.extractModelDescriptor = extractModelDescriptor;
51
- class ModelBase {
52
- /**
53
- * Gets descriptor for this model. It contains information about relations, orm driver, connection properties,
54
- * db table attached, column information and others.
55
- */
56
- get ModelDescriptor() {
57
- return extractModelDescriptor(this.constructor);
58
- }
59
- /**
60
- * Gets di container associated with this model ( via connection object eg. different drivers have their own implementation of things)
61
- */
62
- get Container() {
63
- if (!this._container) {
64
- const orm = di_1.DI.get(orm_js_1.Orm);
65
- const driver = orm.Connections.get(this.ModelDescriptor.Connection);
66
- if (!driver) {
67
- throw new Error(`model ${this.constructor.name} have invalid connection ${this.ModelDescriptor.Connection}, please check your db config file or model connection name`);
68
- }
69
- this._container = driver.Container;
70
- }
71
- return this._container;
72
- }
73
- get PrimaryKeyName() {
74
- return this.ModelDescriptor.PrimaryKey;
75
- }
76
- get PrimaryKeyValue() {
77
- return this[this.PrimaryKeyName];
78
- }
79
- set PrimaryKeyValue(newVal) {
80
- this[this.PrimaryKeyName] = newVal;
81
- this.ModelDescriptor.Relations.forEach((r) => {
82
- const rel = this[r.Name];
83
- if (!rel)
84
- return;
85
- switch (r.Type) {
86
- case interfaces_js_1.RelationType.One:
87
- rel[r.ForeignKey] = newVal;
88
- break;
89
- case interfaces_js_1.RelationType.Many:
90
- rel.forEach((rVal) => (rVal[r.ForeignKey] = newVal));
91
- break;
92
- case interfaces_js_1.RelationType.ManyToMany:
93
- // TODO: rethink this
94
- break;
95
- }
96
- });
97
- }
98
- valueOf() {
99
- return this.PrimaryKeyValue;
100
- }
101
- driver() {
102
- const orm = di_1.DI.get(orm_js_1.Orm);
103
- const driver = orm.Connections.get(this.ModelDescriptor.Connection);
104
- return driver;
105
- }
106
- /**
107
- * Recursivelly takes all relation data and returns as single array
108
- */
109
- getFlattenRelationModels(recursive) {
110
- const reduceRelations = function (m) {
111
- const relations = [...m.ModelDescriptor.Relations.values()];
112
- const models = lodash_1.default.flatMap(relations, (r) => {
113
- if (r.Type === interfaces_js_1.RelationType.Many || r.Type === interfaces_js_1.RelationType.ManyToMany) {
114
- return m[r.Name];
115
- }
116
- if (m[r.Name].Value) {
117
- return [m[r.Name].Value];
118
- }
119
- }).filter((x) => x !== undefined);
120
- if (recursive) {
121
- return [...models, ...lodash_1.default.flatMap(models, reduceRelations)];
122
- }
123
- return models;
124
- };
125
- return reduceRelations(this);
126
- }
127
- static getModelDescriptor() {
128
- throw new Error('Not implemented');
129
- }
130
- static getRelationDescriptor(_relation) {
131
- throw new Error('Not implemented');
132
- }
133
- /**
134
- * Clears all data in table
135
- */
136
- static truncate() {
137
- throw new Error('Not implemented');
138
- }
139
- /**
140
- * Get all data from db
141
- */
142
- static all(_page, _perPage) {
143
- throw new Error('Not implemented');
144
- }
145
- /**
146
- * Inserts data to DB.
147
- *
148
- * @param _data - data to insert
149
- */
150
- static insert(_data, _insertBehaviour = interfaces_js_1.InsertBehaviour.None) {
151
- throw new Error('Not implemented');
152
- }
153
- static where(_column, _operator, _value) {
154
- throw new Error('Not implemented');
155
- }
156
- /**
157
- * Updates single or multiple records at once with provided value based on condition
158
- *
159
- * @param _data - data to set
160
- */
161
- static update(_data) {
162
- throw new Error('Not implemented');
163
- }
164
- /**
165
- * Tries to find all models with given primary keys
166
- */
167
- static find(_pks) {
168
- throw new Error('Not implemented');
169
- }
170
- static first() {
171
- throw new Error('Not implemented');
172
- }
173
- static last() {
174
- throw new Error('Not implemented');
175
- }
176
- static newest() {
177
- throw new Error('Not implemented');
178
- }
179
- static oldest() {
180
- throw new Error('Not implemented');
181
- }
182
- static count() {
183
- throw new Error('Not implemented');
184
- }
185
- /**
186
- * Tries to find all models in db. If not all exists, throws exception
187
- */
188
- static findOrFail(_pks) {
189
- throw new Error('Not implemented');
190
- }
191
- /**
192
- * gets model by specified pk, if not exists, returns null
193
- *
194
- */
195
- static get(_pk) {
196
- throw new Error('Not implemented');
197
- }
198
- /**
199
- * Finds model by specified pk. If model not exists in db throws exception
200
- *
201
- */
202
- static getOrFail(_pk) {
203
- throw new Error('Not implemented');
204
- }
205
- /**
206
- *
207
- * Checks if model with pk key or unique fields exists and if not creates one AND NOT save in db
208
- * NOTE: it checks for unique fields constraint
209
- */
210
- static getOrNew(_pk, _data) {
211
- throw new Error('Not implemented');
212
- }
213
- /**
214
- * Creates query on this model. used for quering db for partial data, to perform some kind of operations
215
- * that dont need full ORM model to involve, or other non standard operations eg. joins or raw data queries based on this model
216
- */
217
- static query() {
218
- throw new Error('Not implemented');
219
- }
220
- /**
221
- *
222
- * Checks if model with pk key / unique fields exists and if not creates one and saves to db
223
- * NOTE: it checks for unique fields too.
224
- *
225
- * @param data - model width data to check
226
- */
227
- static getOrCreate(_pk, _data) {
228
- throw new Error('Not implemented');
229
- }
230
- /**
231
- * Creates new model & saves is to db
232
- *
233
- * @param data - initial model data
234
- */
235
- static create(_data) {
236
- throw new Error('Not implemented');
237
- }
238
- /**
239
- * Deletes model from db
240
- *
241
- * @param pk - primary key
242
- */
243
- static destroy(_pk) {
244
- throw new Error('Not implemented');
245
- }
246
- /**
247
- * Checks if model exists in db
248
- */
249
- static exists() {
250
- throw new Error('Not implemented');
251
- }
252
- constructor(data) {
253
- /**
254
- * List of hidden properties from JSON / dehydrations
255
- * eg. password field of user
256
- */
257
- this._hidden = [];
258
- this.setDefaults();
259
- if (data) {
260
- this.hydrate(data);
261
- }
262
- }
263
- /**
264
- * Fills model with data. It only fills properties that exists in database
265
- *
266
- * @param data - data to fill
267
- */
268
- hydrate(data) {
269
- this.Container.resolve(Array.ofType(hydrators_js_1.ModelHydrator)).forEach((h) => h.hydrate(this, data));
270
- }
271
- /**
272
- *
273
- * Attachess model to proper relation an sets foreign key
274
- *
275
- * @param data - model to attach
276
- */
277
- attach(data) {
278
- // TODO: refactor this, to not check every time for relation
279
- // do this as map or smth
280
- for (const [_, v] of this.ModelDescriptor.Relations.entries()) {
281
- if (v.TargetModel.name === data.constructor.name) {
282
- // TODO: refactor this, so we dont update foreign key
283
- // instead we must use belongsTo relation on data model to update
284
- data[v.ForeignKey] = this.PrimaryKeyValue;
285
- switch (v.Type) {
286
- case interfaces_js_1.RelationType.One:
287
- this[v.Name].attach(data);
288
- break;
289
- case interfaces_js_1.RelationType.Many:
290
- case interfaces_js_1.RelationType.ManyToMany:
291
- this[v.Name].push(data);
292
- break;
293
- }
294
- }
295
- }
296
- }
297
- /**
298
- * Extracts all data from model. It takes only properties that exists in DB
299
- */
300
- dehydrate(omit) {
301
- return this.Container.resolve(dehydrators_js_1.StandardModelDehydrator).dehydrate(this, [...(omit ?? []), ...this._hidden]);
302
- }
303
- /**
304
- *
305
- * Extracts all data from model with relation data. Relation data are dehydrated recursively.
306
- *
307
- * @param omit - fields to omit
308
- */
309
- dehydrateWithRelations(omit) {
310
- return this.Container.resolve(dehydrators_js_1.StandardModelWithRelationsDehydrator).dehydrate(this, [...(omit ?? []), ...this._hidden]);
311
- }
312
- toSql() {
313
- return this.Container.resolve(interfaces_js_1.ModelToSqlConverter).toSql(this);
314
- }
315
- /**
316
- * deletes enitt from db. If model have SoftDelete decorator, model is marked as deleted
317
- */
318
- async destroy() {
319
- if (!this.PrimaryKeyValue) {
320
- return;
321
- }
322
- await this.constructor.destroy(this.PrimaryKeyValue);
323
- }
324
- /**
325
- * If model can be in achived state - sets archived at date and saves it to db
326
- */
327
- async archive() {
328
- if (this.ModelDescriptor.Archived) {
329
- this[this.ModelDescriptor.Archived.ArchivedAt] = luxon_1.DateTime.now();
330
- }
331
- else {
332
- throw new exceptions_js_1.OrmException('archived at column not exists in model');
333
- }
334
- const { query } = this.createUpdateQuery();
335
- await query.update(this.toSql()).where(this.PrimaryKeyName, this.PrimaryKeyValue);
336
- }
337
- async update() {
338
- const { query } = this.createUpdateQuery();
339
- if (this.ModelDescriptor.Timestamps.UpdatedAt) {
340
- this[this.ModelDescriptor.Timestamps.UpdatedAt] = luxon_1.DateTime.now();
341
- }
342
- await query.update(this.toSql()).where(this.PrimaryKeyName, this.PrimaryKeyValue);
343
- }
344
- /**
345
- * Save all changes to db. It creates new entry id db or updates existing one if
346
- * primary key exists
347
- */
348
- async insert(insertBehaviour = interfaces_js_1.InsertBehaviour.None) {
349
- const { query, description } = this.createInsertQuery();
350
- switch (insertBehaviour) {
351
- case interfaces_js_1.InsertBehaviour.InsertOrIgnore:
352
- query.orIgnore();
353
- break;
354
- case interfaces_js_1.InsertBehaviour.InsertOrUpdate:
355
- query.onDuplicate().update(description.Columns.filter((c) => !c.PrimaryKey).map((c) => c.Name));
356
- break;
357
- case interfaces_js_1.InsertBehaviour.InsertOrReplace:
358
- query.orReplace();
359
- break;
360
- }
361
- const iMidleware = {
362
- afterQuery: (data) => {
363
- this.PrimaryKeyValue = this.PrimaryKeyValue ?? data.LastInsertId;
364
- return data;
365
- },
366
- modelCreation: () => null,
367
- afterHydration: () => null,
368
- };
369
- query.middleware(iMidleware);
370
- return query.values(this.toSql());
371
- }
372
- /**
373
- *
374
- * Shorthand for inserting model when no primary key exists, or update
375
- * its value in db if primary key is set
376
- *
377
- * @param insertBehaviour - insert mode
378
- */
379
- async insertOrUpdate(insertBehaviour = interfaces_js_1.InsertBehaviour.None) {
380
- if (this.PrimaryKeyValue) {
381
- await this.update();
382
- }
383
- else {
384
- await this.insert(insertBehaviour);
385
- }
386
- }
387
- /**
388
- * Gets model data from database and returns as fresh instance.
389
- *
390
- * If primary key is not fetched, tries to load by columns with unique constraint.
391
- * If there is no unique columns or primary key, throws error
392
- */
393
- async fresh() {
394
- const { query, description } = this.createSelectQuery();
395
- query.select('*');
396
- _preparePkWhere(description, query, this);
397
- _prepareOrderBy(description, query);
398
- // TODO: rethink all cast of this type?
399
- return (await query.firstOrFail());
400
- }
401
- /**
402
- * Refresh model from database.
403
- *
404
- * If no primary key is set, tries to fetch data base on columns
405
- * with unique constraints. If none exists, throws exception
406
- */
407
- async refresh() {
408
- let model = null;
409
- model = await this.fresh();
410
- for (const c of this.ModelDescriptor.Columns) {
411
- this[c.Name] = model[c.Name];
412
- }
413
- }
414
- toJSON() {
415
- return this.dehydrate();
416
- }
417
- /**
418
- * sets default values for model. values are taken from DB default column prop
419
- */
420
- setDefaults() {
421
- this.ModelDescriptor.Columns?.forEach((c) => {
422
- if (c.Uuid) {
423
- this[c.Name] = (0, uuid_1.v4)();
424
- }
425
- else {
426
- this[c.Name] = c.DefaultValue;
427
- }
428
- });
429
- if (this.ModelDescriptor.Timestamps.CreatedAt) {
430
- this[this.ModelDescriptor.Timestamps.CreatedAt] = luxon_1.DateTime.now();
431
- }
432
- for (const [, rel] of this.ModelDescriptor.Relations) {
433
- if (rel.Factory) {
434
- this[rel.Name] = rel.Factory(this, rel, this.Container);
435
- }
436
- else if (rel.RelationClass) {
437
- this[rel.Name] = this.Container.resolve(rel.RelationClass, [this, rel.TargetModel, rel, []]);
438
- }
439
- else if (rel.Type === interfaces_js_1.RelationType.Many) {
440
- this[rel.Name] = new relation_objects_js_1.OneToManyRelationList(this, rel.TargetModel, rel, []);
441
- }
442
- else if (rel.Type === interfaces_js_1.RelationType.ManyToMany) {
443
- this[rel.Name] = new relation_objects_js_1.ManyToManyRelationList(this, rel.TargetModel, rel, []);
444
- }
445
- else {
446
- this[rel.Name] = new relation_objects_js_1.SingleRelation(this, rel.TargetModel, rel, null);
447
- }
448
- }
449
- }
450
- createSelectQuery() {
451
- return createQuery(this.constructor, builders_js_1.SelectQueryBuilder);
452
- }
453
- createUpdateQuery() {
454
- return createQuery(this.constructor, builders_js_1.UpdateQueryBuilder);
455
- }
456
- createInsertQuery() {
457
- return createQuery(this.constructor, builders_js_1.InsertQueryBuilder);
458
- }
459
- }
460
- exports.ModelBase = ModelBase;
461
- function _descriptor(model) {
462
- return model[decorators_js_1.MODEL_DESCTRIPTION_SYMBOL];
463
- }
464
- function _preparePkWhere(description, query, model) {
465
- if (description.PrimaryKey) {
466
- query.where(description.PrimaryKey, model.PrimaryKeyValue);
467
- }
468
- else {
469
- const unique = description.Columns.filter((x) => x.Unique);
470
- if (unique.length !== 0) {
471
- for (const c of unique) {
472
- query.where(c.Name, '=', model[c.Name]);
473
- }
474
- }
475
- else {
476
- throw new exceptions_js_1.OrmException('Model dont have primary key set or columns with unique constraint, cannot fetch model from database');
477
- }
478
- }
479
- }
480
- function _prepareOrderBy(description, query, order) {
481
- if (description.PrimaryKey) {
482
- query.order(description.PrimaryKey, order ?? enums_js_1.SortOrder.DESC);
483
- }
484
- else {
485
- const unique = description.Columns.filter((c) => c.Unique);
486
- if (unique.length !== 0) {
487
- unique.forEach((c) => query.order(c.Name, order ?? enums_js_1.SortOrder.DESC));
488
- }
489
- else if (description.Timestamps?.CreatedAt) {
490
- query.order(description.Timestamps.CreatedAt, order ?? enums_js_1.SortOrder.DESC);
491
- }
492
- else if (description.Timestamps?.UpdatedAt) {
493
- query.order(description.Timestamps.UpdatedAt, order ?? enums_js_1.SortOrder.DESC);
494
- }
495
- }
496
- }
497
- class HistoricalModel {
498
- }
499
- exports.HistoricalModel = HistoricalModel;
500
- /**
501
- * Helper function to create query based on model
502
- *
503
- * @param model - source model for query
504
- * @param query - query class
505
- * @param injectModel - should inject model information into query, if not, query will return raw data
506
- *
507
- * @returns
508
- */
509
- function createQuery(model, query, injectModel = true) {
510
- const dsc = _descriptor(model);
511
- if (!dsc) {
512
- throw new Error(`model ${model.name} does not have model descriptor. Use @model decorator on class`);
513
- }
514
- const orm = di_1.DI.get(orm_js_1.Orm);
515
- const driver = orm.Connections.get(dsc.Connection);
516
- if (!driver) {
517
- throw new Error(`model ${model.name} have invalid connection ${dsc.Connection}, please check your db config file or model connection name`);
518
- }
519
- const cnt = driver.Container;
520
- const qr = cnt.resolve(query, [driver, injectModel ? orm.Models.find((x) => x.name === model.name).type : null]);
521
- if (qr instanceof builders_js_1.SelectQueryBuilder) {
522
- const scope = model._queryScopes;
523
- if (scope) {
524
- Object.getOwnPropertyNames(scope.__proto__)
525
- .filter((x) => x !== 'constructor')
526
- .forEach(function (property) {
527
- if (typeof scope[property] === 'function') {
528
- qr[property] = scope[property].bind(qr);
529
- }
530
- });
531
- }
532
- }
533
- qr.middleware(new middlewares_js_1.DiscriminationMapMiddleware(dsc));
534
- qr.setTable(dsc.TableName);
535
- if (driver.Options.Database) {
536
- qr.database(driver.Options.Database);
537
- }
538
- return {
539
- query: qr,
540
- description: dsc,
541
- model,
542
- container: driver.Container,
543
- };
544
- }
545
- exports.createQuery = createQuery;
546
- exports.MODEL_STATIC_MIXINS = {
547
- getModelDescriptor() {
548
- const dsc = _descriptor(this);
549
- if (!dsc) {
550
- throw new exceptions_js_1.OrmException(`Model ${this.constructor.name} has no descriptor`);
551
- }
552
- return dsc;
553
- },
554
- getRelationDescriptor(relation) {
555
- const descriptor = this.getModelDescriptor();
556
- let rDescriptor = null;
557
- for (const [key, value] of descriptor.Relations) {
558
- if (key.toLowerCase() === relation.toLowerCase().trim()) {
559
- rDescriptor = value;
560
- break;
561
- }
562
- }
563
- if (!rDescriptor) {
564
- throw new exceptions_js_1.OrmException(`Model ${this.constructor.name} has no relation ${relation}`);
565
- }
566
- return rDescriptor;
567
- },
568
- truncate() {
569
- const { query } = createQuery(this, builders_js_1.TruncateTableQueryBuilder, false);
570
- return query;
571
- },
572
- driver() {
573
- const dsc = this.getModelDescriptor();
574
- const orm = di_1.DI.get(orm_js_1.Orm);
575
- const driver = orm.Connections.get(dsc.Connection);
576
- if (!driver) {
577
- throw new Error(`model ${this.name} have invalid connection ${dsc.Connection}, please check your db config file or model connection name`);
578
- }
579
- return driver;
580
- },
581
- query() {
582
- const { query } = createQuery(this, builders_js_1.SelectQueryBuilder);
583
- return query;
584
- },
585
- where(column, operator, value) {
586
- const { query } = createQuery(this, builders_js_1.SelectQueryBuilder);
587
- query.select('*');
588
- return query.where(column, operator, value);
589
- },
590
- update(data) {
591
- const { query } = createQuery(this, builders_js_1.UpdateQueryBuilder);
592
- return query.update(data);
593
- },
594
- all(page, perPage) {
595
- const { query } = createQuery(this, builders_js_1.SelectQueryBuilder);
596
- query.select('*');
597
- if (page >= 0 && perPage > 0) {
598
- query.take(perPage).skip(page * perPage);
599
- }
600
- return query;
601
- },
602
- /**
603
- * Try to insert new value
604
- */
605
- async insert(data, insertBehaviour = interfaces_js_1.InsertBehaviour.None) {
606
- const { query, description, container } = createQuery(this, builders_js_1.InsertQueryBuilder);
607
- const converter = container.resolve(interfaces_js_1.ObjectToSqlConverter);
608
- if (Array.isArray(data)) {
609
- if (insertBehaviour !== interfaces_js_1.InsertBehaviour.None) {
610
- throw new exceptions_js_1.OrmException(`insert behaviour is not supported with arrays`);
611
- }
612
- query.values(data.map((d) => {
613
- if (d instanceof ModelBase) {
614
- return d.toSql();
615
- }
616
- return converter.toSql(d);
617
- }));
618
- }
619
- else {
620
- switch (insertBehaviour) {
621
- case interfaces_js_1.InsertBehaviour.InsertOrIgnore:
622
- query.orIgnore();
623
- break;
624
- case interfaces_js_1.InsertBehaviour.InsertOrUpdate:
625
- query.onDuplicate().update(description.Columns.filter((c) => !c.PrimaryKey).map((c) => c.Name));
626
- break;
627
- case interfaces_js_1.InsertBehaviour.InsertOrReplace:
628
- query.orReplace();
629
- break;
630
- }
631
- if (data instanceof ModelBase) {
632
- query.values(data.toSql());
633
- }
634
- else {
635
- query.values(converter.toSql(data));
636
- }
637
- }
638
- const iMidleware = {
639
- afterQuery: (result) => {
640
- if (Array.isArray(data)) {
641
- data.forEach((v, idx) => {
642
- if (v instanceof ModelBase) {
643
- v.PrimaryKeyValue = v.PrimaryKeyValue ?? result.LastInsertId - data.length + idx + 1;
644
- }
645
- });
646
- }
647
- else if (data instanceof ModelBase) {
648
- data.PrimaryKeyValue = data.PrimaryKeyValue ?? result.LastInsertId;
649
- }
650
- return result;
651
- },
652
- modelCreation: () => null,
653
- afterHydration: () => null,
654
- };
655
- query.middleware(iMidleware);
656
- return query;
657
- },
658
- async find(pks) {
659
- const { query, description } = createQuery(this, builders_js_1.SelectQueryBuilder);
660
- const pkey = description.PrimaryKey;
661
- query.select('*');
662
- query.whereIn(pkey, pks);
663
- return await query;
664
- },
665
- async findOrFail(pks) {
666
- const { query, description, model } = createQuery(this, builders_js_1.SelectQueryBuilder);
667
- const pkey = description.PrimaryKey;
668
- query.select('*');
669
- query.whereIn(pkey, pks);
670
- const result = await query;
671
- if (result.length !== pks.length) {
672
- throw new Error(`could not find all results for model ${model.name}`);
673
- }
674
- return result;
675
- },
676
- async get(pk) {
677
- const { query, description } = createQuery(this, builders_js_1.SelectQueryBuilder);
678
- const pkey = description.PrimaryKey;
679
- query.select('*');
680
- query.where(pkey, pk);
681
- _prepareOrderBy(description, query);
682
- return (await query.first());
683
- },
684
- async getOrFail(pk) {
685
- const { query, description } = createQuery(this, builders_js_1.SelectQueryBuilder);
686
- const pkey = description.PrimaryKey;
687
- query.select('*');
688
- query.where(pkey, pk);
689
- _prepareOrderBy(description, query);
690
- return (await query.firstOrFail());
691
- },
692
- destroy(pks) {
693
- const description = _descriptor(this);
694
- const data = Array.isArray(pks) ? pks : [pks];
695
- const { query } = description.SoftDelete?.DeletedAt ? createQuery(this, builders_js_1.UpdateQueryBuilder) : createQuery(this, builders_js_1.DeleteQueryBuilder);
696
- if (description.SoftDelete?.DeletedAt) {
697
- query.update({
698
- [description.SoftDelete.DeletedAt]: luxon_1.DateTime.now(),
699
- });
700
- }
701
- if (pks) {
702
- query.whereIn(description.PrimaryKey, data);
703
- }
704
- return query;
705
- },
706
- async create(data) {
707
- const entity = new (Function.prototype.bind.apply(this))(data);
708
- await entity.insert();
709
- return entity;
710
- },
711
- async getOrCreate(pk, data) {
712
- const { query, description } = createQuery(this, builders_js_1.SelectQueryBuilder);
713
- // pk constrain
714
- if (description.PrimaryKey && pk !== null) {
715
- query.where(description.PrimaryKey, pk);
716
- }
717
- // check for all unique columns ( unique constrain )
718
- description.Columns.filter((c) => c.Unique).forEach((c) => {
719
- query.andWhere(c, data[c.Name]);
720
- });
721
- _prepareOrderBy(description, query);
722
- let entity = (await query.first());
723
- if (!entity) {
724
- entity = new (Function.prototype.bind.apply(this))(data);
725
- await entity.insert();
726
- return entity;
727
- }
728
- return entity;
729
- },
730
- async getOrNew(pk, data) {
731
- const { query, description } = createQuery(this, builders_js_1.SelectQueryBuilder);
732
- // pk constrain
733
- if (description.PrimaryKey) {
734
- query.where(description.PrimaryKey, pk);
735
- }
736
- // check for all unique columns ( unique constrain )
737
- description.Columns.filter((c) => c.Unique).forEach((c) => {
738
- query.andWhere(c, data[c.Name]);
739
- });
740
- _prepareOrderBy(description, query);
741
- let entity = (await query.first());
742
- if (!entity) {
743
- entity = new (Function.prototype.bind.apply(this))(data);
744
- return entity;
745
- }
746
- return entity;
747
- },
748
- async exists(pk) {
749
- const { query, description } = createQuery(this, builders_js_1.SelectQueryBuilder);
750
- // pk constrain
751
- if (description.PrimaryKey && pk !== null) {
752
- query.where(description.PrimaryKey, pk);
753
- }
754
- const result = await query.clearColumns().select(description.PrimaryKey).first();
755
- if (result) {
756
- return true;
757
- }
758
- return false;
759
- },
760
- async first(callback) {
761
- const { query, description } = createQuery(this, builders_js_1.SelectQueryBuilder);
762
- _prepareOrderBy(description, query, enums_js_1.SortOrder.ASC);
763
- if (callback) {
764
- callback(query);
765
- }
766
- return (await query.first());
767
- },
768
- async last(callback) {
769
- const { query, description } = createQuery(this, builders_js_1.SelectQueryBuilder);
770
- _prepareOrderBy(description, query, enums_js_1.SortOrder.DESC);
771
- if (callback) {
772
- callback(query);
773
- }
774
- return (await query.first());
775
- },
776
- async newest(callback) {
777
- const { query, description } = createQuery(this, builders_js_1.SelectQueryBuilder);
778
- if (description.Timestamps?.CreatedAt) {
779
- query.order(description.Timestamps.CreatedAt, enums_js_1.SortOrder.DESC);
780
- }
781
- else {
782
- throw new exceptions_js_1.OrmException('cannot fetch newest entity - CreateAt column not exists in model/db');
783
- }
784
- if (callback) {
785
- callback(query);
786
- }
787
- return (await query.first());
788
- },
789
- async oldest(callback) {
790
- const { query, description } = createQuery(this, builders_js_1.SelectQueryBuilder);
791
- if (description.Timestamps?.CreatedAt) {
792
- query.order(description.Timestamps.CreatedAt, enums_js_1.SortOrder.ASC);
793
- }
794
- else {
795
- throw new exceptions_js_1.OrmException('cannot fetch oldest entity - CreateAt column not exists in model/db');
796
- }
797
- if (callback) {
798
- callback(query);
799
- }
800
- return (await query.first());
801
- },
802
- async count(callback) {
803
- const { query } = createQuery(this, builders_js_1.SelectQueryBuilder);
804
- query.count('*', 'count');
805
- if (callback) {
806
- callback(query);
807
- }
808
- return await (await query.asRaw()).count;
809
- },
810
- };
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.MODEL_STATIC_MIXINS = exports.createQuery = exports.HistoricalModel = exports.ModelBase = exports.extractModelDescriptor = void 0;
7
+ /* eslint-disable prettier/prettier */
8
+ const enums_js_1 = require("./enums.js");
9
+ const decorators_js_1 = require("./decorators.js");
10
+ const interfaces_js_1 = require("./interfaces.js");
11
+ const builders_js_1 = require("./builders.js");
12
+ const di_1 = require("@spinajs/di");
13
+ const orm_js_1 = require("./orm.js");
14
+ const hydrators_js_1 = require("./hydrators.js");
15
+ const lodash_1 = __importDefault(require("lodash"));
16
+ const uuid_1 = require("uuid");
17
+ const exceptions_js_1 = require("./exceptions.js");
18
+ const dehydrators_js_1 = require("./dehydrators.js");
19
+ const luxon_1 = require("luxon");
20
+ const relation_objects_js_1 = require("./relation-objects.js");
21
+ const middlewares_js_1 = require("./middlewares.js");
22
+ function extractModelDescriptor(targetOrForward) {
23
+ const target = !(0, di_1.isConstructor)(targetOrForward) && targetOrForward ? targetOrForward() : targetOrForward;
24
+ if (!target) {
25
+ return null;
26
+ }
27
+ let descriptor = null;
28
+ _reduce(target);
29
+ return descriptor;
30
+ function _reduce(t) {
31
+ if (!t) {
32
+ return;
33
+ }
34
+ if (t[decorators_js_1.MODEL_DESCTRIPTION_SYMBOL]) {
35
+ descriptor = descriptor ?? {};
36
+ lodash_1.default.mergeWith(descriptor, t[decorators_js_1.MODEL_DESCTRIPTION_SYMBOL], (a, b) => {
37
+ if (!a) {
38
+ return b;
39
+ }
40
+ if (Array.isArray(a)) {
41
+ return a.concat(b);
42
+ }
43
+ return a;
44
+ });
45
+ }
46
+ _reduce(t.prototype);
47
+ _reduce(t.__proto__);
48
+ }
49
+ }
50
+ exports.extractModelDescriptor = extractModelDescriptor;
51
+ class ModelBase {
52
+ /**
53
+ * Gets descriptor for this model. It contains information about relations, orm driver, connection properties,
54
+ * db table attached, column information and others.
55
+ */
56
+ get ModelDescriptor() {
57
+ return extractModelDescriptor(this.constructor);
58
+ }
59
+ /**
60
+ * Gets di container associated with this model ( via connection object eg. different drivers have their own implementation of things)
61
+ */
62
+ get Container() {
63
+ if (!this._container) {
64
+ const orm = di_1.DI.get(orm_js_1.Orm);
65
+ const driver = orm.Connections.get(this.ModelDescriptor.Connection);
66
+ if (!driver) {
67
+ throw new Error(`model ${this.constructor.name} have invalid connection ${this.ModelDescriptor.Connection}, please check your db config file or model connection name`);
68
+ }
69
+ this._container = driver.Container;
70
+ }
71
+ return this._container;
72
+ }
73
+ get PrimaryKeyName() {
74
+ return this.ModelDescriptor.PrimaryKey;
75
+ }
76
+ get PrimaryKeyValue() {
77
+ return this[this.PrimaryKeyName];
78
+ }
79
+ set PrimaryKeyValue(newVal) {
80
+ this[this.PrimaryKeyName] = newVal;
81
+ this.ModelDescriptor.Relations.forEach((r) => {
82
+ const rel = this[r.Name];
83
+ if (!rel)
84
+ return;
85
+ switch (r.Type) {
86
+ case interfaces_js_1.RelationType.One:
87
+ rel[r.ForeignKey] = newVal;
88
+ break;
89
+ case interfaces_js_1.RelationType.Many:
90
+ rel.forEach((rVal) => (rVal[r.ForeignKey] = newVal));
91
+ break;
92
+ case interfaces_js_1.RelationType.ManyToMany:
93
+ // TODO: rethink this
94
+ break;
95
+ }
96
+ });
97
+ }
98
+ valueOf() {
99
+ return this.PrimaryKeyValue;
100
+ }
101
+ driver() {
102
+ const orm = di_1.DI.get(orm_js_1.Orm);
103
+ const driver = orm.Connections.get(this.ModelDescriptor.Connection);
104
+ return driver;
105
+ }
106
+ /**
107
+ * Recursivelly takes all relation data and returns as single array
108
+ */
109
+ getFlattenRelationModels(recursive) {
110
+ const reduceRelations = function (m) {
111
+ const relations = [...m.ModelDescriptor.Relations.values()];
112
+ const models = lodash_1.default.flatMap(relations, (r) => {
113
+ if (r.Type === interfaces_js_1.RelationType.Many || r.Type === interfaces_js_1.RelationType.ManyToMany) {
114
+ return m[r.Name];
115
+ }
116
+ if (m[r.Name].Value) {
117
+ return [m[r.Name].Value];
118
+ }
119
+ }).filter((x) => x !== undefined);
120
+ if (recursive) {
121
+ return [...models, ...lodash_1.default.flatMap(models, reduceRelations)];
122
+ }
123
+ return models;
124
+ };
125
+ return reduceRelations(this);
126
+ }
127
+ static getModelDescriptor() {
128
+ throw new Error('Not implemented');
129
+ }
130
+ static getRelationDescriptor(_relation) {
131
+ throw new Error('Not implemented');
132
+ }
133
+ /**
134
+ * Clears all data in table
135
+ */
136
+ static truncate() {
137
+ throw new Error('Not implemented');
138
+ }
139
+ /**
140
+ * Get all data from db
141
+ */
142
+ static all(_page, _perPage) {
143
+ throw new Error('Not implemented');
144
+ }
145
+ /**
146
+ * Inserts data to DB.
147
+ *
148
+ * @param _data - data to insert
149
+ */
150
+ static insert(_data, _insertBehaviour = interfaces_js_1.InsertBehaviour.None) {
151
+ throw new Error('Not implemented');
152
+ }
153
+ static where(_column, _operator, _value) {
154
+ throw new Error('Not implemented');
155
+ }
156
+ /**
157
+ * Updates single or multiple records at once with provided value based on condition
158
+ *
159
+ * @param _data - data to set
160
+ */
161
+ static update(_data) {
162
+ throw new Error('Not implemented');
163
+ }
164
+ /**
165
+ * Tries to find all models with given primary keys
166
+ */
167
+ static find(_pks) {
168
+ throw new Error('Not implemented');
169
+ }
170
+ static first() {
171
+ throw new Error('Not implemented');
172
+ }
173
+ static last() {
174
+ throw new Error('Not implemented');
175
+ }
176
+ static newest() {
177
+ throw new Error('Not implemented');
178
+ }
179
+ static oldest() {
180
+ throw new Error('Not implemented');
181
+ }
182
+ static count() {
183
+ throw new Error('Not implemented');
184
+ }
185
+ /**
186
+ * Tries to find all models in db. If not all exists, throws exception
187
+ */
188
+ static findOrFail(_pks) {
189
+ throw new Error('Not implemented');
190
+ }
191
+ /**
192
+ * gets model by specified pk, if not exists, returns null
193
+ *
194
+ */
195
+ static get(_pk) {
196
+ throw new Error('Not implemented');
197
+ }
198
+ /**
199
+ * Finds model by specified pk. If model not exists in db throws exception
200
+ *
201
+ */
202
+ static getOrFail(_pk) {
203
+ throw new Error('Not implemented');
204
+ }
205
+ /**
206
+ *
207
+ * Checks if model with pk key or unique fields exists and if not creates one AND NOT save in db
208
+ * NOTE: it checks for unique fields constraint
209
+ */
210
+ static getOrNew(_pk, _data) {
211
+ throw new Error('Not implemented');
212
+ }
213
+ /**
214
+ * Creates query on this model. used for quering db for partial data, to perform some kind of operations
215
+ * that dont need full ORM model to involve, or other non standard operations eg. joins or raw data queries based on this model
216
+ */
217
+ static query() {
218
+ throw new Error('Not implemented');
219
+ }
220
+ /**
221
+ *
222
+ * Checks if model with pk key / unique fields exists and if not creates one and saves to db
223
+ * NOTE: it checks for unique fields too.
224
+ *
225
+ * @param data - model width data to check
226
+ */
227
+ static getOrCreate(_pk, _data) {
228
+ throw new Error('Not implemented');
229
+ }
230
+ /**
231
+ * Creates new model & saves is to db
232
+ *
233
+ * @param data - initial model data
234
+ */
235
+ static create(_data) {
236
+ throw new Error('Not implemented');
237
+ }
238
+ /**
239
+ * Deletes model from db
240
+ *
241
+ * @param pk - primary key
242
+ */
243
+ static destroy(_pk) {
244
+ throw new Error('Not implemented');
245
+ }
246
+ /**
247
+ * Checks if model exists in db
248
+ */
249
+ static exists() {
250
+ throw new Error('Not implemented');
251
+ }
252
+ constructor(data) {
253
+ /**
254
+ * List of hidden properties from JSON / dehydrations
255
+ * eg. password field of user
256
+ */
257
+ this._hidden = [];
258
+ this.setDefaults();
259
+ if (data) {
260
+ this.hydrate(data);
261
+ }
262
+ }
263
+ /**
264
+ * Fills model with data. It only fills properties that exists in database
265
+ *
266
+ * @param data - data to fill
267
+ */
268
+ hydrate(data) {
269
+ this.Container.resolve(Array.ofType(hydrators_js_1.ModelHydrator)).forEach((h) => h.hydrate(this, data));
270
+ }
271
+ /**
272
+ *
273
+ * Attachess model to proper relation an sets foreign key
274
+ *
275
+ * @param data - model to attach
276
+ */
277
+ attach(data) {
278
+ // TODO: refactor this, to not check every time for relation
279
+ // do this as map or smth
280
+ for (const [_, v] of this.ModelDescriptor.Relations.entries()) {
281
+ if (v.TargetModel.name === data.constructor.name) {
282
+ // TODO: refactor this, so we dont update foreign key
283
+ // instead we must use belongsTo relation on data model to update
284
+ data[v.ForeignKey] = this.PrimaryKeyValue;
285
+ switch (v.Type) {
286
+ case interfaces_js_1.RelationType.One:
287
+ this[v.Name].attach(data);
288
+ break;
289
+ case interfaces_js_1.RelationType.Many:
290
+ case interfaces_js_1.RelationType.ManyToMany:
291
+ this[v.Name].push(data);
292
+ break;
293
+ }
294
+ }
295
+ }
296
+ }
297
+ /**
298
+ * Extracts all data from model. It takes only properties that exists in DB
299
+ */
300
+ dehydrate(omit) {
301
+ return this.Container.resolve(dehydrators_js_1.StandardModelDehydrator).dehydrate(this, [...(omit ?? []), ...this._hidden]);
302
+ }
303
+ /**
304
+ *
305
+ * Extracts all data from model with relation data. Relation data are dehydrated recursively.
306
+ *
307
+ * @param omit - fields to omit
308
+ */
309
+ dehydrateWithRelations(omit) {
310
+ return this.Container.resolve(dehydrators_js_1.StandardModelWithRelationsDehydrator).dehydrate(this, [...(omit ?? []), ...this._hidden]);
311
+ }
312
+ toSql() {
313
+ return this.Container.resolve(interfaces_js_1.ModelToSqlConverter).toSql(this);
314
+ }
315
+ /**
316
+ * deletes enitt from db. If model have SoftDelete decorator, model is marked as deleted
317
+ */
318
+ async destroy() {
319
+ if (!this.PrimaryKeyValue) {
320
+ return;
321
+ }
322
+ await this.constructor.destroy(this.PrimaryKeyValue);
323
+ }
324
+ /**
325
+ * If model can be in achived state - sets archived at date and saves it to db
326
+ */
327
+ async archive() {
328
+ if (this.ModelDescriptor.Archived) {
329
+ this[this.ModelDescriptor.Archived.ArchivedAt] = luxon_1.DateTime.now();
330
+ }
331
+ else {
332
+ throw new exceptions_js_1.OrmException('archived at column not exists in model');
333
+ }
334
+ const { query } = this.createUpdateQuery();
335
+ await query.update(this.toSql()).where(this.PrimaryKeyName, this.PrimaryKeyValue);
336
+ }
337
+ async update() {
338
+ const { query } = this.createUpdateQuery();
339
+ if (this.ModelDescriptor.Timestamps.UpdatedAt) {
340
+ this[this.ModelDescriptor.Timestamps.UpdatedAt] = luxon_1.DateTime.now();
341
+ }
342
+ await query.update(this.toSql()).where(this.PrimaryKeyName, this.PrimaryKeyValue);
343
+ }
344
+ /**
345
+ * Save all changes to db. It creates new entry id db or updates existing one if
346
+ * primary key exists
347
+ */
348
+ async insert(insertBehaviour = interfaces_js_1.InsertBehaviour.None) {
349
+ const { query, description } = this.createInsertQuery();
350
+ switch (insertBehaviour) {
351
+ case interfaces_js_1.InsertBehaviour.InsertOrIgnore:
352
+ query.orIgnore();
353
+ break;
354
+ case interfaces_js_1.InsertBehaviour.InsertOrUpdate:
355
+ query.onDuplicate().update(description.Columns.filter((c) => !c.PrimaryKey).map((c) => c.Name));
356
+ break;
357
+ case interfaces_js_1.InsertBehaviour.InsertOrReplace:
358
+ query.orReplace();
359
+ break;
360
+ }
361
+ const iMidleware = {
362
+ afterQuery: (data) => {
363
+ this.PrimaryKeyValue = this.PrimaryKeyValue ?? data.LastInsertId;
364
+ return data;
365
+ },
366
+ modelCreation: () => null,
367
+ afterHydration: () => null,
368
+ };
369
+ query.middleware(iMidleware);
370
+ return query.values(this.toSql());
371
+ }
372
+ /**
373
+ *
374
+ * Shorthand for inserting model when no primary key exists, or update
375
+ * its value in db if primary key is set
376
+ *
377
+ * @param insertBehaviour - insert mode
378
+ */
379
+ async insertOrUpdate(insertBehaviour = interfaces_js_1.InsertBehaviour.None) {
380
+ if (this.PrimaryKeyValue) {
381
+ await this.update();
382
+ }
383
+ else {
384
+ await this.insert(insertBehaviour);
385
+ }
386
+ }
387
+ /**
388
+ * Gets model data from database and returns as fresh instance.
389
+ *
390
+ * If primary key is not fetched, tries to load by columns with unique constraint.
391
+ * If there is no unique columns or primary key, throws error
392
+ */
393
+ async fresh() {
394
+ const { query, description } = this.createSelectQuery();
395
+ query.select('*');
396
+ _preparePkWhere(description, query, this);
397
+ _prepareOrderBy(description, query);
398
+ // TODO: rethink all cast of this type?
399
+ return (await query.firstOrFail());
400
+ }
401
+ /**
402
+ * Refresh model from database.
403
+ *
404
+ * If no primary key is set, tries to fetch data base on columns
405
+ * with unique constraints. If none exists, throws exception
406
+ */
407
+ async refresh() {
408
+ let model = null;
409
+ model = await this.fresh();
410
+ for (const c of this.ModelDescriptor.Columns) {
411
+ this[c.Name] = model[c.Name];
412
+ }
413
+ }
414
+ toJSON() {
415
+ return this.dehydrate();
416
+ }
417
+ /**
418
+ * sets default values for model. values are taken from DB default column prop
419
+ */
420
+ setDefaults() {
421
+ this.ModelDescriptor.Columns?.forEach((c) => {
422
+ if (c.Uuid) {
423
+ this[c.Name] = (0, uuid_1.v4)();
424
+ }
425
+ else {
426
+ this[c.Name] = c.DefaultValue;
427
+ }
428
+ });
429
+ if (this.ModelDescriptor.Timestamps.CreatedAt) {
430
+ this[this.ModelDescriptor.Timestamps.CreatedAt] = luxon_1.DateTime.now();
431
+ }
432
+ for (const [, rel] of this.ModelDescriptor.Relations) {
433
+ if (rel.Factory) {
434
+ this[rel.Name] = rel.Factory(this, rel, this.Container);
435
+ }
436
+ else if (rel.RelationClass) {
437
+ this[rel.Name] = this.Container.resolve(rel.RelationClass, [this, rel.TargetModel, rel, []]);
438
+ }
439
+ else if (rel.Type === interfaces_js_1.RelationType.Many) {
440
+ this[rel.Name] = new relation_objects_js_1.OneToManyRelationList(this, rel.TargetModel, rel, []);
441
+ }
442
+ else if (rel.Type === interfaces_js_1.RelationType.ManyToMany) {
443
+ this[rel.Name] = new relation_objects_js_1.ManyToManyRelationList(this, rel.TargetModel, rel, []);
444
+ }
445
+ else {
446
+ this[rel.Name] = new relation_objects_js_1.SingleRelation(this, rel.TargetModel, rel, null);
447
+ }
448
+ }
449
+ }
450
+ createSelectQuery() {
451
+ return createQuery(this.constructor, builders_js_1.SelectQueryBuilder);
452
+ }
453
+ createUpdateQuery() {
454
+ return createQuery(this.constructor, builders_js_1.UpdateQueryBuilder);
455
+ }
456
+ createInsertQuery() {
457
+ return createQuery(this.constructor, builders_js_1.InsertQueryBuilder);
458
+ }
459
+ }
460
+ exports.ModelBase = ModelBase;
461
+ function _descriptor(model) {
462
+ return model[decorators_js_1.MODEL_DESCTRIPTION_SYMBOL];
463
+ }
464
+ function _preparePkWhere(description, query, model) {
465
+ if (description.PrimaryKey) {
466
+ query.where(description.PrimaryKey, model.PrimaryKeyValue);
467
+ }
468
+ else {
469
+ const unique = description.Columns.filter((x) => x.Unique);
470
+ if (unique.length !== 0) {
471
+ for (const c of unique) {
472
+ query.where(c.Name, '=', model[c.Name]);
473
+ }
474
+ }
475
+ else {
476
+ throw new exceptions_js_1.OrmException('Model dont have primary key set or columns with unique constraint, cannot fetch model from database');
477
+ }
478
+ }
479
+ }
480
+ function _prepareOrderBy(description, query, order) {
481
+ if (description.PrimaryKey) {
482
+ query.order(description.PrimaryKey, order ?? enums_js_1.SortOrder.DESC);
483
+ }
484
+ else {
485
+ const unique = description.Columns.filter((c) => c.Unique);
486
+ if (unique.length !== 0) {
487
+ unique.forEach((c) => query.order(c.Name, order ?? enums_js_1.SortOrder.DESC));
488
+ }
489
+ else if (description.Timestamps?.CreatedAt) {
490
+ query.order(description.Timestamps.CreatedAt, order ?? enums_js_1.SortOrder.DESC);
491
+ }
492
+ else if (description.Timestamps?.UpdatedAt) {
493
+ query.order(description.Timestamps.UpdatedAt, order ?? enums_js_1.SortOrder.DESC);
494
+ }
495
+ }
496
+ }
497
+ class HistoricalModel {
498
+ }
499
+ exports.HistoricalModel = HistoricalModel;
500
+ /**
501
+ * Helper function to create query based on model
502
+ *
503
+ * @param model - source model for query
504
+ * @param query - query class
505
+ * @param injectModel - should inject model information into query, if not, query will return raw data
506
+ *
507
+ * @returns
508
+ */
509
+ function createQuery(model, query, injectModel = true) {
510
+ const dsc = _descriptor(model);
511
+ if (!dsc) {
512
+ throw new Error(`model ${model.name} does not have model descriptor. Use @model decorator on class`);
513
+ }
514
+ const orm = di_1.DI.get(orm_js_1.Orm);
515
+ const driver = orm.Connections.get(dsc.Connection);
516
+ if (!driver) {
517
+ throw new Error(`model ${model.name} have invalid connection ${dsc.Connection}, please check your db config file or model connection name`);
518
+ }
519
+ const cnt = driver.Container;
520
+ const qr = cnt.resolve(query, [driver, injectModel ? orm.Models.find((x) => x.name === model.name).type : null]);
521
+ if (qr instanceof builders_js_1.SelectQueryBuilder) {
522
+ const scope = model._queryScopes;
523
+ if (scope) {
524
+ Object.getOwnPropertyNames(scope.__proto__)
525
+ .filter((x) => x !== 'constructor')
526
+ .forEach(function (property) {
527
+ if (typeof scope[property] === 'function') {
528
+ qr[property] = scope[property].bind(qr);
529
+ }
530
+ });
531
+ }
532
+ }
533
+ qr.middleware(new middlewares_js_1.DiscriminationMapMiddleware(dsc));
534
+ qr.setTable(dsc.TableName);
535
+ if (driver.Options.Database) {
536
+ qr.database(driver.Options.Database);
537
+ }
538
+ return {
539
+ query: qr,
540
+ description: dsc,
541
+ model,
542
+ container: driver.Container,
543
+ };
544
+ }
545
+ exports.createQuery = createQuery;
546
+ exports.MODEL_STATIC_MIXINS = {
547
+ getModelDescriptor() {
548
+ const dsc = _descriptor(this);
549
+ if (!dsc) {
550
+ throw new exceptions_js_1.OrmException(`Model ${this.constructor.name} has no descriptor`);
551
+ }
552
+ return dsc;
553
+ },
554
+ getRelationDescriptor(relation) {
555
+ const descriptor = this.getModelDescriptor();
556
+ let rDescriptor = null;
557
+ for (const [key, value] of descriptor.Relations) {
558
+ if (key.toLowerCase() === relation.toLowerCase().trim()) {
559
+ rDescriptor = value;
560
+ break;
561
+ }
562
+ }
563
+ if (!rDescriptor) {
564
+ throw new exceptions_js_1.OrmException(`Model ${this.constructor.name} has no relation ${relation}`);
565
+ }
566
+ return rDescriptor;
567
+ },
568
+ truncate() {
569
+ const { query } = createQuery(this, builders_js_1.TruncateTableQueryBuilder, false);
570
+ return query;
571
+ },
572
+ driver() {
573
+ const dsc = this.getModelDescriptor();
574
+ const orm = di_1.DI.get(orm_js_1.Orm);
575
+ const driver = orm.Connections.get(dsc.Connection);
576
+ if (!driver) {
577
+ throw new Error(`model ${this.name} have invalid connection ${dsc.Connection}, please check your db config file or model connection name`);
578
+ }
579
+ return driver;
580
+ },
581
+ query() {
582
+ const { query } = createQuery(this, builders_js_1.SelectQueryBuilder);
583
+ return query;
584
+ },
585
+ where(column, operator, value) {
586
+ const { query } = createQuery(this, builders_js_1.SelectQueryBuilder);
587
+ query.select('*');
588
+ return query.where(column, operator, value);
589
+ },
590
+ update(data) {
591
+ const { query } = createQuery(this, builders_js_1.UpdateQueryBuilder);
592
+ return query.update(data);
593
+ },
594
+ all(page, perPage) {
595
+ const { query } = createQuery(this, builders_js_1.SelectQueryBuilder);
596
+ query.select('*');
597
+ if (page >= 0 && perPage > 0) {
598
+ query.take(perPage).skip(page * perPage);
599
+ }
600
+ return query;
601
+ },
602
+ /**
603
+ * Try to insert new value
604
+ */
605
+ async insert(data, insertBehaviour = interfaces_js_1.InsertBehaviour.None) {
606
+ const { query, description, container } = createQuery(this, builders_js_1.InsertQueryBuilder);
607
+ const converter = container.resolve(interfaces_js_1.ObjectToSqlConverter);
608
+ if (Array.isArray(data)) {
609
+ if (insertBehaviour !== interfaces_js_1.InsertBehaviour.None) {
610
+ throw new exceptions_js_1.OrmException(`insert behaviour is not supported with arrays`);
611
+ }
612
+ query.values(data.map((d) => {
613
+ if (d instanceof ModelBase) {
614
+ return d.toSql();
615
+ }
616
+ return converter.toSql(d);
617
+ }));
618
+ }
619
+ else {
620
+ switch (insertBehaviour) {
621
+ case interfaces_js_1.InsertBehaviour.InsertOrIgnore:
622
+ query.orIgnore();
623
+ break;
624
+ case interfaces_js_1.InsertBehaviour.InsertOrUpdate:
625
+ query.onDuplicate().update(description.Columns.filter((c) => !c.PrimaryKey).map((c) => c.Name));
626
+ break;
627
+ case interfaces_js_1.InsertBehaviour.InsertOrReplace:
628
+ query.orReplace();
629
+ break;
630
+ }
631
+ if (data instanceof ModelBase) {
632
+ query.values(data.toSql());
633
+ }
634
+ else {
635
+ query.values(converter.toSql(data));
636
+ }
637
+ }
638
+ const iMidleware = {
639
+ afterQuery: (result) => {
640
+ if (Array.isArray(data)) {
641
+ data.forEach((v, idx) => {
642
+ if (v instanceof ModelBase) {
643
+ v.PrimaryKeyValue = v.PrimaryKeyValue ?? result.LastInsertId - data.length + idx + 1;
644
+ }
645
+ });
646
+ }
647
+ else if (data instanceof ModelBase) {
648
+ data.PrimaryKeyValue = data.PrimaryKeyValue ?? result.LastInsertId;
649
+ }
650
+ return result;
651
+ },
652
+ modelCreation: () => null,
653
+ afterHydration: () => null,
654
+ };
655
+ query.middleware(iMidleware);
656
+ return query;
657
+ },
658
+ async find(pks) {
659
+ const { query, description } = createQuery(this, builders_js_1.SelectQueryBuilder);
660
+ const pkey = description.PrimaryKey;
661
+ query.select('*');
662
+ query.whereIn(pkey, pks);
663
+ return await query;
664
+ },
665
+ async findOrFail(pks) {
666
+ const { query, description, model } = createQuery(this, builders_js_1.SelectQueryBuilder);
667
+ const pkey = description.PrimaryKey;
668
+ query.select('*');
669
+ query.whereIn(pkey, pks);
670
+ const result = await query;
671
+ if (result.length !== pks.length) {
672
+ throw new Error(`could not find all results for model ${model.name}`);
673
+ }
674
+ return result;
675
+ },
676
+ async get(pk) {
677
+ const { query, description } = createQuery(this, builders_js_1.SelectQueryBuilder);
678
+ const pkey = description.PrimaryKey;
679
+ query.select('*');
680
+ query.where(pkey, pk);
681
+ _prepareOrderBy(description, query);
682
+ return (await query.first());
683
+ },
684
+ async getOrFail(pk) {
685
+ const { query, description } = createQuery(this, builders_js_1.SelectQueryBuilder);
686
+ const pkey = description.PrimaryKey;
687
+ query.select('*');
688
+ query.where(pkey, pk);
689
+ _prepareOrderBy(description, query);
690
+ return (await query.firstOrFail());
691
+ },
692
+ destroy(pks) {
693
+ const description = _descriptor(this);
694
+ const data = Array.isArray(pks) ? pks : [pks];
695
+ const { query } = description.SoftDelete?.DeletedAt ? createQuery(this, builders_js_1.UpdateQueryBuilder) : createQuery(this, builders_js_1.DeleteQueryBuilder);
696
+ if (description.SoftDelete?.DeletedAt) {
697
+ query.update({
698
+ [description.SoftDelete.DeletedAt]: luxon_1.DateTime.now(),
699
+ });
700
+ }
701
+ if (pks) {
702
+ query.whereIn(description.PrimaryKey, data);
703
+ }
704
+ return query;
705
+ },
706
+ async create(data) {
707
+ const entity = new (Function.prototype.bind.apply(this))(data);
708
+ await entity.insert();
709
+ return entity;
710
+ },
711
+ async getOrCreate(pk, data) {
712
+ const { query, description } = createQuery(this, builders_js_1.SelectQueryBuilder);
713
+ // pk constrain
714
+ if (description.PrimaryKey && pk !== null) {
715
+ query.where(description.PrimaryKey, pk);
716
+ }
717
+ // check for all unique columns ( unique constrain )
718
+ description.Columns.filter((c) => c.Unique).forEach((c) => {
719
+ query.andWhere(c, data[c.Name]);
720
+ });
721
+ _prepareOrderBy(description, query);
722
+ let entity = (await query.first());
723
+ if (!entity) {
724
+ entity = new (Function.prototype.bind.apply(this))(data);
725
+ await entity.insert();
726
+ return entity;
727
+ }
728
+ return entity;
729
+ },
730
+ async getOrNew(pk, data) {
731
+ const { query, description } = createQuery(this, builders_js_1.SelectQueryBuilder);
732
+ // pk constrain
733
+ if (description.PrimaryKey) {
734
+ query.where(description.PrimaryKey, pk);
735
+ }
736
+ // check for all unique columns ( unique constrain )
737
+ description.Columns.filter((c) => c.Unique).forEach((c) => {
738
+ query.andWhere(c, data[c.Name]);
739
+ });
740
+ _prepareOrderBy(description, query);
741
+ let entity = (await query.first());
742
+ if (!entity) {
743
+ entity = new (Function.prototype.bind.apply(this))(data);
744
+ return entity;
745
+ }
746
+ return entity;
747
+ },
748
+ async exists(pk) {
749
+ const { query, description } = createQuery(this, builders_js_1.SelectQueryBuilder);
750
+ // pk constrain
751
+ if (description.PrimaryKey && pk !== null) {
752
+ query.where(description.PrimaryKey, pk);
753
+ }
754
+ const result = await query.clearColumns().select(description.PrimaryKey).first();
755
+ if (result) {
756
+ return true;
757
+ }
758
+ return false;
759
+ },
760
+ async first(callback) {
761
+ const { query, description } = createQuery(this, builders_js_1.SelectQueryBuilder);
762
+ _prepareOrderBy(description, query, enums_js_1.SortOrder.ASC);
763
+ if (callback) {
764
+ callback(query);
765
+ }
766
+ return (await query.first());
767
+ },
768
+ async last(callback) {
769
+ const { query, description } = createQuery(this, builders_js_1.SelectQueryBuilder);
770
+ _prepareOrderBy(description, query, enums_js_1.SortOrder.DESC);
771
+ if (callback) {
772
+ callback(query);
773
+ }
774
+ return (await query.first());
775
+ },
776
+ async newest(callback) {
777
+ const { query, description } = createQuery(this, builders_js_1.SelectQueryBuilder);
778
+ if (description.Timestamps?.CreatedAt) {
779
+ query.order(description.Timestamps.CreatedAt, enums_js_1.SortOrder.DESC);
780
+ }
781
+ else {
782
+ throw new exceptions_js_1.OrmException('cannot fetch newest entity - CreateAt column not exists in model/db');
783
+ }
784
+ if (callback) {
785
+ callback(query);
786
+ }
787
+ return (await query.first());
788
+ },
789
+ async oldest(callback) {
790
+ const { query, description } = createQuery(this, builders_js_1.SelectQueryBuilder);
791
+ if (description.Timestamps?.CreatedAt) {
792
+ query.order(description.Timestamps.CreatedAt, enums_js_1.SortOrder.ASC);
793
+ }
794
+ else {
795
+ throw new exceptions_js_1.OrmException('cannot fetch oldest entity - CreateAt column not exists in model/db');
796
+ }
797
+ if (callback) {
798
+ callback(query);
799
+ }
800
+ return (await query.first());
801
+ },
802
+ async count(callback) {
803
+ const { query } = createQuery(this, builders_js_1.SelectQueryBuilder);
804
+ query.count('*', 'count');
805
+ if (callback) {
806
+ callback(query);
807
+ }
808
+ return await (await query.asRaw()).count;
809
+ },
810
+ };
811
811
  //# sourceMappingURL=model.js.map