@depup/bookshelf 1.2.0-depup.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintrc.json +18 -0
- package/.nycrc.yml +3 -0
- package/.prettierrc +8 -0
- package/CHANGELOG.md +764 -0
- package/LICENSE +22 -0
- package/README.md +32 -0
- package/bookshelf.js +8 -0
- package/changes.json +14 -0
- package/lib/base/collection.js +802 -0
- package/lib/base/eager.js +111 -0
- package/lib/base/events.js +129 -0
- package/lib/base/model.js +995 -0
- package/lib/base/relation.js +69 -0
- package/lib/bookshelf.js +558 -0
- package/lib/collection.js +545 -0
- package/lib/constants.js +2 -0
- package/lib/eager.js +122 -0
- package/lib/errors.js +36 -0
- package/lib/extend.js +41 -0
- package/lib/helpers.js +212 -0
- package/lib/model.js +1568 -0
- package/lib/relation.js +947 -0
- package/lib/sync.js +244 -0
- package/package.json +111 -0
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
const _ = require('lodash');
|
|
2
|
+
const CollectionBase = require('./collection');
|
|
3
|
+
const extend = require('../extend');
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @class
|
|
7
|
+
* @param {string} type
|
|
8
|
+
* The type of relation to create. Can be one of 'hasOne', 'hasMany', 'belongsTo',
|
|
9
|
+
* 'belongsToMany' or 'morphTo'.
|
|
10
|
+
* @param {Model|Collection|null} Target
|
|
11
|
+
* The target model or collection for this relation or `null` in case the target model will be
|
|
12
|
+
* determined at a later time, as is the case of `morphTo` relations.
|
|
13
|
+
* @param {object} options
|
|
14
|
+
* Additional properties to set on the relation object. These vary according to the type of
|
|
15
|
+
* relation.
|
|
16
|
+
*/
|
|
17
|
+
function RelationBase(type, Target, options) {
|
|
18
|
+
if (Target) {
|
|
19
|
+
this.targetTableName = _.result(Target.prototype, 'tableName');
|
|
20
|
+
this.targetIdAttribute = _.result(Target.prototype, 'idAttribute');
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
Object.assign(this, {type, target: Target}, options);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Creates a new relation instance. Used by the `Eager` relation when dealing with `morphTo` cases,
|
|
28
|
+
* where the same relation is targeting multiple models. It uses the same argument parameters as the
|
|
29
|
+
* base constructor.
|
|
30
|
+
*
|
|
31
|
+
* @return {RelationBase}
|
|
32
|
+
*/
|
|
33
|
+
RelationBase.prototype.instance = function instance(type, Target, options) {
|
|
34
|
+
return new this.constructor(type, Target, options);
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Creates a new, unparsed model. Used internally in the eager fetch helper methods because parsing
|
|
39
|
+
* may mutate information necessary for eager pairing.
|
|
40
|
+
*
|
|
41
|
+
* @param {object} data Model attributes to set on the new model.
|
|
42
|
+
* @return {Model} The new model.
|
|
43
|
+
*/
|
|
44
|
+
RelationBase.prototype.createModel = function createModel(data) {
|
|
45
|
+
if (this.target.prototype instanceof CollectionBase) {
|
|
46
|
+
return new this.target.prototype.model(data)._reset();
|
|
47
|
+
}
|
|
48
|
+
return new this.target(data)._reset();
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Clones a relation. Required by {@link Model#fetchPage}.
|
|
53
|
+
*
|
|
54
|
+
* @todo Can probably be removed for a simpler approach, or just the `instance` method.
|
|
55
|
+
* @return {RelationBase}
|
|
56
|
+
*/
|
|
57
|
+
RelationBase.prototype.clone = function clone() {
|
|
58
|
+
return new this.constructor(null, null, this);
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Extends the Base Relation.
|
|
63
|
+
*
|
|
64
|
+
* @method
|
|
65
|
+
* @static
|
|
66
|
+
*/
|
|
67
|
+
RelationBase.extend = extend;
|
|
68
|
+
|
|
69
|
+
module.exports = RelationBase;
|
package/lib/bookshelf.js
ADDED
|
@@ -0,0 +1,558 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const _ = require('lodash');
|
|
4
|
+
|
|
5
|
+
// We've supplemented `Events` with a `triggerThen` method to allow for
|
|
6
|
+
// asynchronous event handling via promises. We also mix this into the
|
|
7
|
+
// prototypes of the main objects in the library.
|
|
8
|
+
const Events = require('./base/events');
|
|
9
|
+
|
|
10
|
+
// All core modules required for the bookshelf instance.
|
|
11
|
+
const BookshelfModel = require('./model');
|
|
12
|
+
const BookshelfCollection = require('./collection');
|
|
13
|
+
const BookshelfRelation = require('./relation');
|
|
14
|
+
const errors = require('./errors');
|
|
15
|
+
|
|
16
|
+
function preventOverwrite(store, name) {
|
|
17
|
+
if (store[name]) throw new Error(`${name} is already defined in the registry`);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* @class
|
|
22
|
+
* @classdesc
|
|
23
|
+
*
|
|
24
|
+
* The Bookshelf library is initialized by passing an initialized Knex client
|
|
25
|
+
* instance. The knex documentation provides a number of examples for different
|
|
26
|
+
* databases.
|
|
27
|
+
*
|
|
28
|
+
* @constructor
|
|
29
|
+
* @param {Knex} knex Knex instance.
|
|
30
|
+
*/
|
|
31
|
+
function Bookshelf(knex) {
|
|
32
|
+
if (!knex || knex.name !== 'knex') {
|
|
33
|
+
throw new Error('Invalid knex instance');
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function resolveModel(input) {
|
|
37
|
+
if (typeof input !== 'string') return input;
|
|
38
|
+
|
|
39
|
+
return (
|
|
40
|
+
bookshelf.collection(input) ||
|
|
41
|
+
bookshelf.model(input) ||
|
|
42
|
+
(function() {
|
|
43
|
+
throw new errors.ModelNotResolvedError(`The model ${input} could not be resolved from the registry.`);
|
|
44
|
+
})()
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** @lends Bookshelf.prototype */
|
|
49
|
+
const bookshelf = {
|
|
50
|
+
registry: {
|
|
51
|
+
collections: {},
|
|
52
|
+
models: {}
|
|
53
|
+
},
|
|
54
|
+
VERSION: require('../package.json').version,
|
|
55
|
+
|
|
56
|
+
collection(name, Collection, staticProperties) {
|
|
57
|
+
if (Collection) {
|
|
58
|
+
preventOverwrite(this.registry.collections, name);
|
|
59
|
+
|
|
60
|
+
if (_.isPlainObject(Collection)) {
|
|
61
|
+
Collection = this.Collection.extend(Collection, staticProperties);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
this.registry.collections[name] = Collection;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return this.registry.collections[name] || bookshelf.resolve(name);
|
|
68
|
+
},
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Registers a model. Omit the second argument `Model` to return a previously registered model that matches the
|
|
72
|
+
* provided name.
|
|
73
|
+
*
|
|
74
|
+
* Note that when registering a model with this method it will also be available to all relation methods, allowing
|
|
75
|
+
* you to use a string name in that case. See the calls to `hasMany()` in the examples above.
|
|
76
|
+
*
|
|
77
|
+
* @example
|
|
78
|
+
* // Defining and registering a model
|
|
79
|
+
* module.exports = bookshelf.model('Customer', {
|
|
80
|
+
* tableName: 'customers',
|
|
81
|
+
* orders() {
|
|
82
|
+
* return this.hasMany('Order')
|
|
83
|
+
* }
|
|
84
|
+
* })
|
|
85
|
+
*
|
|
86
|
+
* // Retrieving a previously registered model
|
|
87
|
+
* const Customer = bookshelf.model('Customer')
|
|
88
|
+
*
|
|
89
|
+
* // Registering already defined models
|
|
90
|
+
* // file: customer.js
|
|
91
|
+
* const Customer = bookshelf.Model.extend({
|
|
92
|
+
* tableName: 'customers',
|
|
93
|
+
* orders() {
|
|
94
|
+
* return this.hasMany('Order')
|
|
95
|
+
* }
|
|
96
|
+
* })
|
|
97
|
+
* module.exports = bookshelf.model('Customer', Customer)
|
|
98
|
+
*
|
|
99
|
+
* // file: order.js
|
|
100
|
+
* const Order = bookshelf.Model.extend({
|
|
101
|
+
* tableName: 'orders',
|
|
102
|
+
* customer() {
|
|
103
|
+
* return this.belongsTo('Customer')
|
|
104
|
+
* }
|
|
105
|
+
* })
|
|
106
|
+
* module.exports = bookshelf.model('Order', Order)
|
|
107
|
+
*
|
|
108
|
+
* @param {string} name
|
|
109
|
+
* The name to save the model as, or the name of the model to retrieve if no further arguments are passed to this
|
|
110
|
+
* method.
|
|
111
|
+
* @param {Model|Object} [Model]
|
|
112
|
+
* The model to register. If a plain object is passed it will be converted to a {@link Model}. See example above.
|
|
113
|
+
* @param {Object} [staticProperties]
|
|
114
|
+
* If a plain object is passed as second argument, this can be used to specify additional static properties and
|
|
115
|
+
* methods for the new model that is created.
|
|
116
|
+
* @return {Model} The registered model.
|
|
117
|
+
*/
|
|
118
|
+
model(name, Model, staticProperties) {
|
|
119
|
+
if (Model) {
|
|
120
|
+
preventOverwrite(this.registry.models, name);
|
|
121
|
+
if (_.isPlainObject(Model)) Model = this.Model.extend(Model, staticProperties);
|
|
122
|
+
this.registry.models[name] = Model;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
return this.registry.models[name] || bookshelf.resolve(name);
|
|
126
|
+
},
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Override this in your bookshelf instance to define a custom function that will resolve the location of a model or
|
|
130
|
+
* collection when using the {@link Bookshelf#model} method or when passing a string with a model name in any of the
|
|
131
|
+
* collection methods (e.g. {@link Model#hasOne}, {@link Model#hasMany}, etc.).
|
|
132
|
+
*
|
|
133
|
+
* This will only be used if the specified name cannot be found in the registry. Note that this function
|
|
134
|
+
* can return anything you'd like, so it's not restricted in functionality.
|
|
135
|
+
*
|
|
136
|
+
* @example
|
|
137
|
+
* const Customer = bookshelf.model('Customer', {
|
|
138
|
+
* tableName: 'customers'
|
|
139
|
+
* })
|
|
140
|
+
*
|
|
141
|
+
* bookshelf.resolve = (name) => {
|
|
142
|
+
* if (name === 'SpecialCustomer') return Customer;
|
|
143
|
+
* }
|
|
144
|
+
*
|
|
145
|
+
* @param {string} name The model name to resolve.
|
|
146
|
+
* @return {*} The return value will depend on what your re-implementation of this function does.
|
|
147
|
+
*/
|
|
148
|
+
resolve(name) {}
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
const Model = (bookshelf.Model = BookshelfModel.extend(
|
|
152
|
+
{
|
|
153
|
+
_builder: builderFn,
|
|
154
|
+
|
|
155
|
+
// The `Model` constructor is referenced as a property on the `Bookshelf` instance, mixing in the correct
|
|
156
|
+
// `builder` method, as well as the `relation` method, passing in the correct `Model` & `Collection`
|
|
157
|
+
// constructors for later reference.
|
|
158
|
+
_relation(type, Target, options) {
|
|
159
|
+
Target = resolveModel(Target);
|
|
160
|
+
|
|
161
|
+
if (type !== 'morphTo' && !_.isFunction(Target)) {
|
|
162
|
+
throw new Error(
|
|
163
|
+
'A valid target model must be defined for the ' + _.result(this, 'tableName') + ' ' + type + ' relation'
|
|
164
|
+
);
|
|
165
|
+
}
|
|
166
|
+
return new Relation(type, Target, options);
|
|
167
|
+
},
|
|
168
|
+
|
|
169
|
+
morphTo(relationName, ...args) {
|
|
170
|
+
let candidates = args;
|
|
171
|
+
let columnNames = null;
|
|
172
|
+
|
|
173
|
+
if (Array.isArray(args[0]) || args[0] === null || args[0] === undefined) {
|
|
174
|
+
candidates = args.slice(1);
|
|
175
|
+
columnNames = args[0];
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
if (Array.isArray(columnNames)) {
|
|
179
|
+
// Try to use the columnNames as target instead
|
|
180
|
+
try {
|
|
181
|
+
columnNames[0] = resolveModel(columnNames[0]);
|
|
182
|
+
} catch (error) {
|
|
183
|
+
// If it did not work, they were real columnNames
|
|
184
|
+
if (error instanceof errors.ModelNotResolvedError) throw error;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
const models = candidates.map((candidate) => {
|
|
189
|
+
if (!Array.isArray(candidate)) return resolveModel(candidate);
|
|
190
|
+
|
|
191
|
+
const model = candidate[0];
|
|
192
|
+
const morphValue = candidate[1];
|
|
193
|
+
|
|
194
|
+
return [resolveModel(model), morphValue];
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
return BookshelfModel.prototype.morphTo.apply(this, [relationName, columnNames].concat(models));
|
|
198
|
+
},
|
|
199
|
+
|
|
200
|
+
through(Source, ...rest) {
|
|
201
|
+
return BookshelfModel.prototype.through.apply(this, [resolveModel(Source), ...rest]);
|
|
202
|
+
}
|
|
203
|
+
},
|
|
204
|
+
{
|
|
205
|
+
/**
|
|
206
|
+
* @method Model.forge
|
|
207
|
+
* @description
|
|
208
|
+
*
|
|
209
|
+
* A simple helper function to instantiate a new Model without needing `new`.
|
|
210
|
+
*
|
|
211
|
+
* @param {Object=} attributes Initial values for this model's attributes.
|
|
212
|
+
* @param {Object=} options Hash of options.
|
|
213
|
+
* @param {string=} options.tableName Initial value for {@linkcode Model#tableName tableName}.
|
|
214
|
+
* @param {Boolean=} [options.hasTimestamps=false]
|
|
215
|
+
*
|
|
216
|
+
* Initial value for {@linkcode Model#hasTimestamps hasTimestamps}.
|
|
217
|
+
*
|
|
218
|
+
* @param {Boolean} [options.parse=false]
|
|
219
|
+
*
|
|
220
|
+
* Convert attributes by {@linkcode Model#parse parse} before being
|
|
221
|
+
* {@linkcode Model#set set} on the `model`.
|
|
222
|
+
*/
|
|
223
|
+
forge: function forge(attributes, options) {
|
|
224
|
+
return new this(attributes, options);
|
|
225
|
+
},
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* A simple static helper to instantiate a new {@link Collection}, setting the model it's
|
|
229
|
+
* called on as the collection's target model.
|
|
230
|
+
*
|
|
231
|
+
* @example
|
|
232
|
+
* Customer.collection().fetch().then((customers) => {
|
|
233
|
+
* // ...
|
|
234
|
+
* })
|
|
235
|
+
*
|
|
236
|
+
* @method Model.collection
|
|
237
|
+
* @param {Model[]} [models] Any models to be added to the collection.
|
|
238
|
+
* @param {Object} [options] Additional options to pass to the {@link Collection} constructor.
|
|
239
|
+
* @param {string|function} [options.comparator]
|
|
240
|
+
* If specified this is used to sort the collection. It can be a string representing the
|
|
241
|
+
* model attribute to sort by, or a custom function. Check the documentation for {@link
|
|
242
|
+
* https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort
|
|
243
|
+
* Array.prototype.sort} for more info on how to use a custom comparator function. If this
|
|
244
|
+
* options is not specified the collection sort order depends on what the database returns.
|
|
245
|
+
* @returns {Collection}
|
|
246
|
+
* The newly created collection. It will be empty unless any models were passed as the first
|
|
247
|
+
* argument.
|
|
248
|
+
*/
|
|
249
|
+
collection(models, options) {
|
|
250
|
+
return new bookshelf.Collection(models || [], _.extend({}, options, {model: this}));
|
|
251
|
+
},
|
|
252
|
+
|
|
253
|
+
/**
|
|
254
|
+
* Shortcut to a model's `count` method so you don't need to instantiate a new model to count
|
|
255
|
+
* the number of records.
|
|
256
|
+
*
|
|
257
|
+
* @example
|
|
258
|
+
* Duck.count().then((count) => {
|
|
259
|
+
* console.log('number of ducks', count)
|
|
260
|
+
* })
|
|
261
|
+
*
|
|
262
|
+
* @method Model.count
|
|
263
|
+
* @since 0.8.2
|
|
264
|
+
* @see Model#count
|
|
265
|
+
* @param {string} [column='*']
|
|
266
|
+
* Specify a column to count. Rows with `null` values in this column will be excluded.
|
|
267
|
+
* @param {Object} [options] Hash of options.
|
|
268
|
+
* @param {boolean} [options.debug=false]
|
|
269
|
+
* Whether to enable debugging mode or not. When enabled will show information about the
|
|
270
|
+
* queries being run.
|
|
271
|
+
* @returns {Promise<number|string>}
|
|
272
|
+
*/
|
|
273
|
+
count(column, options) {
|
|
274
|
+
return this.forge().count(column, options);
|
|
275
|
+
},
|
|
276
|
+
|
|
277
|
+
/**
|
|
278
|
+
* @method Model.fetchAll
|
|
279
|
+
* @description
|
|
280
|
+
*
|
|
281
|
+
* Simple helper function for retrieving all instances of the given model.
|
|
282
|
+
*
|
|
283
|
+
* @see Model#fetchAll
|
|
284
|
+
* @returns {Promise<Collection>}
|
|
285
|
+
*/
|
|
286
|
+
fetchAll(options) {
|
|
287
|
+
return this.forge().fetchAll(options);
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
));
|
|
291
|
+
|
|
292
|
+
const Collection = (bookshelf.Collection = BookshelfCollection.extend(
|
|
293
|
+
{
|
|
294
|
+
_builder: builderFn,
|
|
295
|
+
through(Source, ...args) {
|
|
296
|
+
return BookshelfCollection.prototype.through.apply(this, [resolveModel(Source), ...args]);
|
|
297
|
+
}
|
|
298
|
+
},
|
|
299
|
+
{
|
|
300
|
+
/**
|
|
301
|
+
* @method Collection.forge
|
|
302
|
+
* @description
|
|
303
|
+
*
|
|
304
|
+
* A simple helper function to instantiate a new Collection without needing
|
|
305
|
+
* new.
|
|
306
|
+
*
|
|
307
|
+
* @param {(Object[]|Model[])=} [models]
|
|
308
|
+
* Set of models (or attribute hashes) with which to initialize the
|
|
309
|
+
* collection.
|
|
310
|
+
* @param {Object} options Hash of options.
|
|
311
|
+
*
|
|
312
|
+
* @example
|
|
313
|
+
*
|
|
314
|
+
* var Promise = require('bluebird');
|
|
315
|
+
* var Accounts = bookshelf.Collection.extend({
|
|
316
|
+
* model: Account
|
|
317
|
+
* });
|
|
318
|
+
*
|
|
319
|
+
* var accounts = Accounts.forge([
|
|
320
|
+
* {name: 'Person1'},
|
|
321
|
+
* {name: 'Person2'}
|
|
322
|
+
* ]);
|
|
323
|
+
*
|
|
324
|
+
* Promise.all(accounts.invokeMap('save')).then(function() {
|
|
325
|
+
* // collection models should now be saved...
|
|
326
|
+
* });
|
|
327
|
+
*/
|
|
328
|
+
forge: function forge(models, options) {
|
|
329
|
+
return new this(models, options);
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
));
|
|
333
|
+
|
|
334
|
+
// The collection also references the correct `Model`, specified above, for
|
|
335
|
+
// creating new `Model` instances in the collection.
|
|
336
|
+
Collection.prototype.model = Model;
|
|
337
|
+
Model.prototype.Collection = Collection;
|
|
338
|
+
|
|
339
|
+
const Relation = BookshelfRelation.extend({Model, Collection});
|
|
340
|
+
|
|
341
|
+
// A `Bookshelf` instance may be used as a top-level pub-sub bus, as it mixes
|
|
342
|
+
// in the `Events` object. It also contains the version number, and a
|
|
343
|
+
// `Transaction` method referencing the correct version of `knex` passed into
|
|
344
|
+
// the object.
|
|
345
|
+
_.extend(bookshelf, Events, errors, {
|
|
346
|
+
/**
|
|
347
|
+
* An alias to `{@link http://knexjs.org/#Transactions Knex#transaction}`. The `transaction`
|
|
348
|
+
* object must be passed along in the options of any relevant Bookshelf calls, to ensure all
|
|
349
|
+
* queries are on the same connection. The entire transaction block is wrapped around a Promise
|
|
350
|
+
* that will commit the transaction if it resolves successfully, or roll it back if the Promise
|
|
351
|
+
* is rejected.
|
|
352
|
+
*
|
|
353
|
+
* Note that there is no need to explicitly call `transaction.commit()` or
|
|
354
|
+
* `transaction.rollback()` since the entire transaction will be committed if there are no
|
|
355
|
+
* errors inside the transaction block.
|
|
356
|
+
*
|
|
357
|
+
* When fetching inside a transaction it's possible to specify a row-level lock by passing the
|
|
358
|
+
* wanted lock type in the `lock` option to {@linkcode Model#fetch fetch}. Available options are
|
|
359
|
+
* `lock: 'forUpdate'` and `lock: 'forShare'`.
|
|
360
|
+
*
|
|
361
|
+
* @example
|
|
362
|
+
* var Promise = require('bluebird')
|
|
363
|
+
*
|
|
364
|
+
* Bookshelf.transaction((t) => {
|
|
365
|
+
* return new Library({name: 'Old Books'})
|
|
366
|
+
* .save(null, {transacting: t})
|
|
367
|
+
* .tap(function(model) {
|
|
368
|
+
* return Promise.map([
|
|
369
|
+
* {title: 'Canterbury Tales'},
|
|
370
|
+
* {title: 'Moby Dick'},
|
|
371
|
+
* {title: 'Hamlet'}
|
|
372
|
+
* ], (info) => {
|
|
373
|
+
* return new Book(info).save({'shelf_id': model.id}, {transacting: t})
|
|
374
|
+
* })
|
|
375
|
+
* })
|
|
376
|
+
* }).then((library) => {
|
|
377
|
+
* console.log(library.related('books').pluck('title'))
|
|
378
|
+
* }).catch((err) => {
|
|
379
|
+
* console.error(err)
|
|
380
|
+
* })
|
|
381
|
+
*
|
|
382
|
+
* @method Bookshelf#transaction
|
|
383
|
+
* @param {Bookshelf~transactionCallback} transactionCallback
|
|
384
|
+
* Callback containing transaction logic. The callback should return a Promise.
|
|
385
|
+
* @returns {Promise}
|
|
386
|
+
* A promise resolving to the value returned from
|
|
387
|
+
* {@link Bookshelf~transactionCallback transactionCallback}.
|
|
388
|
+
*/
|
|
389
|
+
transaction() {
|
|
390
|
+
return this.knex.transaction.apply(this.knex, arguments);
|
|
391
|
+
},
|
|
392
|
+
|
|
393
|
+
/**
|
|
394
|
+
* This is a transaction block to be provided to {@link Bookshelf#transaction}. All of the
|
|
395
|
+
* database operations inside it can be part of the same transaction by passing the
|
|
396
|
+
* `transacting: transaction` option to {@link Model#fetch fetch}, {@link Model#save save} or
|
|
397
|
+
* {@link Model#destroy destroy}.
|
|
398
|
+
*
|
|
399
|
+
* Note that unless you explicitly pass the `transaction` object along to any relevant model
|
|
400
|
+
* operations, those operations will not be part of the transaction, even though they may be
|
|
401
|
+
* inside the transaction callback.
|
|
402
|
+
*
|
|
403
|
+
* @callback Bookshelf~transactionCallback
|
|
404
|
+
* @see {@link http://knexjs.org/#Transactions Knex#transaction}
|
|
405
|
+
* @see Bookshelf#transaction
|
|
406
|
+
*
|
|
407
|
+
* @param {Transaction} transaction
|
|
408
|
+
* @returns {Promise}
|
|
409
|
+
* The Promise will resolve to the return value of the callback, or be rejected with an error
|
|
410
|
+
* thrown inside it. If it resolves, the entire transaction is committed, otherwise it is
|
|
411
|
+
* rolled back.
|
|
412
|
+
*/
|
|
413
|
+
|
|
414
|
+
/**
|
|
415
|
+
* @method Bookshelf#plugin
|
|
416
|
+
* @memberOf Bookshelf
|
|
417
|
+
* @description
|
|
418
|
+
*
|
|
419
|
+
* This method provides a nice, tested, standardized way of adding plugins
|
|
420
|
+
* to a `Bookshelf` instance, injecting the current instance into the
|
|
421
|
+
* plugin, which should be a `module.exports`.
|
|
422
|
+
*
|
|
423
|
+
* You can add a plugin by specifying a string with the name of the plugin
|
|
424
|
+
* to load. In this case it will try to find a module. It will pass the
|
|
425
|
+
* string to `require()`, so you can either require a third-party dependency
|
|
426
|
+
* by name or one of your own modules by relative path:
|
|
427
|
+
*
|
|
428
|
+
* bookshelf.plugin('./bookshelf-plugins/my-favourite-plugin');
|
|
429
|
+
* bookshelf.plugin('plugin-from-npm');
|
|
430
|
+
*
|
|
431
|
+
* There are a few official plugins published in `npm`, along with many
|
|
432
|
+
* independently developed ones. See
|
|
433
|
+
* [the list of available plugins](index.html#official-plugins).
|
|
434
|
+
*
|
|
435
|
+
* You can also provide an array of strings or functions, which is the same
|
|
436
|
+
* as calling `bookshelf.plugin()` multiple times. In this case the same
|
|
437
|
+
* options object will be reused:
|
|
438
|
+
*
|
|
439
|
+
* bookshelf.plugin(['cool-plugin', './my-plugins/even-cooler-plugin']);
|
|
440
|
+
*
|
|
441
|
+
* Example plugin:
|
|
442
|
+
*
|
|
443
|
+
* // Converts all string values to lower case when setting attributes on a model
|
|
444
|
+
* module.exports = function(bookshelf) {
|
|
445
|
+
* bookshelf.Model = bookshelf.Model.extend({
|
|
446
|
+
* set(key, value, options) {
|
|
447
|
+
* if (!key) return this
|
|
448
|
+
* if (typeof value === 'string') value = value.toLowerCase()
|
|
449
|
+
* return bookshelf.Model.prototype.set.call(this, key, value, options)
|
|
450
|
+
* }
|
|
451
|
+
* })
|
|
452
|
+
* }
|
|
453
|
+
*
|
|
454
|
+
* @param {string|array|Function} plugin
|
|
455
|
+
* The plugin or plugins to load. If you provide a string it can
|
|
456
|
+
* represent an npm package or a file somewhere on your project. You can
|
|
457
|
+
* also pass a function as argument to add it as a plugin. Finally, it's
|
|
458
|
+
* also possible to pass an array of strings or functions to add them all
|
|
459
|
+
* at once.
|
|
460
|
+
* @param {mixed} options
|
|
461
|
+
* This can be anything you want and it will be passed directly to the
|
|
462
|
+
* plugin as the second argument when loading it.
|
|
463
|
+
* @return {Bookshelf} The bookshelf instance for chaining.
|
|
464
|
+
*/
|
|
465
|
+
plugin(plugin, options) {
|
|
466
|
+
if (_.isString(plugin)) {
|
|
467
|
+
if (plugin === 'pagination') {
|
|
468
|
+
const message =
|
|
469
|
+
'Pagination plugin was moved into core Bookshelf. You can now use `fetchPage()` without having to ' +
|
|
470
|
+
"call `.plugin('pagination')`. Remove any `.plugin('pagination')` calls to clear this message.";
|
|
471
|
+
return console.warn(message); // eslint-disable-line no-console
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
if (plugin === 'visibility') {
|
|
475
|
+
const message =
|
|
476
|
+
'Visibility plugin was moved into core Bookshelf. You can now set the `hidden` and `visible` properties ' +
|
|
477
|
+
"without having to call `.plugin('visibility')`. Remove any `.plugin('visibility')` calls to clear this " +
|
|
478
|
+
'message.';
|
|
479
|
+
return console.warn(message); // eslint-disable-line no-console
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
if (plugin === 'registry') {
|
|
483
|
+
const message =
|
|
484
|
+
'Registry plugin was moved into core Bookshelf. You can now register models using `bookshelf.model()` ' +
|
|
485
|
+
"and collections using `bookshelf.collection()` without having to call `.plugin('registry')`. Remove " +
|
|
486
|
+
"any `.plugin('registry')` calls to clear this message.";
|
|
487
|
+
return console.warn(message); // eslint-disable-line no-console
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
if (plugin === 'processor') {
|
|
491
|
+
const message =
|
|
492
|
+
'Processor plugin was removed from core Bookshelf. To migrate to the new standalone package follow the ' +
|
|
493
|
+
'instructions in https://github.com/bookshelf/bookshelf/wiki/Migrating-from-0.15.1-to-1.0.0#processor-plugin';
|
|
494
|
+
return console.warn(message); // eslint-disable-line no-console
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
if (plugin === 'case-converter') {
|
|
498
|
+
const message =
|
|
499
|
+
'Case converter plugin was removed from core Bookshelf. To migrate to the new standalone package follow ' +
|
|
500
|
+
'the instructions in https://github.com/bookshelf/bookshelf/wiki/Migrating-from-0.15.1-to-1.0.0#case-converter-plugin';
|
|
501
|
+
return console.warn(message); // eslint-disable-line no-console
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
if (plugin === 'virtuals') {
|
|
505
|
+
const message =
|
|
506
|
+
'Virtuals plugin was removed from core Bookshelf. To migrate to the new standalone package follow ' +
|
|
507
|
+
'the instructions in https://github.com/bookshelf/bookshelf/wiki/Migrating-from-0.15.1-to-1.0.0#virtuals-plugin';
|
|
508
|
+
return console.warn(message); // eslint-disable-line no-console
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
require(plugin)(this, options);
|
|
512
|
+
} else if (Array.isArray(plugin)) {
|
|
513
|
+
plugin.forEach((p) => this.plugin(p, options));
|
|
514
|
+
} else {
|
|
515
|
+
plugin(this, options);
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
return this;
|
|
519
|
+
}
|
|
520
|
+
});
|
|
521
|
+
|
|
522
|
+
/**
|
|
523
|
+
* @member Bookshelf#knex
|
|
524
|
+
* @type {Knex}
|
|
525
|
+
* @description
|
|
526
|
+
* A reference to the {@link http://knexjs.org Knex.js} instance being used by Bookshelf.
|
|
527
|
+
*/
|
|
528
|
+
bookshelf.knex = knex;
|
|
529
|
+
|
|
530
|
+
function builderFn(tableNameOrBuilder) {
|
|
531
|
+
let builder = null;
|
|
532
|
+
|
|
533
|
+
if (_.isString(tableNameOrBuilder)) {
|
|
534
|
+
builder = bookshelf.knex(tableNameOrBuilder);
|
|
535
|
+
} else if (tableNameOrBuilder == null) {
|
|
536
|
+
builder = bookshelf.knex.queryBuilder();
|
|
537
|
+
} else {
|
|
538
|
+
// Assuming here that `tableNameOrBuilder` is a QueryBuilder instance. Not
|
|
539
|
+
// aware of a way to check that this is the case (ie. using
|
|
540
|
+
// `Knex.isQueryBuilder` or equivalent).
|
|
541
|
+
builder = tableNameOrBuilder;
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
return builder.on('query', (data) => this.trigger('query', data));
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
// Attach `where`, `query`, and `fetchAll` as static methods.
|
|
548
|
+
['where', 'query'].forEach((method) => {
|
|
549
|
+
Model[method] = Collection[method] = function() {
|
|
550
|
+
const model = this.forge();
|
|
551
|
+
return model[method].apply(model, arguments);
|
|
552
|
+
};
|
|
553
|
+
});
|
|
554
|
+
|
|
555
|
+
return bookshelf;
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
module.exports = Bookshelf;
|