UNPKG

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