ilana-orm 1.0.14 → 1.0.16

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/orm/Relation.d.ts CHANGED
@@ -52,9 +52,11 @@ export class BelongsToMany extends Relation {
52
52
  withTimestamps(): this;
53
53
  addConstraints(): void;
54
54
  getResults(): Promise<Model[]>;
55
- attach(id: any, attributes?: any): Promise<void>;
55
+ attach(id: any, attributes?: Record<string, any>): Promise<void>;
56
56
  detach(id?: any): Promise<number>;
57
- sync(ids: any[]): Promise<void>;
57
+ sync(ids: any[] | Record<string | number, Record<string, any>>): Promise<void>;
58
+ toggle(ids: any | any[]): Promise<void>;
59
+ updateExistingPivot(id: any, attributes: Record<string, any>): Promise<number>;
58
60
  }
59
61
 
60
62
  export class HasManyThrough extends Relation {
@@ -96,4 +98,14 @@ export class MorphMany extends Relation {
96
98
 
97
99
  addConstraints(): void;
98
100
  getResults(): Promise<Collection<Model>>;
99
- }
101
+ }
102
+ export class MorphOne extends Relation {
103
+ protected morphType: string;
104
+ protected morphId: string;
105
+ protected morphClass: string;
106
+
107
+ constructor(parent: Model, related: string | typeof Model, morphType: string, morphId: string, morphClass: string);
108
+
109
+ addConstraints(): void;
110
+ getResults(): Promise<Model | null>;
111
+ }
package/orm/Relation.js CHANGED
@@ -198,10 +198,39 @@ class BelongsToMany extends Relation {
198
198
 
199
199
  async sync(ids) {
200
200
  await this.detach();
201
- for (const id of ids) {
202
- await this.attach(id);
201
+ if (Array.isArray(ids)) {
202
+ for (const id of ids) {
203
+ await this.attach(id);
204
+ }
205
+ } else {
206
+ // Object form: { id: { pivot_col: val } }
207
+ for (const [id, attrs] of Object.entries(ids)) {
208
+ await this.attach(id, attrs);
209
+ }
210
+ }
211
+ }
212
+
213
+ async toggle(ids) {
214
+ const current = await this.getResults();
215
+ const currentIds = current.map(m => String(m.getAttribute(this.relatedKey)));
216
+ const list = Array.isArray(ids) ? ids : [ids];
217
+ for (const id of list) {
218
+ if (currentIds.includes(String(id))) {
219
+ await this.detach(id);
220
+ } else {
221
+ await this.attach(id);
222
+ }
203
223
  }
204
224
  }
225
+
226
+ async updateExistingPivot(id, attributes) {
227
+ const data = { ...attributes };
228
+ if (this.pivotTimestamps) data.updated_at = new Date();
229
+ return new QueryBuilder(this.pivotTable)
230
+ .where(this.parentPivotKey, this.parent.getAttribute(this.parentKey))
231
+ .where(this.relatedPivotKey, id)
232
+ .update(data);
233
+ }
205
234
  }
206
235
 
207
236
  class HasManyThrough extends Relation {
@@ -227,10 +256,16 @@ class HasManyThrough extends Relation {
227
256
  }
228
257
 
229
258
  async getResults() {
259
+ const relatedClass = this.getRelatedClass();
260
+ const throughClass = ModelRegistry.get(this.through) || null;
261
+ const throughTable = throughClass
262
+ ? throughClass.getTableName()
263
+ : (typeof this.through === 'string' ? this.through : this.through.getTableName());
264
+ const relatedTable = relatedClass.getTableName();
230
265
  return this.newQuery()
231
- .select(`${this.related.getTableName()}.*`)
232
- .join(this.through.getTableName(), `${this.related.getTableName()}.${this.secondLocalKey}`, `${this.through.getTableName()}.${this.secondKey}`)
233
- .where(`${this.through.getTableName()}.${this.firstKey}`, this.parent.getAttribute(this.localKey))
266
+ .select(`${relatedTable}.*`)
267
+ .join(throughTable, `${relatedTable}.${this.secondKey}`, `${throughTable}.${this.secondLocalKey}`)
268
+ .where(`${throughTable}.${this.firstKey}`, this.parent.getAttribute(this.localKey))
234
269
  .get();
235
270
  }
236
271
  }
@@ -238,9 +273,12 @@ class HasManyThrough extends Relation {
238
273
  // Polymorphic Relations
239
274
  class MorphTo extends Relation {
240
275
  constructor(parent, morphType, morphId) {
241
- super(parent, Model, morphId, 'id');
242
- this.morphType = morphType;
243
- this.morphId = morphId;
276
+ // Accept either a base name ('commentable') or full column names ('commentable_type', 'commentable_id')
277
+ const typeCol = morphType && !morphType.endsWith('_type') ? `${morphType}_type` : morphType;
278
+ const idCol = morphId || (morphType ? `${morphType.replace(/_type$/, '')}_id` : undefined);
279
+ super(parent, Model, idCol, 'id');
280
+ this.morphType = typeCol;
281
+ this.morphId = idCol;
244
282
  }
245
283
 
246
284
  addConstraints() {
@@ -269,9 +307,12 @@ class MorphTo extends Relation {
269
307
 
270
308
  class MorphMany extends Relation {
271
309
  constructor(parent, related, morphType, morphId, morphClass) {
272
- super(parent, related, morphId, 'id');
273
- this.morphType = morphType;
274
- this.morphId = morphId;
310
+ // Accept either a base name ('commentable') or full column names ('commentable_type', 'commentable_id')
311
+ const typeCol = morphType && !morphType.endsWith('_type') ? `${morphType}_type` : morphType;
312
+ const idCol = morphId || (morphType ? `${morphType.replace(/_type$/, '')}_id` : undefined);
313
+ super(parent, related, idCol, 'id');
314
+ this.morphType = typeCol;
315
+ this.morphId = idCol;
275
316
  this.morphClass = morphClass;
276
317
  }
277
318
 
@@ -287,6 +328,27 @@ class MorphMany extends Relation {
287
328
  }
288
329
  }
289
330
 
331
+ class MorphOne extends Relation {
332
+ constructor(parent, related, morphType, morphId, morphClass) {
333
+ // Accept either a base name ('imageable') or full column names ('imageable_type', 'imageable_id')
334
+ const typeCol = morphType && !morphType.endsWith('_type') ? `${morphType}_type` : morphType;
335
+ const idCol = morphId || (morphType ? `${morphType.replace(/_type$/, '')}_id` : undefined);
336
+ super(parent, related, idCol, 'id');
337
+ this.morphType = typeCol;
338
+ this.morphId = idCol;
339
+ this.morphClass = morphClass;
340
+ }
341
+
342
+ addConstraints() {}
343
+
344
+ async getResults() {
345
+ return this.newQuery()
346
+ .where(this.morphType, this.morphClass)
347
+ .where(this.morphId, this.parent.getAttribute(this.localKey))
348
+ .first();
349
+ }
350
+ }
351
+
290
352
  module.exports = {
291
353
  Relation,
292
354
  HasOne,
@@ -295,5 +357,6 @@ module.exports = {
295
357
  BelongsToMany,
296
358
  HasManyThrough,
297
359
  MorphTo,
360
+ MorphOne,
298
361
  MorphMany
299
362
  };
package/orm/Relation.mjs CHANGED
@@ -4,4 +4,4 @@ const require = createRequire(import.meta.url);
4
4
 
5
5
  const Relations = require('./Relation.js');
6
6
 
7
- export const { Relation, HasOne, HasMany, BelongsTo, BelongsToMany, HasManyThrough, MorphTo, MorphMany } = Relations;
7
+ export const { Relation, HasOne, HasMany, BelongsTo, BelongsToMany, HasManyThrough, MorphTo, MorphOne, MorphMany } = Relations;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ilana-orm",
3
- "version": "1.0.14",
3
+ "version": "1.0.16",
4
4
  "description": "A fully-featured, Eloquent-style ORM for Node.js with TypeScript support",
5
5
  "main": "index.js",
6
6
  "types": "index.d.ts",
@@ -114,14 +114,15 @@
114
114
  "ilana.png"
115
115
  ],
116
116
  "dependencies": {
117
- "knex": "^3.0.0",
118
- "uuid": "^9.0.0",
119
- "@faker-js/faker": "^8.0.0",
120
- "pg": "^8.0.0",
121
- "mysql2": "^3.0.0",
122
- "sqlite3": "^5.0.0",
123
- "dotenv": "^16.3.1",
124
- "moment-timezone": "^0.6.0"
117
+ "knex": "^3.1.0",
118
+ "uuid": "^10.0.0",
119
+ "@faker-js/faker": "^9.0.0",
120
+ "dotenv": "^16.4.0"
121
+ },
122
+ "optionalDependencies": {
123
+ "pg": "^8.13.0",
124
+ "mysql2": "^3.11.0",
125
+ "sqlite3": "^5.1.7"
125
126
  },
126
127
  "devDependencies": {
127
128
  "@types/node": "^20.0.0",