149 lines
4.1 KiB
JavaScript
149 lines
4.1 KiB
JavaScript
var async = require('async');
|
|
var utils = require('./utils');
|
|
var isPlainObject = utils.isPlainObject;
|
|
var defineCachedRelations = utils.defineCachedRelations;
|
|
|
|
/*!
|
|
* Include mixin for ./model.js
|
|
*/
|
|
module.exports = Inclusion;
|
|
|
|
/**
|
|
* Inclusion - Model mixin.
|
|
*
|
|
* @class
|
|
*/
|
|
|
|
function Inclusion() {
|
|
}
|
|
|
|
/**
|
|
* Enables you to load relations of several objects and optimize numbers of requests.
|
|
*
|
|
* Examples:
|
|
*
|
|
* Load all users' posts with only one additional request:
|
|
* `User.include(users, 'posts', function() {});`
|
|
* Or
|
|
* `User.include(users, ['posts'], function() {});`
|
|
*
|
|
* Load all users posts and passports with two additional requests:
|
|
* `User.include(users, ['posts', 'passports'], function() {});`
|
|
*
|
|
* Load all passports owner (users), and all posts of each owner loaded:
|
|
*```Passport.include(passports, {owner: 'posts'}, function() {});
|
|
*``` Passport.include(passports, {owner: ['posts', 'passports']});
|
|
*``` Passport.include(passports, {owner: [{posts: 'images'}, 'passports']});
|
|
*
|
|
* @param {Array} objects Array of instances
|
|
* @param {String}, {Object} or {Array} include Which relations to load.
|
|
* @param {Function} cb Callback called when relations are loaded
|
|
*
|
|
*/
|
|
Inclusion.include = function (objects, include, cb) {
|
|
var self = this;
|
|
|
|
if (!include || (Array.isArray(include) && include.length === 0) ||
|
|
(isPlainObject(include) && Object.keys(include).length === 0)) {
|
|
// The objects are empty
|
|
return process.nextTick(function() {
|
|
cb && cb(null, objects);
|
|
});
|
|
}
|
|
|
|
include = normalizeInclude(include);
|
|
|
|
async.each(include, function(item, callback) {
|
|
processIncludeItem(objects, item, callback);
|
|
}, function(err) {
|
|
cb && cb(err, objects);
|
|
});
|
|
|
|
|
|
/*!
|
|
* Normalize the include to be an array
|
|
* @param include
|
|
* @returns {*}
|
|
*/
|
|
function normalizeInclude(include) {
|
|
if (typeof include === 'string') {
|
|
return [include];
|
|
} else if (isPlainObject(include)) {
|
|
// Build an array of key/value pairs
|
|
var newInclude = [];
|
|
for (var key in include) {
|
|
var obj = {};
|
|
obj[key] = include[key];
|
|
newInclude.push(obj);
|
|
}
|
|
return newInclude;
|
|
} else {
|
|
return include;
|
|
}
|
|
}
|
|
|
|
function processIncludeItem(objs, include, cb) {
|
|
var relations = self.relations;
|
|
|
|
var relationName, subInclude;
|
|
if (isPlainObject(include)) {
|
|
relationName = Object.keys(include)[0];
|
|
subInclude = include[relationName];
|
|
} else {
|
|
relationName = include;
|
|
subInclude = null;
|
|
}
|
|
var relation = relations[relationName];
|
|
|
|
if (!relation) {
|
|
cb(new Error('Relation "' + relationName + '" is not defined for '
|
|
+ self.modelName + ' model'));
|
|
return;
|
|
}
|
|
|
|
// Calling the relation method for each object
|
|
async.each(objs, function (obj, callback) {
|
|
if(relation.type === 'belongsTo') {
|
|
// If the belongsTo relation doesn't have an owner
|
|
if(obj[relation.keyFrom] === null || obj[relation.keyFrom] === undefined) {
|
|
defineCachedRelations(obj);
|
|
// Set to null if the owner doesn't exist
|
|
obj.__cachedRelations[relationName] = null;
|
|
if(obj === inst) {
|
|
obj.__data[relationName] = null;
|
|
} else {
|
|
obj[relationName] = null;
|
|
}
|
|
return callback();
|
|
}
|
|
}
|
|
var inst = (obj instanceof self) ? obj : new self(obj);
|
|
// Calling the relation method on the instance
|
|
inst[relationName](function (err, result) {
|
|
if (err) {
|
|
return callback(err);
|
|
} else {
|
|
defineCachedRelations(obj);
|
|
obj.__cachedRelations[relationName] = result;
|
|
if(obj === inst) {
|
|
obj.__data[relationName] = result;
|
|
obj.strict = false;
|
|
} else {
|
|
obj[relationName] = result;
|
|
}
|
|
|
|
if (subInclude && result) {
|
|
var subItems = relation.multiple ? result : [result];
|
|
// Recursively include the related models
|
|
relation.modelTo.include(subItems, subInclude, callback);
|
|
} else {
|
|
callback(null, result);
|
|
}
|
|
}
|
|
});
|
|
}, cb);
|
|
|
|
}
|
|
};
|
|
|