UNPKG

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