1 |
|
2 |
|
3 | 'use strict';
|
4 |
|
5 | const _ = require('lodash');
|
6 | const inflection = require('inflection');
|
7 |
|
8 | const Helpers = require('./helpers');
|
9 | const ModelBase = require('./base/model');
|
10 | const RelationBase = require('./base/relation');
|
11 | const Promise = require('./base/promise');
|
12 | const constants = require('./constants');
|
13 | const push = Array.prototype.push;
|
14 | const removePivotPrefix = (key) => key.slice(constants.PIVOT_PREFIX.length);
|
15 | const hasPivotPrefix = (key) => _.startsWith(key, constants.PIVOT_PREFIX);
|
16 |
|
17 | const Relation = RelationBase.extend({
|
18 |
|
19 |
|
20 |
|
21 | init(parent) {
|
22 | this.parentId = parent.id;
|
23 | this.parentTableName = _.result(parent, 'tableName');
|
24 | this.parentIdAttribute = this.attribute('parentIdAttribute', parent);
|
25 |
|
26 |
|
27 |
|
28 | this.parentAttributes = parent.format(_.clone(parent.attributes));
|
29 |
|
30 | if (this.type === 'morphTo' && !parent._isEager) {
|
31 |
|
32 |
|
33 | this.target = Helpers.morphCandidate(this.candidates, this.parentAttributes[this.key('morphKey')]);
|
34 | this.targetTableName = _.result(this.target.prototype, 'tableName');
|
35 | }
|
36 |
|
37 | this.targetIdAttribute = this.attribute('targetIdAttribute', parent);
|
38 | this.parentFk = this.attribute('parentFk');
|
39 |
|
40 | const target = this.target ? this.relatedInstance() : {};
|
41 | target.relatedData = this;
|
42 |
|
43 | if (this.type === 'belongsToMany') {
|
44 | _.extend(target, pivotHelpers);
|
45 | }
|
46 |
|
47 | return target;
|
48 | },
|
49 |
|
50 |
|
51 |
|
52 | through(source, Target, options) {
|
53 | const type = this.type;
|
54 | if (type !== 'hasOne' && type !== 'hasMany' && type !== 'belongsToMany' && type !== 'belongsTo') {
|
55 | throw new Error('`through` is only chainable from `hasOne`, `belongsTo`, `hasMany`, or `belongsToMany`');
|
56 | }
|
57 |
|
58 | this.throughTarget = Target;
|
59 | this.throughTableName = _.result(Target.prototype, 'tableName');
|
60 |
|
61 | _.extend(this, options);
|
62 | _.extend(source, pivotHelpers);
|
63 |
|
64 | this.parentIdAttribute = this.attribute('parentIdAttribute');
|
65 | this.targetIdAttribute = this.attribute('targetIdAttribute');
|
66 | this.throughIdAttribute = this.attribute('throughIdAttribute', Target);
|
67 | this.parentFk = this.attribute('parentFk');
|
68 |
|
69 |
|
70 | if (this.type === 'belongsToMany') {
|
71 | this.foreignKey = this.throughForeignKey;
|
72 | } else if (this.otherKey) {
|
73 | this.foreignKey = this.otherKey;
|
74 | }
|
75 |
|
76 | return source;
|
77 | },
|
78 |
|
79 |
|
80 |
|
81 | key(keyName) {
|
82 | if (this[keyName]) return this[keyName];
|
83 | switch (keyName) {
|
84 | case 'otherKey':
|
85 | this[keyName] = singularMemo(this.targetTableName) + '_' + this.targetIdAttribute;
|
86 | break;
|
87 | case 'throughForeignKey':
|
88 | this[keyName] = singularMemo(this.joinTable()) + '_' + this.throughIdAttribute;
|
89 | break;
|
90 | case 'foreignKey':
|
91 | switch (this.type) {
|
92 | case 'morphTo': {
|
93 | const idKeyName = this.columnNames && this.columnNames[1] ? this.columnNames[1] : this.morphName + '_id';
|
94 | this[keyName] = idKeyName;
|
95 | break;
|
96 | }
|
97 | case 'belongsTo':
|
98 | this[keyName] = singularMemo(this.targetTableName) + '_' + this.targetIdAttribute;
|
99 | break;
|
100 | default:
|
101 | if (this.isMorph()) {
|
102 | this[keyName] = this.columnNames && this.columnNames[1] ? this.columnNames[1] : this.morphName + '_id';
|
103 | break;
|
104 | }
|
105 | this[keyName] = singularMemo(this.parentTableName) + '_' + this.parentIdAttribute;
|
106 | break;
|
107 | }
|
108 | break;
|
109 | case 'morphKey':
|
110 | this[keyName] = this.columnNames && this.columnNames[0] ? this.columnNames[0] : this.morphName + '_type';
|
111 | break;
|
112 | case 'morphValue':
|
113 | this[keyName] = this.morphValue || this.parentTableName || this.targetTableName;
|
114 | break;
|
115 | }
|
116 | return this[keyName];
|
117 | },
|
118 |
|
119 |
|
120 |
|
121 | attribute(attribute, parent) {
|
122 | switch (attribute) {
|
123 | case 'parentIdAttribute':
|
124 | if (this.isThrough()) {
|
125 | if (this.type === 'belongsTo' && this.throughForeignKey) {
|
126 | return this.throughForeignKey;
|
127 | }
|
128 |
|
129 | if (this.type === 'belongsToMany' && this.isThroughForeignKeyTargeted()) {
|
130 | return this.throughForeignKeyTarget;
|
131 | }
|
132 |
|
133 | if (this.isOtherKeyTargeted()) {
|
134 | return this.otherKeyTarget;
|
135 | }
|
136 |
|
137 |
|
138 | return this.parentIdAttribute;
|
139 | }
|
140 |
|
141 | if (this.type === 'belongsTo' && this.foreignKey) {
|
142 | return this.foreignKey;
|
143 | }
|
144 |
|
145 | if (this.type !== 'belongsTo' && this.isForeignKeyTargeted()) {
|
146 | return this.foreignKeyTarget;
|
147 | }
|
148 |
|
149 | return _.result(parent, 'idAttribute');
|
150 |
|
151 | case 'targetIdAttribute':
|
152 | if (this.isThrough()) {
|
153 | if ((this.type === 'belongsToMany' || this.type === 'belongsTo') && this.isOtherKeyTargeted()) {
|
154 | return this.otherKeyTarget;
|
155 | }
|
156 |
|
157 |
|
158 | return this.targetIdAttribute;
|
159 | }
|
160 |
|
161 | if (this.type === 'morphTo' && !parent._isEager) {
|
162 | return _.result(this.target.prototype, 'idAttribute');
|
163 | }
|
164 |
|
165 | if (this.type === 'belongsTo' && this.isForeignKeyTargeted()) {
|
166 | return this.foreignKeyTarget;
|
167 | }
|
168 |
|
169 | if (this.type === 'belongsToMany' && this.isOtherKeyTargeted()) {
|
170 | return this.otherKeyTarget;
|
171 | }
|
172 |
|
173 | return this.targetIdAttribute;
|
174 |
|
175 | case 'throughIdAttribute':
|
176 | if (this.type !== 'belongsToMany' && this.isThroughForeignKeyTargeted()) {
|
177 | return this.throughForeignKeyTarget;
|
178 | }
|
179 |
|
180 | if (this.type === 'belongsToMany' && this.throughForeignKey) {
|
181 | return this.throughForeignKey;
|
182 | }
|
183 |
|
184 | return _.result(parent.prototype, 'idAttribute');
|
185 |
|
186 | case 'parentFk':
|
187 | if (!this.hasParentAttributes()) {
|
188 | return;
|
189 | }
|
190 |
|
191 | if (this.isThrough()) {
|
192 | if (this.type === 'belongsToMany' && this.isThroughForeignKeyTargeted()) {
|
193 | return this.parentAttributes[this.throughForeignKeyTarget];
|
194 | }
|
195 |
|
196 | if (this.type === 'belongsTo') {
|
197 | return this.throughForeignKey ? this.parentAttributes[this.parentIdAttribute] : this.parentId;
|
198 | }
|
199 |
|
200 | if (this.isOtherKeyTargeted()) {
|
201 | return this.parentAttributes[this.otherKeyTarget];
|
202 | }
|
203 |
|
204 |
|
205 | return this.parentFk;
|
206 | }
|
207 |
|
208 | return this.parentAttributes[this.isInverse() ? this.key('foreignKey') : this.parentIdAttribute];
|
209 | }
|
210 | },
|
211 |
|
212 |
|
213 | selectConstraints(knex, options) {
|
214 | const resp = options.parentResponse;
|
215 |
|
216 |
|
217 | if (this.isJoined()) this.joinClauses(knex);
|
218 |
|
219 |
|
220 | if (options._beforeFn) options._beforeFn.call(knex, knex);
|
221 |
|
222 |
|
223 | if (Array.isArray(options.columns)) {
|
224 | knex.columns(options.columns);
|
225 | }
|
226 |
|
227 | const currentColumns = _.find(knex._statements, {grouping: 'columns'});
|
228 |
|
229 | if (!currentColumns || currentColumns.length === 0) {
|
230 | knex.distinct(this.targetTableName + '.*');
|
231 | }
|
232 |
|
233 | if (this.isJoined()) this.joinColumns(knex);
|
234 |
|
235 |
|
236 |
|
237 | if (this.isSingle() && !resp) knex.limit(1);
|
238 |
|
239 |
|
240 | this.whereClauses(knex, resp);
|
241 | },
|
242 |
|
243 |
|
244 | joinColumns(knex) {
|
245 | const columns = [];
|
246 | const joinTable = this.joinTable();
|
247 | if (this.isThrough()) columns.push(this.throughIdAttribute);
|
248 | columns.push(this.key('foreignKey'));
|
249 | if (this.type === 'belongsToMany') columns.push(this.key('otherKey'));
|
250 | push.apply(columns, this.pivotColumns);
|
251 | knex.columns(
|
252 | _.map(columns, function(col) {
|
253 | return joinTable + '.' + col + ' as _pivot_' + col;
|
254 | })
|
255 | );
|
256 | },
|
257 |
|
258 |
|
259 | joinClauses(knex) {
|
260 | const joinTable = this.joinTable();
|
261 |
|
262 | if (this.type === 'belongsTo' || this.type === 'belongsToMany') {
|
263 | const targetKey = this.type === 'belongsTo' ? this.key('foreignKey') : this.key('otherKey');
|
264 |
|
265 | knex.join(joinTable, joinTable + '.' + targetKey, '=', this.targetTableName + '.' + this.targetIdAttribute);
|
266 |
|
267 |
|
268 | if (this.type === 'belongsTo') {
|
269 | knex.join(
|
270 | this.parentTableName,
|
271 | joinTable + '.' + this.throughIdAttribute,
|
272 | '=',
|
273 | this.parentTableName + '.' + this.key('throughForeignKey')
|
274 | );
|
275 | }
|
276 | } else {
|
277 | knex.join(
|
278 | joinTable,
|
279 | joinTable + '.' + this.throughIdAttribute,
|
280 | '=',
|
281 | this.targetTableName + '.' + this.key('throughForeignKey')
|
282 | );
|
283 | }
|
284 | },
|
285 |
|
286 |
|
287 |
|
288 | whereClauses(knex, response) {
|
289 | let key;
|
290 |
|
291 | if (this.isJoined()) {
|
292 | const isBelongsTo = this.type === 'belongsTo';
|
293 | const targetTable = isBelongsTo ? this.parentTableName : this.joinTable();
|
294 |
|
295 | const column = isBelongsTo ? this.parentIdAttribute : this.key('foreignKey');
|
296 |
|
297 | key = `${targetTable}.${column}`;
|
298 | } else {
|
299 | const column = this.isInverse() ? this.targetIdAttribute : this.key('foreignKey');
|
300 |
|
301 | key = `${this.targetTableName}.${column}`;
|
302 | }
|
303 |
|
304 | const method = response ? 'whereIn' : 'where';
|
305 | const ids = response ? this.eagerKeys(response) : this.parentFk;
|
306 | knex[method](key, ids);
|
307 |
|
308 | if (this.isMorph()) {
|
309 | const table = this.targetTableName;
|
310 | const key = this.key('morphKey');
|
311 | const value = this.key('morphValue');
|
312 | knex.where(`${table}.${key}`, value);
|
313 | }
|
314 | },
|
315 |
|
316 |
|
317 | eagerKeys(response) {
|
318 | const key = this.isInverse() && !this.isThrough() ? this.key('foreignKey') : this.parentIdAttribute;
|
319 | return _.reject(
|
320 | _(response)
|
321 | .map(key)
|
322 | .uniq()
|
323 | .value(),
|
324 | _.isNil
|
325 | );
|
326 | },
|
327 |
|
328 |
|
329 | joinTable() {
|
330 | if (this.isThrough()) return this.throughTableName;
|
331 | return this.joinTableName || [this.parentTableName, this.targetTableName].sort().join('_');
|
332 | },
|
333 |
|
334 |
|
335 |
|
336 | relatedInstance(models, options) {
|
337 | models = models || [];
|
338 | options = options || {};
|
339 |
|
340 | const Target = this.target;
|
341 |
|
342 |
|
343 |
|
344 | if (this.isSingle()) {
|
345 | if (!(Target.prototype instanceof ModelBase)) {
|
346 | throw new Error(`The ${this.type} related object must be a Bookshelf.Model`);
|
347 | }
|
348 | return models[0] || new Target();
|
349 | }
|
350 |
|
351 |
|
352 |
|
353 | if (Target.prototype instanceof ModelBase) {
|
354 | return Target.collection(models, {
|
355 | parse: true,
|
356 | merge: options.merge,
|
357 | remove: options.remove
|
358 | });
|
359 | }
|
360 | return new Target(models, {parse: true});
|
361 | },
|
362 |
|
363 |
|
364 |
|
365 | eagerPair(relationName, related, parentModels, options) {
|
366 |
|
367 | if (this.type === 'morphTo') {
|
368 | parentModels = _.filter(parentModels, (m) => {
|
369 | return m.get(this.key('morphKey')) === this.key('morphValue');
|
370 | });
|
371 | }
|
372 |
|
373 |
|
374 | if (this.isJoined()) related = this.parsePivot(related);
|
375 |
|
376 |
|
377 | const idKey = (key) => (_.isBuffer(key) ? key.toString('hex') : key);
|
378 | const grouped = _.groupBy(related, (m) => {
|
379 | let key;
|
380 | if (m.pivot) {
|
381 | if (this.isInverse() && this.isThrough()) {
|
382 | key = this.isThroughForeignKeyTargeted() ? m.pivot.get(this.throughForeignKeyTarget) : m.pivot.id;
|
383 | } else {
|
384 | key = m.pivot.get(this.key('foreignKey'));
|
385 | }
|
386 | } else if (this.isInverse()) {
|
387 | key = this.isForeignKeyTargeted() ? m.get(this.foreignKeyTarget) : m.id;
|
388 | } else {
|
389 | key = m.get(this.key('foreignKey'));
|
390 | }
|
391 | return idKey(key);
|
392 | });
|
393 |
|
394 |
|
395 |
|
396 | _.each(parentModels, (model) => {
|
397 | let groupedKey;
|
398 | if (!this.isInverse()) {
|
399 | const parsedKey = Object.keys(model.parse({[this.parentIdAttribute]: null}))[0];
|
400 | groupedKey = idKey(model.get(parsedKey));
|
401 | } else {
|
402 | const keyColumn = this.key(this.isThrough() ? 'throughForeignKey' : 'foreignKey');
|
403 | const formatted = model.format(_.clone(model.attributes));
|
404 | groupedKey = idKey(formatted[keyColumn]);
|
405 | }
|
406 | if (groupedKey != null) {
|
407 | const relation = (model.relations[relationName] = this.relatedInstance(grouped[groupedKey], options));
|
408 | if (this.type === 'belongsToMany') {
|
409 |
|
410 | relation.relatedData = model[relationName]().relatedData;
|
411 | } else {
|
412 | relation.relatedData = this;
|
413 | }
|
414 | if (this.isJoined()) _.extend(relation, pivotHelpers);
|
415 | }
|
416 | });
|
417 |
|
418 |
|
419 |
|
420 | related.map((model) => {
|
421 | model.attributes = model.parse(model.attributes);
|
422 | model.formatTimestamps()._previousAttributes = _.cloneDeep(model.attributes);
|
423 | model._reset();
|
424 | });
|
425 |
|
426 | return related;
|
427 | },
|
428 |
|
429 | parsePivot(models) {
|
430 | return _.map(models, (model) => {
|
431 |
|
432 | const grouped = _.reduce(
|
433 | model.attributes,
|
434 | (acc, value, key) => {
|
435 | if (hasPivotPrefix(key)) {
|
436 | acc.pivot[removePivotPrefix(key)] = value;
|
437 | } else {
|
438 | acc.model[key] = value;
|
439 | }
|
440 | return acc;
|
441 | },
|
442 | {model: {}, pivot: {}}
|
443 | );
|
444 |
|
445 |
|
446 | model.attributes = grouped.model;
|
447 |
|
448 |
|
449 |
|
450 | if (!_.isEmpty(grouped.pivot)) {
|
451 | const Through = this.throughTarget;
|
452 | const tableName = this.joinTable();
|
453 | model.pivot = Through != null ? new Through(grouped.pivot) : new this.Model(grouped.pivot, {tableName});
|
454 | }
|
455 |
|
456 | return model;
|
457 | });
|
458 | },
|
459 |
|
460 |
|
461 | isThrough() {
|
462 | return this.throughTarget != null;
|
463 | },
|
464 | isJoined() {
|
465 | return this.type === 'belongsToMany' || this.isThrough();
|
466 | },
|
467 | isMorph() {
|
468 | return this.type === 'morphOne' || this.type === 'morphMany';
|
469 | },
|
470 | isSingle() {
|
471 | const type = this.type;
|
472 | return type === 'hasOne' || type === 'belongsTo' || type === 'morphOne' || type === 'morphTo';
|
473 | },
|
474 | isInverse() {
|
475 | return this.type === 'belongsTo' || this.type === 'morphTo';
|
476 | },
|
477 | isForeignKeyTargeted() {
|
478 | return this.foreignKeyTarget != null;
|
479 | },
|
480 | isThroughForeignKeyTargeted() {
|
481 | return this.throughForeignKeyTarget != null;
|
482 | },
|
483 | isOtherKeyTargeted() {
|
484 | return this.otherKeyTarget != null;
|
485 | },
|
486 | hasParentAttributes() {
|
487 | return this.parentAttributes != null;
|
488 | },
|
489 |
|
490 |
|
491 | withPivot(columns) {
|
492 | if (!Array.isArray(columns)) columns = [columns];
|
493 | this.pivotColumns = this.pivotColumns || [];
|
494 | push.apply(this.pivotColumns, columns);
|
495 | }
|
496 | });
|
497 |
|
498 |
|
499 | const singularMemo = (function() {
|
500 | const cache = Object.create(null);
|
501 | return function(arg) {
|
502 | if (!(arg in cache)) {
|
503 | cache[arg] = inflection.singularize(arg);
|
504 | }
|
505 | return cache[arg];
|
506 | };
|
507 | })();
|
508 |
|
509 |
|
510 |
|
511 |
|
512 | const pivotHelpers = {
|
513 | |
514 |
|
515 |
|
516 |
|
517 |
|
518 |
|
519 |
|
520 |
|
521 |
|
522 |
|
523 |
|
524 |
|
525 |
|
526 |
|
527 |
|
528 |
|
529 |
|
530 |
|
531 |
|
532 |
|
533 |
|
534 |
|
535 |
|
536 |
|
537 |
|
538 |
|
539 |
|
540 |
|
541 |
|
542 |
|
543 | attach(ids, options) {
|
544 | return Promise.try(() => this.triggerThen('attaching', this, ids, options))
|
545 | .then(() => this._handler('insert', ids, options))
|
546 | .then((response) => this.triggerThen('attached', this, response, options))
|
547 | .return(this);
|
548 | },
|
549 |
|
550 | |
551 |
|
552 |
|
553 |
|
554 |
|
555 |
|
556 |
|
557 |
|
558 |
|
559 |
|
560 |
|
561 |
|
562 |
|
563 |
|
564 |
|
565 |
|
566 |
|
567 |
|
568 |
|
569 |
|
570 | detach(ids, options) {
|
571 | return Promise.try(() => this.triggerThen('detaching', this, ids, options))
|
572 | .then(() => this._handler('delete', ids, options))
|
573 | .then((response) => this.triggerThen('detached', this, response, options))
|
574 | .return(this);
|
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 | updatePivot: function(attributes, options) {
|
602 | return this._handler('update', attributes, options);
|
603 | },
|
604 |
|
605 | |
606 |
|
607 |
|
608 |
|
609 |
|
610 |
|
611 |
|
612 |
|
613 |
|
614 |
|
615 |
|
616 |
|
617 |
|
618 |
|
619 |
|
620 |
|
621 |
|
622 | withPivot: function(columns) {
|
623 | this.relatedData.withPivot(columns);
|
624 | return this;
|
625 | },
|
626 |
|
627 |
|
628 |
|
629 | _handler: Promise.method(function(method, ids, options) {
|
630 | const pending = [];
|
631 | if (ids == null) {
|
632 | if (method === 'insert') return Promise.resolve(this);
|
633 | if (method === 'delete') pending.push(this._processPivot(method, null, options));
|
634 | }
|
635 | if (!Array.isArray(ids)) ids = ids ? [ids] : [];
|
636 | _.each(ids, (id) => pending.push(this._processPivot(method, id, options)));
|
637 | return Promise.all(pending).return(this);
|
638 | }),
|
639 |
|
640 |
|
641 |
|
642 |
|
643 |
|
644 | _processPivot: Promise.method(function(method, item) {
|
645 | const relatedData = this.relatedData,
|
646 | args = Array.prototype.slice.call(arguments),
|
647 | fks = {},
|
648 | data = {};
|
649 |
|
650 | fks[relatedData.key('foreignKey')] = relatedData.parentFk;
|
651 |
|
652 |
|
653 |
|
654 |
|
655 | if (_.isObject(item)) {
|
656 | if (item instanceof ModelBase) {
|
657 | fks[relatedData.key('otherKey')] = item.id;
|
658 | } else if (method !== 'update') {
|
659 | _.extend(data, item);
|
660 | }
|
661 | } else if (item) {
|
662 | fks[relatedData.key('otherKey')] = item;
|
663 | }
|
664 |
|
665 | args.push(_.extend(data, fks), fks);
|
666 |
|
667 | if (this.relatedData.throughTarget) {
|
668 | return this._processModelPivot.apply(this, args);
|
669 | }
|
670 |
|
671 | return this._processPlainPivot.apply(this, args);
|
672 | }),
|
673 |
|
674 |
|
675 |
|
676 |
|
677 | _processPlainPivot: Promise.method(function(method, item, options, data) {
|
678 | const relatedData = this.relatedData;
|
679 |
|
680 |
|
681 |
|
682 | const builder = this._builder(relatedData.joinTable());
|
683 | if (options && options.query) {
|
684 | Helpers.query.call(null, {_knex: builder}, [options.query]);
|
685 | }
|
686 |
|
687 | if (options) {
|
688 | if (options.transacting) builder.transacting(options.transacting);
|
689 | if (options.debug) builder.debug();
|
690 | }
|
691 |
|
692 | const collection = this;
|
693 | if (method === 'delete') {
|
694 | return builder
|
695 | .where(data)
|
696 | .del()
|
697 | .then(function() {
|
698 | if (!item) return collection.reset();
|
699 | const model = collection.get(data[relatedData.key('otherKey')]);
|
700 | if (model) {
|
701 | collection.remove(model);
|
702 | }
|
703 | });
|
704 | }
|
705 | if (method === 'update') {
|
706 | return builder
|
707 | .where(data)
|
708 | .update(item)
|
709 | .then(function(numUpdated) {
|
710 | if (options && options.require === true && numUpdated === 0) {
|
711 | throw new Error('No rows were updated');
|
712 | }
|
713 | return numUpdated;
|
714 | });
|
715 | }
|
716 |
|
717 | return this.triggerThen('creating', this, data, options).then(function() {
|
718 | return builder.insert(data).then(function() {
|
719 | collection.add(item);
|
720 | });
|
721 | });
|
722 | }),
|
723 |
|
724 |
|
725 |
|
726 |
|
727 | _processModelPivot: Promise.method(function(method, item, options, data, fks) {
|
728 | const relatedData = this.relatedData,
|
729 | JoinModel = relatedData.throughTarget,
|
730 | joinModel = new JoinModel();
|
731 |
|
732 | fks = joinModel.parse(fks);
|
733 | data = joinModel.parse(data);
|
734 |
|
735 | if (method === 'insert') {
|
736 | return joinModel.set(data).save(null, options);
|
737 | }
|
738 |
|
739 | return joinModel
|
740 | .set(fks)
|
741 | .fetch({
|
742 | require: true
|
743 | })
|
744 | .then(function(instance) {
|
745 | if (method === 'delete') {
|
746 | return instance.destroy(options);
|
747 | }
|
748 | return instance.save(item, options);
|
749 | });
|
750 | })
|
751 | };
|
752 |
|
753 | module.exports = Relation;
|