UNPKG

58 kBJavaScriptView Raw
1'use strict';
2
3const _ = require('lodash');
4const createError = require('create-error');
5
6const Sync = require('./sync');
7const Helpers = require('./helpers');
8const EagerRelation = require('./eager');
9const Errors = require('./errors');
10
11const ModelBase = require('./base/model');
12const Promise = require('./base/promise');
13
14/**
15 * @class Model
16 * @extends ModelBase
17 * @inheritdoc
18 * @classdesc
19 *
20 * Models are simple objects representing individual database rows, specifying
21 * the tableName and any relations to other models. They can be extended with
22 * any domain-specific methods, which can handle components such as validations,
23 * computed properties, and access control.
24 *
25 * @constructor
26 * @description
27 *
28 * When creating an instance of a model, you can pass in the initial values of
29 * the attributes, which will be {@link Model#set set} on the
30 * model. If you define an {@link initialize} function, it will be invoked
31 * when the model is created.
32 *
33 * new Book({
34 * title: "One Thousand and One Nights",
35 * author: "Scheherazade"
36 * });
37 *
38 * In rare cases, if you're looking to get fancy, you may want to override
39 * {@link Model#constructor constructor}, which allows you to replace the
40 * actual constructor function for your model.
41 *
42 * let Book = bookshelf.Model.extend({
43 * tableName: 'documents',
44 * constructor: function() {
45 * bookshelf.Model.apply(this, arguments);
46 * this.on('saving', function(model, attrs, options) {
47 * options.query.where('type', '=', 'book');
48 * });
49 * }
50 * });
51 *
52 * @param {Object} attributes Initial values for this model's attributes.
53 * @param {Object=} options Hash of options.
54 * @param {string=} options.tableName Initial value for {@link Model#tableName tableName}.
55 * @param {Boolean=} [options.hasTimestamps=false]
56 *
57 * Initial value for {@link Model#hasTimestamps hasTimestamps}.
58 *
59 * @param {Boolean} [options.parse=false]
60 *
61 * Convert attributes by {@link Model#parse parse} before being {@link
62 * Model#set set} on the model.
63 *
64 */
65const BookshelfModel = ModelBase.extend(
66 {
67 /**
68 * The `hasOne` relation specifies that this table has exactly one of another
69 * type of object, specified by a foreign key in the other table.
70 *
71 * let Record = bookshelf.Model.extend({
72 * tableName: 'health_records'
73 * });
74 *
75 * let Patient = bookshelf.Model.extend({
76 * tableName: 'patients',
77 * record: function() {
78 * return this.hasOne(Record);
79 * }
80 * });
81 *
82 * // select * from `health_records` where `patient_id` = 1;
83 * new Patient({id: 1}).related('record').fetch().then(function(model) {
84 * // ...
85 * });
86 *
87 * // alternatively, if you don't need the relation loaded on the patient's relations hash:
88 * new Patient({id: 1}).record().fetch().then(function(model) {
89 * // ...
90 * });
91 *
92 * @method Model#hasOne
93 *
94 * @param {Model} Target
95 *
96 * Constructor of {@link Model} targeted by join.
97 *
98 * @param {string=} foreignKey
99 *
100 * ForeignKey in the `Target` model. By default, the `foreignKey` is assumed to
101 * be the singular form of this model's {@link Model#tableName tableName},
102 * followed by `_id` / `_{{{@link Model#idAttribute idAttribute}}}`.
103 *
104 * @param {string=} foreignKeyTarget
105 *
106 * Column in this model's table which `foreignKey` references, if other
107 * than this model's `id` / `{@link Model#idAttribute idAttribute}`.
108 *
109 * @returns {Model}
110 *
111 * The return value will always be a model, even if the relation doesn't exist, but in that
112 * case the relation will be `null` when {@link Model#serialize serializing} the model.
113 */
114 hasOne(Target, foreignKey, foreignKeyTarget) {
115 return this._relation('hasOne', Target, {
116 foreignKey,
117 foreignKeyTarget
118 }).init(this);
119 },
120
121 /**
122 * The `hasMany` relation specifies that this model has one or more rows in
123 * another table which match on this model's primary key.
124 *
125 * let Author = bookshelf.Model.extend({
126 * tableName: 'authors',
127 * books: function() {
128 * return this.hasMany(Book);
129 * }
130 * });
131 *
132 * // select * from `authors` where id = 1
133 * // select * from `books` where author_id = 1
134 * Author.where({id: 1}).fetch({withRelated: ['books']}).then(function(author) {
135 * console.log(JSON.stringify(author.related('books')));
136 * });
137 *
138 * @method Model#hasMany
139 *
140 * @param {Model} Target
141 *
142 * Constructor of {@link Model} targeted by join.
143 *
144 * @param {string=} foreignKey
145 *
146 * ForeignKey in the `Target` model. By default, the foreignKey is assumed to
147 * be the singular form of this model's tableName, followed by `_id` /
148 * `_{{{@link Model#idAttribute idAttribute}}}`.
149 *
150 * @param {string=} foreignKeyTarget
151 *
152 * Column in this model's table which `foreignKey` references, if other
153 * than this model's `id` / `{@link Model#idAttribute idAttribute}`.
154 *
155 * @returns {Collection}
156 */
157 hasMany(Target, foreignKey, foreignKeyTarget) {
158 return this._relation('hasMany', Target, {
159 foreignKey,
160 foreignKeyTarget
161 }).init(this);
162 },
163
164 /**
165 * The `belongsTo` relationship is used when a model is a member of
166 * another `Target` model.
167 *
168 * It can be used in a {@linkplain one-to-one} associations as the inverse
169 * of a {@link Model#hasOne hasOne}. It can also used in {@linkplain
170 * one-to-many} associations as the inverse of a {@link Model#hasMany hasMany}
171 * (and is the one side of that association). In both cases, the {@link
172 * Model#belongsTo belongsTo} relationship is used for a model that is a
173 * member of another Target model, referenced by the foreignKey in the current
174 * model.
175 *
176 * let Book = bookshelf.Model.extend({
177 * tableName: 'books',
178 * author: function() {
179 * return this.belongsTo(Author);
180 * }
181 * });
182 *
183 * // select * from `books` where id = 1
184 * // select * from `authors` where id = book.author_id
185 * Book.where({id: 1}).fetch({withRelated: ['author']}).then(function(book) {
186 * console.log(JSON.stringify(book.related('author')));
187 * });
188 *
189 * @method Model#belongsTo
190 *
191 * @param {Model} Target
192 *
193 * Constructor of {@link Model} targeted by join.
194 *
195 * @param {string=} foreignKey
196 *
197 * ForeignKey in this model. By default, the foreignKey is assumed to
198 * be the singular form of the `Target` model's tableName, followed by `_id` /
199 * `_{{{@link Model#idAttribute idAttribute}}}`.
200 *
201 * @param {string=} foreignKeyTarget
202 *
203 * Column in the `Target` model's table which `foreignKey` references, if other
204 * than `Target` model's `id` / `{@link Model#idAttribute idAttribute}`.
205 *
206 * @returns {Model}
207 *
208 * The return value will always be a model, even if the relation doesn't exist, but in that
209 * case the relation will be `null` when {@link Model#serialize serializing} the model.
210 */
211 belongsTo(Target, foreignKey, foreignKeyTarget) {
212 return this._relation('belongsTo', Target, {
213 foreignKey,
214 foreignKeyTarget
215 }).init(this);
216 },
217
218 /**
219 * Defines a many-to-many relation, where the current model is joined to one
220 * or more of a `Target` model through another table. The default name for
221 * the joining table is the two table names, joined by an underscore, ordered
222 * alphabetically. For example, a `users` table and an `accounts` table would have
223 * a joining table of accounts_users.
224 *
225 * let Account = bookshelf.Model.extend({
226 * tableName: 'accounts'
227 * });
228 *
229 * let User = bookshelf.Model.extend({
230 * tableName: 'users',
231 * allAccounts: function () {
232 * return this.belongsToMany(Account);
233 * },
234 * adminAccounts: function() {
235 * return this.belongsToMany(Account).query({where: {access: 'admin'}});
236 * },
237 * viewAccounts: function() {
238 * return this.belongsToMany(Account).query({where: {access: 'readonly'}});
239 * }
240 * });
241 *
242 * The default key names in the joining table are the singular versions of the
243 * model table names, followed by `_id` /
244 * _{{{@link Model#idAttribute idAttribute}}}. So in the above case, the
245 * columns in the joining table
246 * would be `user_id`, `account_id`, and `access`, which is used as an
247 * example of how dynamic relations can be formed using different contexts.
248 * To customize the keys used in, or the {@link Model#tableName tableName}
249 * used for the join table, you may specify them like so:
250 *
251 * this.belongsToMany(Account, 'users_accounts', 'userid', 'accountid');
252 *
253 * If you wish to create a {@link Model#belongsToMany belongsToMany}
254 * association where the joining table has a primary key, and more information
255 * about the model, you may create a {@link Model#belongsToMany belongsToMany}
256 * {@link Relation#through through} relation:
257 *
258 * let Doctor = bookshelf.Model.extend({
259 * patients: function() {
260 * return this.belongsToMany(Patient).through(Appointment);
261 * }
262 * });
263 *
264 * let Appointment = bookshelf.Model.extend({
265 * patient: function() {
266 * return this.belongsTo(Patient);
267 * },
268 * doctor: function() {
269 * return this.belongsTo(Doctor);
270 * }
271 * });
272 *
273 * let Patient = bookshelf.Model.extend({
274 * doctors: function() {
275 * return this.belongsToMany(Doctor).through(Appointment);
276 * }
277 * });
278 *
279 * Collections returned by a `belongsToMany` relation are decorated with
280 * several pivot helper methods. See {@link Collection#attach attach},
281 * {@link Collection#detach detach}, {@link Collection#updatePivot
282 * updatePivot} and {@link Collection#withPivot withPivot} for more
283 * information.
284 *
285 * @belongsTo Model
286 * @method Model#belongsToMany
287 * @param {Model} Target
288 *
289 * Constructor of {@link Model} targeted by join.
290 *
291 * @param {string=} table
292 *
293 * Name of the joining table. Defaults to the two table names, joined by an
294 * underscore, ordered alphabetically.
295 *
296 * @param {string=} foreignKey
297 *
298 * Foreign key in this model. By default, the `foreignKey` is assumed to
299 * be the singular form of this model's tableName, followed by `_id` /
300 * `_{{{@link Model#idAttribute idAttribute}}}`.
301 *
302 * @param {string=} otherKey
303 *
304 * Foreign key in the `Target` model. By default, the `otherKey` is assumed to
305 * be the singular form of the `Target` model's tableName, followed by `_id` /
306 * `_{{{@link Model#idAttribute idAttribute}}}`.
307 *
308 * @param {string=} foreignKeyTarget
309 *
310 * Column in this model's table which `foreignKey` references, if other
311 * than `id` / `{@link Model#idAttribute idAttribute}`.
312 *
313 * @param {string=} otherKeyTarget
314 *
315 * Column in the `Target` model's table which `otherKey` references, if other
316 * than `Target` model's `id` / `{@link Model#idAttribute idAttribute}`.
317 *
318 * @returns {Collection}
319 */
320 belongsToMany(Target, joinTableName, foreignKey, otherKey, foreignKeyTarget, otherKeyTarget) {
321 return this._relation('belongsToMany', Target, {
322 joinTableName,
323 foreignKey,
324 otherKey,
325 foreignKeyTarget,
326 otherKeyTarget
327 }).init(this);
328 },
329
330 /**
331 * The {@link Model#morphOne morphOne} is used to signify a {@link oneToOne
332 * one-to-one} {@link polymorphicRelation polymorphic relation} with
333 * another `Target` model, where the `name` of the model is used to determine
334 * which database table keys are used. The naming convention requires the
335 * `name` prefix an `_id` and `_type` field in the database. So for the case
336 * below the table names would be `imageable_type` and `imageable_id`. The
337 * `morphValue` may be optionally set to store/retrieve a different value in
338 * the `_type` column than the {@link Model#tableName}.
339 *
340 * let Site = bookshelf.Model.extend({
341 * tableName: 'sites',
342 * photo: function() {
343 * return this.morphOne(Photo, 'imageable');
344 * }
345 * });
346 *
347 * And with custom `columnNames`:
348 *
349 * let Site = bookshelf.Model.extend({
350 * tableName: 'sites',
351 * photo: function() {
352 * return this.morphOne(Photo, 'imageable', ["ImageableType", "ImageableId"]);
353 * }
354 * });
355 *
356 * Note that both `columnNames` and `morphValue` are optional arguments. How
357 * your argument is treated when only one is specified, depends on the type.
358 * If your argument is an array, it will be assumed to contain custom
359 * `columnNames`. If it's not, it will be assumed to indicate a `morphValue`.
360 *
361 * @method Model#morphOne
362 *
363 * @param {Model} Target Constructor of {@link Model} targeted by join.
364 * @param {string=} name Prefix for `_id` and `_type` columns.
365 * @param {(string[])=} columnNames
366 *
367 * Array containing two column names, the first is the `_type`, the second
368 * is the `_id`.
369 *
370 * @param {string=} [morphValue=Target#{@link Model#tableName tableName}]
371 *
372 * The string value associated with this relationship. Stored in the `_type`
373 * column of the polymorphic table. Defaults to `Target#{@link
374 * Model#tableName tableName}`.
375 *
376 * @returns {Model} The related model.
377 */
378 morphOne(Target, name, columnNames, morphValue) {
379 return this._morphOneOrMany(Target, name, columnNames, morphValue, 'morphOne');
380 },
381
382 /**
383 * {@link Model#morphMany morphMany} is essentially the same as a {@link
384 * Model#morphOne morphOne}, but creating a {@link Collection collection}
385 * rather than a {@link Model model} (similar to a {@link Model#hasOne
386 * hasOne} vs. {@link Model#hasMany hasMany} relation).
387 *
388 * {@link Model#morphMany morphMany} is used to signify a {@link oneToMany
389 * one-to-many} or {@link manyToMany many-to-many} {@link polymorphicRelation
390 * polymorphic relation} with another `Target` model, where the `name` of the
391 * model is used to determine which database table keys are used. The naming
392 * convention requires the `name` prefix an `_id` and `_type` field in the
393 * database. So for the case below the table names would be `imageable_type`
394 * and `imageable_id`. The `morphValue` may be optionally set to
395 * store/retrieve a different value in the `_type` column than the `Target`'s
396 * {@link Model#tableName tableName}.
397 *
398 * let Post = bookshelf.Model.extend({
399 * tableName: 'posts',
400 * photos: function() {
401 * return this.morphMany(Photo, 'imageable');
402 * }
403 * });
404 *
405 * And with custom columnNames:
406 *
407 * let Post = bookshelf.Model.extend({
408 * tableName: 'posts',
409 * photos: function() {
410 * return this.morphMany(Photo, 'imageable', ["ImageableType", "ImageableId"]);
411 * }
412 * });
413 *
414 * @method Model#morphMany
415 *
416 * @param {Model} Target Constructor of {@link Model} targeted by join.
417 * @param {string=} name Prefix for `_id` and `_type` columns.
418 * @param {(string[])=} columnNames
419 *
420 * Array containing two column names, the first is the `_type`, the second is the `_id`.
421 *
422 * @param {string=} [morphValue=Target#{@link Model#tableName tablename}]
423 *
424 * The string value associated with this relationship. Stored in the `_type`
425 * column of the polymorphic table. Defaults to `Target`#{@link Model#tableName
426 * tablename}.
427 *
428 * @returns {Collection} A collection of related models.
429 */
430 morphMany(Target, name, columnNames, morphValue) {
431 return this._morphOneOrMany(Target, name, columnNames, morphValue, 'morphMany');
432 },
433
434 /**
435 * The {@link Model#morphTo morphTo} relation is used to specify the inverse
436 * of the {@link Model#morphOne morphOne} or {@link Model#morphMany
437 * morphMany} relations, where the `targets` must be passed to signify which
438 * {@link Model models} are the potential opposite end of the {@link
439 * polymorphicRelation polymorphic relation}.
440 *
441 * let Photo = bookshelf.Model.extend({
442 * tableName: 'photos',
443 * imageable: function() {
444 * return this.morphTo('imageable', Site, Post);
445 * }
446 * });
447 *
448 * And with custom columnNames:
449 *
450 * let Photo = bookshelf.Model.extend({
451 * tableName: 'photos',
452 * imageable: function() {
453 * return this.morphTo('imageable', ["ImageableType", "ImageableId"], Site, Post);
454 * }
455 * });
456 *
457 * And with custom morphValues, the inverse of morphValue of
458 * {@link Model#morphOne morphOne} and {@link Model#morphMany morphMany},
459 * where the `morphValues` may be optionally set to check against a
460 * different value in the `_type` column than the {@link Model#tableName};
461 * for example, a more descriptive name, or a name that betters adheres to
462 * whatever standard you are using for models.
463 *
464 * let Photo = bookshelf.Model.extend({
465 * tableName: 'photos',
466 * imageable: function() {
467 * return this.morphTo('imageable', [Site, "favicon"], [Post, "cover_photo"]);
468 * }
469 * });
470 *
471 * @method Model#morphTo
472 *
473 * @param {string} name Prefix for `_id` and `_type` columns.
474 * @param {(string[])=} columnNames
475 *
476 * Array containing two column names, the first is the `_type`, the second is the `_id`.
477 *
478 * @param {...Model} Target Constructor of {@link Model} targeted by join.
479 *
480 * @returns {Model}
481 */
482 morphTo(morphName) {
483 if (!_.isString(morphName)) throw new Error('The `morphTo` name must be specified.');
484 let columnNames, candidates;
485 if (arguments[1] == null || (Array.isArray(arguments[1]) && _.isString(arguments[1][0]))) {
486 columnNames = arguments[1] || null; // may be `null` or `undefined`
487 candidates = _.drop(arguments, 2);
488 } else {
489 columnNames = null;
490 candidates = _.drop(arguments, 1);
491 }
492
493 candidates = _.map(candidates, (target) => {
494 if (Array.isArray(target)) return target;
495
496 // Set up the morphValue by default as the tableName
497 return [target, _.result(target.prototype, 'tableName')];
498 });
499
500 return this._relation('morphTo', null, {
501 morphName,
502 columnNames,
503 candidates
504 }).init(this);
505 },
506
507 /**
508 * Helps to create dynamic relations between {@link Model models} and {@link
509 * Collection collections}, where a {@link Model#hasOne hasOne}, {@link
510 * Model#hasMany hasMany}, {@link Model#belongsTo belongsTo}, or {@link
511 * Model#belongsToMany belongsToMany} relation may run through a `JoinModel`.
512 *
513 * A good example of where this would be useful is if a book {@link
514 * Model#hasMany hasMany} paragraphs through chapters. Consider the following examples:
515 *
516 *
517 * let Book = bookshelf.Model.extend({
518 * tableName: 'books',
519 *
520 * // Find all paragraphs associated with this book, by
521 * // passing through the "Chapter" model.
522 * paragraphs: function() {
523 * return this.hasMany(Paragraph).through(Chapter);
524 * },
525 *
526 * chapters: function() {
527 * return this.hasMany(Chapter);
528 * }
529 * });
530 *
531 * let Chapter = bookshelf.Model.extend({
532 * tableName: 'chapters',
533 *
534 * paragraphs: function() {
535 * return this.hasMany(Paragraph);
536 * }
537 * });
538 *
539 * let Paragraph = bookshelf.Model.extend({
540 * tableName: 'paragraphs',
541 *
542 * chapter: function() {
543 * return this.belongsTo(Chapter);
544 * },
545 *
546 * // A reverse relation, where we can get the book from the chapter.
547 * book: function() {
548 * return this.belongsTo(Book).through(Chapter);
549 * }
550 * });
551 *
552 * The "through" table creates a pivot model, which it assigns to {@link
553 * Model#pivot model.pivot} after it is created. On {@link Model#toJSON
554 * toJSON}, the pivot model is flattened to values prefixed with
555 * `_pivot_`.
556 *
557 * @method Model#through
558 * @param {Model} Interim Pivot model.
559 * @param {string=} throughForeignKey
560 *
561 * Foreign key in this model. By default, the `foreignKey` is assumed to
562 * be the singular form of the `Target` model's tableName, followed by `_id` /
563 * `_{{{@link Model#idAttribute idAttribute}}}`.
564 *
565 * @param {string=} otherKey
566 *
567 * Foreign key in the `Interim` model. By default, the `otherKey` is assumed to
568 * be the singular form of this model's tableName, followed by `_id` /
569 * `_{{{@link Model#idAttribute idAttribute}}}`.
570 *
571 * @param {string=} throughForeignKeyTarget
572 *
573 * Column in the `Target` model which `throughForeignKey` references, if other
574 * than `Target` model's `id` / `{@link Model#idAttribute idAttribute}`.
575 *
576 * @param {string=} otherKeyTarget
577 *
578 * Column in this model which `otherKey` references, if other
579 * than `id` / `{@link Model#idAttribute idAttribute}`.
580 *
581 * @returns {Collection}
582 */
583 through(Interim, throughForeignKey, otherKey, throughForeignKeyTarget, otherKeyTarget) {
584 return this.relatedData.through(this, Interim, {
585 throughForeignKey,
586 otherKey,
587 throughForeignKeyTarget,
588 otherKeyTarget
589 });
590 },
591
592 /**
593 * @method Model#refresh
594 * @since 0.8.2
595 * @description
596 *
597 * Update the attributes of a model, fetching it by its primary key. If no
598 * attribute matches its {@link Model#idAttribute idAttribute}, then fetch by
599 * all available fields.
600 *
601 * @param {Object} options
602 * A hash of options. See {@link Model#fetch} for details.
603 * @returns {Promise<Model>}
604 * A promise resolving to this model.
605 */
606 refresh(options) {
607 // If this is new, we use all its attributes. Otherwise we just grab the primary key.
608 const attributes = this.isNew() ? this.attributes : _.pick(this.attributes, this.idAttribute);
609 return this._doFetch(attributes, options);
610 },
611
612 /**
613 * Fetches a {@link Model model} from the database, using any {@link
614 * Model#attributes attributes} currently set on the model to form a `select`
615 * query.
616 *
617 * A {@link Model#event:fetching "fetching"} event will be fired just before the
618 * record is fetched; a good place to hook into for validation. {@link
619 * Model#event:fetched "fetched"} event will be fired when a record is
620 * successfully retrieved.
621 *
622 * If you need to constrain the query
623 * performed by fetch, you can call {@link Model#query query} before calling
624 * {@link Model#fetch fetch}.
625 *
626 * // select * from `books` where `ISBN-13` = '9780440180296'
627 * new Book({'ISBN-13': '9780440180296'})
628 * .fetch()
629 * .then(function(model) {
630 * // outputs 'Slaughterhouse Five'
631 * console.log(model.get('title'));
632 * });
633 *
634 * _If you'd like to only fetch specific columns, you may specify a `columns`
635 * property in the `options` for the {@link Model#fetch fetch} call, or use
636 * {@link Model#query query}, tapping into the {@link Knex} {@link
637 * Knex#column column} method to specify which columns will be fetched._
638 *
639 * A single property, or an array of properties can be specified as a value for
640 * the `withRelated` property. You can also execute callbacks on relations
641 * queries (eg. for sorting a relation). The results of these relation queries
642 * will be loaded into a {@link Model#relations relations} property on the
643 * model, may be retrieved with the {@link Model#related related} method, and
644 * will be serialized as properties on a {@link Model#toJSON toJSON} call
645 * unless `{shallow: true}` is passed.
646 *
647 * let Book = bookshelf.Model.extend({
648 * tableName: 'books',
649 * editions: function() {
650 * return this.hasMany(Edition);
651 * },
652 * chapters: function() {
653 * return this.hasMany(Chapter);
654 * },
655 * genre: function() {
656 * return this.belongsTo(Genre);
657 * }
658 * })
659 *
660 * new Book({'ISBN-13': '9780440180296'}).fetch({
661 * withRelated: [
662 * 'genre', 'editions',
663 * { chapters: function(query) { query.orderBy('chapter_number'); }}
664 * ]
665 * }).then(function(book) {
666 * console.log(book.related('genre').toJSON());
667 * console.log(book.related('editions').toJSON());
668 * console.log(book.toJSON());
669 * });
670 *
671 * @method Model#fetch
672 *
673 * @param {Object=} options - Hash of options.
674 * @param {Boolean=} [options.require=false]
675 * Reject the returned response with a {@link Model.NotFoundError
676 * NotFoundError} if results are empty.
677 * @param {string|string[]} [options.columns='*']
678 * Specify columns to be retrieved.
679 * @param {Transaction} [options.transacting]
680 * Optionally run the query in a transaction.
681 * @param {string} [options.lock]
682 * Type of row-level lock to use. Valid options are `forShare` and
683 * `forUpdate`. This only works in conjunction with the `transacting`
684 * option, and requires a database that supports it.
685 * @param {string|Object|mixed[]} [options.withRelated]
686 * Relations to be retrieved with `Model` instance. Either one or more
687 * relation names or objects mapping relation names to query callbacks.
688 *
689 * @fires Model#fetching
690 * @fires Model#fetched
691 *
692 * @throws {Model.NotFoundError}
693 *
694 * @returns {Promise<Model|null>}
695 * A promise resolving to the fetched {@link Model model} or `null` if
696 * none exists.
697 *
698 */
699 fetch(options) {
700 return this._doFetch(this.attributes, options);
701 },
702
703 _doFetch: Promise.method(function(attributes, options) {
704 options = options ? _.clone(options) : {};
705
706 // Run the `first` call on the `sync` object to fetch a single model.
707 return (
708 this.sync(options)
709 .first(attributes)
710 .bind(this)
711
712 // Jump the rest of the chain if the response doesn't exist...
713 .tap(function(response) {
714 if (!response || response.length === 0) {
715 throw new this.constructor.NotFoundError('EmptyResponse');
716 }
717 })
718
719 // Now, load all of the data into the model as necessary.
720 .tap(this._handleResponse)
721
722 // If the "withRelated" is specified, we also need to eager load all of the
723 // data on the model, as a side-effect, before we ultimately jump into the
724 // next step of the model. Since the `columns` are only relevant to the
725 // current level, ensure those are omitted from the options.
726 .tap(function(response) {
727 if (options.withRelated) {
728 return this._handleEager(response, _.omit(options, 'columns'));
729 }
730 })
731
732 .tap(function(response) {
733 /**
734 * Fired after a `fetch` operation. A promise may be returned from the
735 * event handler for async behaviour.
736 *
737 * @event Model#fetched
738 * @tutorial events
739 * @param {Model} model
740 * The model firing the event.
741 * @param {Object} response
742 * Knex query response.
743 * @param {Object} options
744 * Options object passed to {@link Model#fetch fetch}.
745 * @returns {Promise}
746 * If the handler returns a promise, `fetch` will wait for it to
747 * be resolved.
748 */
749 return this.triggerThen('fetched', this, response, options);
750 })
751 .return(this)
752 .catch(this.constructor.NotFoundError, function(err) {
753 if (options.require) {
754 throw err;
755 }
756 return null;
757 })
758 );
759 }),
760
761 // Private for now.
762 all() {
763 const collection = this.constructor.collection();
764 collection._knex = this.query().clone();
765 this.resetQuery();
766 if (this.relatedData) collection.relatedData = this.relatedData;
767 return collection;
768 },
769
770 /**
771 * @method Model#count
772 * @since 0.8.2
773 * @description
774 *
775 * Gets the number of matching records in the database, respecting any
776 * previous calls to {@link Model#query}.
777 *
778 * @example
779 *
780 * Duck.where('color', 'blue').count('name')
781 * .then(function(count) { //...
782 *
783 * @param {string} [column='*']
784 * Specify a column to count - rows with null values in this column will be excluded.
785 * @param {Object=} options
786 * Hash of options.
787 * @returns {Promise<Number>}
788 * A promise resolving to the number of matching rows.
789 */
790 count(column, options) {
791 return this.all().count(column, options);
792 },
793
794 /**
795 * Fetches a collection of {@link Model models} from the database, using any
796 * query parameters currently set on the model to form a select query. Returns
797 * a promise, which will resolve with the fetched collection. If you wish to
798 * trigger an error if no models are found, pass {require: true} as one of
799 * the options to the `fetchAll` call.
800 *
801 * If you need to constrain the query performed by fetch, you can call the
802 * {@link Model#query query} method before calling fetch.
803 *
804 * @method Model#fetchAll
805 *
806 * @param {Object=} options - Hash of options.
807 * @param {Boolean=} [options.require=false]
808 *
809 * Rejects the returned promise with an `Collection.EmptyError` if no records are returned.
810 *
811 * @param {Transaction=} options.transacting
812 *
813 * Optionally run the query in a transaction.
814 *
815 * @fires Model#fetching:collection
816 * @fires Model#fetched:collection
817 *
818 * @throws {Collection.EmptyError}
819 *
820 * Rejects the promise in the event of an empty response if the `require: true` option.
821 *
822 * @returns {Promise<Collection>} A promise resolving to the fetched {@link Collection collection}.
823 *
824 */
825 fetchAll(options) {
826 const collection = this.all();
827 return collection
828 .once('fetching', (__, columns, opts) => {
829 /**
830 * Fired before a {@link Model#fetchAll fetchAll} operation. A promise
831 * may be returned from the event handler for async behaviour.
832 *
833 * @event Model#fetching:collection
834 * @tutorial events
835 * @param {Collection} collection
836 * The collection that is going to be fetched. At this point it's still empty since the
837 * fetch hasn't happened yet.
838 * @param {string[]} columns
839 * The columns to be retrieved by the query as provided by the underlying query builder.
840 * If the `columns` option is not specified the value of this will usually be an array
841 * with a single string `'tableName.*'`.
842 * @param {Object} options Options object passed to {@link Model#fetchAll fetchAll}.
843 * @returns {Promise}
844 */
845 return this.triggerThen('fetching:collection', collection, columns, opts);
846 })
847 .once('fetched', (__, response, opts) => {
848 /**
849 * Fired after a {@link Model#fetchAll fetchAll} operation. A promise
850 * may be returned from the event handler for async behaviour.
851 *
852 * @event Model#fetched:collection
853 * @tutorial events
854 * @param {Collection} collection The collection that has been fetched.
855 * @param {Object} response
856 * The raw response from the underlying query builder. This will be an array with objects
857 * representing each row, similar to the output of a
858 * {@link Model#serialize serialized Model}.
859 * @param {Object} options Options object passed to {@link Model#fetchAll fetchAll}.
860 * @returns {Promise}
861 */
862 return this.triggerThen('fetched:collection', collection, response, opts);
863 })
864 .fetch(options);
865 },
866
867 /**
868 * @method Model#load
869 * @description
870 * The load method takes an array of relations to eager load attributes onto a
871 * {@link Model}, in a similar way that the `withRelated` property works on
872 * {@link Model#fetch fetch}. Dot separated attributes may be used to specify deep
873 * eager loading.
874 *
875 * @example
876 * new Posts().fetch().then(function(collection) {
877 * collection.at(0)
878 * .load(['author', 'content', 'comments.tags'])
879 * .then(function(model) {
880 * JSON.stringify(model);
881 * });
882 * });
883 *
884 * {
885 * title: 'post title',
886 * author: {...},
887 * content: {...},
888 * comments: [
889 * {tags: [...]}, {tags: [...]}
890 * ]
891 * }
892 *
893 * @param {string|Object|mixed[]} relations The relation, or relations, to be loaded.
894 * @param {Object=} options Hash of options.
895 * @param {Transaction=} options.transacting
896 * Optionally run the query in a transaction.
897 * @param {string=} options.lock
898 * Type of row-level lock to use. Valid options are `forShare` and
899 * `forUpdate`. This only works in conjunction with the `transacting`
900 * option, and requires a database that supports it.
901 * @returns {Promise<Model>} A promise resolving to this {@link Model model}.
902 */
903 load: Promise.method(function(relations, options) {
904 const columns = this.format(_.assignIn({}, this.attributes));
905 const withRelated = Array.isArray(relations) ? relations : [relations];
906 return this._handleEager([columns], _.assignIn({}, options, {shallow: true, withRelated})).return(this);
907 }),
908
909 /**
910 * @method Model#save
911 * @description
912 *
913 * This method is used to perform either an insert or update query using the
914 * model's set {@link Model#attributes attributes}.
915 *
916 * If the model {@link Model#isNew isNew}, any {@link Model#defaults defaults}
917 * will be set and an `insert` query will be performed. Otherwise it will
918 * `update` the record with a corresponding ID. It is also possible to
919 * set default attributes on an `update` by passing the `{defaults: true}`
920 * option in the second argument to the `save` call. This will also use the
921 * same {@link Model#defaults defaults} as the `insert` operation.
922 *
923 * The type of operation to perform (either `insert` or `update`) can be
924 * overriden with the `method` option:
925 *
926 * // This forces an insert with the specified id instead of the expected
927 * // update
928 * new Post({name: 'New Article', id: 34})
929 * .save(null, {method: 'insert'})
930 * .then(function(model) {
931 * // ...
932 * });
933 *
934 * If you only wish to update with the params passed to the save, you may pass
935 * a `{patch: true}` option in the second argument to `save`:
936 *
937 * // UPDATE authors SET "bio" = 'Short user bio' WHERE "id" = 1
938 * new Author({id: 1, first_name: 'User'})
939 * .save({bio: 'Short user bio'}, {patch: true})
940 * .then(function(model) {
941 * // ...
942 * });
943 *
944 * Several events fire on the model when saving: a {@link Model#event:creating
945 * "creating"}, or {@link Model#event:updating "updating"} event if the model is
946 * being inserted or updated, and a "saving" event in either case.
947 *
948 * To prevent saving the model (for example, with validation), throwing an error
949 * inside one of these event listeners will stop saving the model and reject the
950 * promise.
951 *
952 * A {@link Model#event:created "created"}, or {@link Model#event:updated "updated"}
953 * event is fired after the model is saved, as well as a {@link Model#event:saved "saved"}
954 * event either way. If you wish to modify the query when the {@link Model#event:saving
955 * "saving"} event is fired, the knex query object is available in `options.query`.
956 *
957 * See the {@tutorial events} guide for further details.
958 *
959 * @example
960 * // Save with no arguments
961 * Model.forge({id: 5, firstName: 'John', lastName: 'Smith'}).save().then(function() {
962 * //...
963 * });
964 *
965 * // Or add attributes during save
966 * Model.forge({id: 5}).save({firstName: 'John', lastName: 'Smith'}).then(function() {
967 * //...
968 * });
969 *
970 * // Or, if you prefer, for a single attribute
971 * Model.forge({id: 5}).save('name', 'John Smith').then(function() {
972 * //...
973 * });
974 *
975 * @param {string=} key Attribute name.
976 * @param {string=} val Attribute value.
977 * @param {Object=} attrs A hash of attributes.
978 * @param {Object=} options
979 * @param {Transaction=} options.transacting
980 * Optionally run the query in a transaction.
981 * @param {string=} options.method
982 * Explicitly select a save method, either `"update"` or `"insert"`.
983 * @param {Boolean} [options.defaults=false]
984 * Whether to assign or not {@link Model#defaults default} attribute values
985 * on a model when performing an update or create operation.
986 * @param {Boolean} [options.patch=false]
987 * Only save attributes supplied in arguments to `save`.
988 * @param {Boolean} [options.require=true]
989 * Throw a {@link Model.NoRowsUpdatedError} if no records are affected by save.
990 *
991 * @fires Model#saving
992 * @fires Model#creating
993 * @fires Model#updating
994 * @fires Model#created
995 * @fires Model#updated
996 * @fires Model#saved
997 *
998 * @throws {Model.NoRowsUpdatedError}
999 *
1000 * @returns {Promise<Model>} A promise resolving to the saved and updated model.
1001 */
1002 save: Promise.method(function(key, val, options) {
1003 let attrs;
1004
1005 // Handle both `"key", value` and `{key: value}` -style arguments.
1006 if (key == null || typeof key === 'object') {
1007 attrs = key || {};
1008 options = _.clone(val) || {};
1009 } else {
1010 attrs = {
1011 [key]: val
1012 };
1013 options = options ? _.clone(options) : {};
1014 }
1015
1016 return Promise.bind(this)
1017 .then(function() {
1018 return this.saveMethod(options);
1019 })
1020 .then(function(method) {
1021 // Determine which kind of save we will do: update or insert.
1022 options.method = method;
1023
1024 // If the object is being created, we merge any defaults here rather than
1025 // during object creation.
1026 if (method === 'insert' || options.defaults) {
1027 const defaults = _.result(this, 'defaults');
1028 if (defaults) {
1029 attrs = _.defaultsDeep({}, attrs, this.attributes, defaults);
1030 }
1031 }
1032
1033 // Set the attributes on the model. Note that we do this before adding
1034 // timestamps, as `timestamp` calls `set` internally.
1035 this.set(attrs, {silent: true});
1036
1037 // Now set timestamps if appropriate. Extend `attrs` so that the
1038 // timestamps will be provided for a patch operation.
1039 if (this.hasTimestamps) {
1040 Object.assign(attrs, this.timestamp(options));
1041 }
1042
1043 // If there are any save constraints, set them on the model.
1044 if (this.relatedData && this.relatedData.type !== 'morphTo') {
1045 Helpers.saveConstraints(this, this.relatedData);
1046 }
1047
1048 // Gives access to the `query` object in the `options`, in case we need it
1049 // in any event handlers.
1050 const sync = this.sync(options);
1051 options.query = sync.query;
1052
1053 /**
1054 * Saving event.
1055 *
1056 * Fired before an `insert` or `update` query. A promise may be
1057 * returned from the event handler for async behaviour. Throwing an
1058 * exception from the handler will cancel the save.
1059 *
1060 * @event Model#saving
1061 * @tutorial events
1062 * @param {Model} model
1063 * The model firing the event. Its attributes are already changed but
1064 * not commited to the database yet.
1065 * @param {Object} attrs
1066 * Attributes that will be inserted or updated.
1067 *
1068 * **Note**: There's currently a bug that leads to attrs only
1069 * containing attributes that were passed as argument to
1070 * {@link Model#save save}. You can work around this by accessing
1071 * `model.changed` which does contain all the attributes that will be
1072 * inserted or updated.
1073 * @param {Object} options Options object passed to {@link Model#save save}.
1074 * @returns {Promise}
1075 */
1076
1077 /**
1078 * Creating event.
1079 *
1080 * Fired before `insert` query. A promise may be
1081 * returned from the event handler for async behaviour. Throwing an
1082 * exception from the handler will cancel the save operation.
1083 *
1084 * @event Model#creating
1085 * @tutorial events
1086 * @param {Model} model The model firing the event.
1087 * @param {Object} attrs
1088 * Attributes that will be inserted.
1089 *
1090 * **Note**: There's currently a bug that leads to attrs only
1091 * containing attributes that were passed as argument to
1092 * {@link Model#save save}. You can work around this by accessing
1093 * `model.changed` which does contain all the attributes that will be
1094 * inserted.
1095 * @param {Object} options Options object passed to {@link Model#save save}.
1096 * @returns {Promise}
1097 */
1098
1099 /**
1100 * Updating event.
1101 *
1102 * Fired before `update` query. A promise may be
1103 * returned from the event handler for async behaviour. Throwing an
1104 * exception from the handler will cancel the save operation.
1105 *
1106 * @event Model#updating
1107 * @tutorial events
1108 * @param {Model} model
1109 * The model firing the event. Its attributes are already changed but
1110 * not commited to the database yet.
1111 * @param {Object} attrs
1112 * Attributes that will be updated.
1113 *
1114 * **Note**: There's currently a bug that leads to attrs only
1115 * containing attributes that were passed as argument to
1116 * {@link Model#save save}. You can work around this by accessing
1117 * `model.changed` which does contain all the attributes that will be
1118 * updated.
1119 * @param {Object} options Options object passed to {@link Model#save save}.
1120 * @returns {Promise}
1121 */
1122 return this.triggerThen(method === 'insert' ? 'saving creating' : 'saving updating', this, attrs, options)
1123 .bind(this)
1124 .then(function() {
1125 return sync[options.method](method === 'update' && options.patch ? attrs : this.attributes);
1126 })
1127 .then(function(resp) {
1128 // After a successful database save, the id is updated if the model was created
1129 if (method === 'insert' && this.id == null) {
1130 const updatedCols = {};
1131 updatedCols[this.idAttribute] = this.id = resp[0];
1132 const updatedAttrs = this.parse(updatedCols);
1133 Object.assign(this.attributes, updatedAttrs);
1134 } else if (method === 'update' && resp === 0) {
1135 if (options.require !== false) {
1136 throw new this.constructor.NoRowsUpdatedError('No Rows Updated');
1137 }
1138 }
1139
1140 this._reset();
1141
1142 /**
1143 * Saved event.
1144 *
1145 * Fired after an `insert` or `update` query.
1146 *
1147 * @event Model#saved
1148 * @tutorial events
1149 * @param {Model} model The model firing the event.
1150 * @param {(Array|Number)} response
1151 * A list containing the id of the newly created model in case of an
1152 * `insert` or a number representing the affected rows in the case of
1153 * an `update` query.
1154 * @param {Object} options Options object passed to {@link Model#save save}.
1155 * @returns {Promise}
1156 */
1157
1158 /**
1159 * Created event.
1160 *
1161 * Fired after an `insert` query.
1162 *
1163 * @event Model#created
1164 * @tutorial events
1165 * @param {Model} model The model firing the event.
1166 * @param {Array} newId A list containing the id of the newly created model.
1167 * @param {Object} options Options object passed to {@link Model#save save}.
1168 * @returns {Promise}
1169 */
1170
1171 /**
1172 * Updated event.
1173 *
1174 * Fired after an `update` query.
1175 *
1176 * @event Model#updated
1177 * @tutorial events
1178 * @param {Model} model The model firing the event.
1179 * @param {Number} affectedRows Number of rows affected by the update.
1180 * @param {Object} options Options object passed to {@link Model#save save}.
1181 * @returns {Promise}
1182 */
1183 return this.triggerThen(method === 'insert' ? 'created saved' : 'updated saved', this, resp, options);
1184 });
1185 })
1186 .return(this);
1187 }),
1188
1189 /**
1190 * `destroy` performs a `delete` on the model, using the model's {@link
1191 * Model#idAttribute idAttribute} to constrain the query.
1192 *
1193 * A {@link Model#event:destroying "destroying"} event is triggered on the model
1194 * before being destroyed. To prevent destroying the model, throwing an error
1195 * inside one of the event listeners will stop destroying the model and reject the
1196 * promise.
1197 *
1198 * A {@link Model#event:destroyed "destroyed"} event is fired after the model's
1199 * removal is completed.
1200 *
1201 * @method Model#destroy
1202 *
1203 * @param {Object=} options Hash of options.
1204 * @param {Transaction=} options.transacting Optionally run the query in a transaction.
1205 * @param {Boolean} [options.require=true]
1206 * Throw a {@link Model.NoRowsDeletedError} if no records are affected by destroy. This is
1207 * the default behavior as of version 0.13.0.
1208 *
1209 * @example
1210 *
1211 * new User({id: 1})
1212 * .destroy()
1213 * .then(function(model) {
1214 * // ...
1215 * });
1216 *
1217 * @fires Model#destroying
1218 * @fires Model#destroyed
1219 *
1220 * @throws {Model.NoRowsDeletedError}
1221 *
1222 * @returns {Promise<Model>} A promise resolving to the destroyed and thus
1223 * empty model, i.e. all attributes are `undefined`.
1224 */
1225 destroy: Promise.method(function(options) {
1226 options = options ? _.clone(options) : {};
1227 const sync = this.sync(options);
1228 options.query = sync.query;
1229 return Promise.bind(this)
1230 .then(function() {
1231 /**
1232 * Destroying event.
1233 *
1234 * Fired before a `delete` query. A promise may be returned from the event
1235 * handler for async behaviour. Throwing an exception from the handler
1236 * will reject the promise and cancel the deletion.
1237 *
1238 * @event Model#destroying
1239 * @tutorial events
1240 * @param {Model} model The model firing the event.
1241 * @param {Object} options Options object passed to {@link Model#destroy destroy}.
1242 * @returns {Promise}
1243 */
1244 return this.triggerThen('destroying', this, options);
1245 })
1246 .then(function() {
1247 return sync.del();
1248 })
1249 .then(function(affectedRows) {
1250 if (options.require !== false && affectedRows === 0) {
1251 throw new this.constructor.NoRowsDeletedError('No Rows Deleted');
1252 }
1253
1254 this._previousAttributes = _.clone(this.attributes);
1255 this.clear();
1256
1257 /**
1258 * Destroyed event.
1259 *
1260 * Fired after a `delete` query. A promise may be returned from the event
1261 * handler for async behaviour.
1262 *
1263 * @event Model#destroyed
1264 * @tutorial events
1265 * @param {Model} model The model firing the event.
1266 * @param {Object} options Options object passed to {@link Model#destroy destroy}.
1267 * @returns {Promise}
1268 */
1269 return this.triggerThen('destroyed', this, options);
1270 })
1271 .then(this._reset);
1272 }),
1273
1274 /**
1275 * Used to reset the internal state of the current query builder instance.
1276 * This method is called internally each time a database action is completed
1277 * by {@link Sync}
1278 *
1279 * @method Model#resetQuery
1280 * @returns {Model} Self, this method is chainable.
1281 */
1282 resetQuery() {
1283 this._knex = null;
1284 return this;
1285 },
1286
1287 /**
1288 * The `query` method is used to tap into the underlying Knex query builder
1289 * instance for the current model. If called with no arguments, it will
1290 * return the query builder directly. Otherwise, it will call the specified
1291 * method on the query builder, applying any additional arguments from the
1292 * `model.query` call. If the method argument is a function, it will be
1293 * called with the Knex query builder as the context and the first argument,
1294 * returning the current model.
1295 *
1296 * @example
1297 *
1298 * model
1299 * .query('where', 'other_id', '=', '5')
1300 * .fetch()
1301 * .then(function(model) {
1302 * // ...
1303 * });
1304 *
1305 * model
1306 * .query({where: {other_id: '5'}, orWhere: {key: 'value'}})
1307 * .fetch()
1308 * .then(function(model) {
1309 * // ...
1310 * });
1311 *
1312 * model.query(function(qb) {
1313 * qb.where('other_person', 'LIKE', '%Demo').orWhere('other_id', '>', 10);
1314 * }).fetch()
1315 * .then(function(model) {
1316 * // ...
1317 * });
1318 *
1319 * let qb = model.query();
1320 * qb.where({id: 1}).select().then(function(resp) {
1321 * // ...
1322 * });
1323 *
1324 * @method Model#query
1325 * @param {function|Object|...string=} arguments The query method.
1326 * @returns {Model|QueryBuilder}
1327 * Will return this model or, if called with no arguments, the underlying query builder.
1328 *
1329 * @see {@link http://knexjs.org/#Builder Knex `QueryBuilder`}
1330 */
1331 query() {
1332 return Helpers.query(this, Array.from(arguments));
1333 },
1334
1335 /**
1336 * The where method is used as convenience for the most common {@link
1337 * Model#query query} method, adding a where clause to the builder. Any
1338 * additional knex methods may be accessed using {@link Model#query query}.
1339 *
1340 * Accepts either key, value syntax, or a hash of attributes.
1341 *
1342 * @example
1343 *
1344 * model.where('favorite_color', '<>', 'green').fetch().then(function() { //...
1345 * // or
1346 * model.where('favorite_color', 'red').fetch().then(function() { //...
1347 * // or
1348 * model.where({favorite_color: 'red', shoe_size: 12}).fetch().then(function() { //...
1349 *
1350 * @method Model#where
1351 * @param {Object|...string} method
1352 *
1353 * Either `key, [operator], value` syntax, or a hash of attributes to
1354 * match. Note that these must be formatted as they are in the database,
1355 * not how they are stored after {@link Model#parse}.
1356 *
1357 * @returns {Model} Self, this method is chainable.
1358 *
1359 * @see Model#query
1360 */
1361 where() {
1362 return this.query.apply(this, ['where'].concat(Array.from(arguments)));
1363 },
1364
1365 /**
1366 * @method Model#orderBy
1367 * @since 0.9.3
1368 * @description
1369 *
1370 * Specifies the column to sort on and sort order.
1371 *
1372 * The order parameter is optional, and defaults to 'ASC'. You may
1373 * also specify 'DESC' order by prepending a hyphen to the sort column
1374 * name. `orderBy("date", 'DESC')` is the same as `orderBy("-date")`.
1375 *
1376 * Unless specified using dot notation (i.e., "table.column"), the default
1377 * table will be the table name of the model `orderBy` was called on.
1378 *
1379 * @example
1380 *
1381 * Car.forge().orderBy('color', 'ASC').fetchAll()
1382 * .then(function (rows) { // ...
1383 *
1384 * @param sort {string}
1385 * Column to sort on
1386 * @param order {string}
1387 * Ascending ('ASC') or descending ('DESC') order
1388 */
1389 orderBy() {
1390 return Helpers.orderBy.apply(null, [this].concat(Array.from(arguments)));
1391 },
1392
1393 /* Ensure that QueryBuilder is copied on clone. */
1394 clone() {
1395 // This needs to use the direct apply method because the spread operator
1396 // incorrectly converts to `clone.apply(ModelBase.prototype, arguments)`
1397 // instead of `apply(this, arguments)`
1398 const cloned = BookshelfModel.__super__.clone.apply(this, arguments);
1399 if (this._knex != null) {
1400 cloned._knex = cloned._builder(this._knex.clone());
1401 }
1402 return cloned;
1403 },
1404
1405 /**
1406 * Creates and returns a new Bookshelf.Sync instance.
1407 *
1408 * @method Model#sync
1409 * @private
1410 * @returns Sync
1411 */
1412 sync(options) {
1413 return new Sync(this, options);
1414 },
1415
1416 /**
1417 * Helper for setting up the `morphOne` or `morphMany` relations.
1418 *
1419 * @method Model#_morphOneOrMany
1420 * @private
1421 */
1422 _morphOneOrMany(Target, morphName, columnNames, morphValue, type) {
1423 if (!Array.isArray(columnNames)) {
1424 // Shift by one place
1425 morphValue = columnNames;
1426 columnNames = null;
1427 }
1428 if (!morphName || !Target) throw new Error('The polymorphic `name` and `Target` are required.');
1429
1430 return this._relation(type, Target, {
1431 morphName: morphName,
1432 morphValue: morphValue,
1433 columnNames: columnNames
1434 }).init(this);
1435 },
1436
1437 /**
1438 * @name Model#_handleResponse
1439 * @private
1440 * @description
1441 *
1442 * Handles the response data for the model, returning from the model's fetch call.
1443 *
1444 * @param {Object} Response from Knex query.
1445 *
1446 * @todo: need to check on Backbone's status there, ticket #2636
1447 * @todo: {silent: true, parse: true}, for parity with collection#set
1448 */
1449 _handleResponse(response) {
1450 const relatedData = this.relatedData;
1451
1452 this.set(this.parse(response[0]), {silent: true})
1453 .formatTimestamps()
1454 ._reset();
1455 this._previousAttributes = _.cloneDeep(this.attributes);
1456
1457 if (relatedData && relatedData.isJoined()) {
1458 relatedData.parsePivot([this]);
1459 }
1460 },
1461
1462 /**
1463 * @name Model#_handleEager
1464 * @private
1465 * @description
1466 *
1467 * Handles the related data loading on the model.
1468 *
1469 * @param {Object} Response from Knex query.
1470 */
1471 _handleEager(response, options) {
1472 return new EagerRelation([this], response, this).fetch(options);
1473 }
1474 },
1475 {
1476 extended(child) {
1477 /**
1478 * @class Model.NotFoundError
1479 * @description
1480 *
1481 * Thrown when no records are found by {@link Model#fetch fetch} or
1482 * {@link Model#refresh} when called with the
1483 * `{require: true}` option.
1484 */
1485 child.NotFoundError = createError(this.NotFoundError);
1486
1487 /**
1488 * @class Model.NoRowsUpdatedError
1489 * @description
1490 *
1491 * Thrown when no records are saved by {@link Model#save save}
1492 * unless called with the `{require: false}` option.
1493 */
1494 child.NoRowsUpdatedError = createError(this.NoRowsUpdatedError);
1495
1496 /**
1497 * @class Model.NoRowsDeletedError
1498 * @description
1499 *
1500 * Thrown when no record is deleted by {@link Model#destroy destroy}
1501 * if called with the `{require: true}` option.
1502 */
1503 child.NoRowsDeletedError = createError(this.NoRowsDeletedError);
1504 }
1505 }
1506);
1507
1508BookshelfModel.NotFoundError = Errors.NotFoundError;
1509BookshelfModel.NoRowsUpdatedError = Errors.NoRowsUpdatedError;
1510BookshelfModel.NoRowsDeletedError = Errors.NoRowsDeletedError;
1511
1512module.exports = BookshelfModel;