Add support for hasOne
This commit is contained in:
parent
34c1998f04
commit
2db43c58e5
|
@ -656,3 +656,142 @@ RelationDefinition.hasAndBelongsToMany = function hasAndBelongsToMany(modelFrom,
|
|||
this.hasMany(modelFrom, modelTo, {as: params.as, through: params.through});
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* HasOne
|
||||
* @param modelFrom
|
||||
* @param modelTo
|
||||
* @param params
|
||||
*/
|
||||
RelationDefinition.hasOne = function (modelFrom, modelTo, params) {
|
||||
params = params || {};
|
||||
if ('string' === typeof modelTo) {
|
||||
params.as = modelTo;
|
||||
if (params.model) {
|
||||
modelTo = params.model;
|
||||
} else {
|
||||
var modelToName = modelTo.toLowerCase();
|
||||
modelTo = lookupModel(modelFrom.dataSource.modelBuilder.models, modelToName);
|
||||
}
|
||||
}
|
||||
|
||||
var pk = modelFrom.dataSource.idName(modelTo.modelName) || 'id';
|
||||
var relationName = params.as || i8n.camelize(modelTo.modelName, true);
|
||||
|
||||
var fk = params.foreignKey || i8n.camelize(modelFrom.modelName + '_id', true);
|
||||
|
||||
var relationDef = modelFrom.relations[relationName] = new RelationDefinition({
|
||||
name: relationName,
|
||||
type: RelationTypes.hasOne,
|
||||
modelFrom: modelFrom,
|
||||
keyFrom: pk,
|
||||
keyTo: fk,
|
||||
modelTo: modelTo
|
||||
});
|
||||
|
||||
modelFrom.dataSource.defineForeignKey(modelTo.modelName, fk, modelFrom.modelName);
|
||||
|
||||
// Define a property for the scope so that we have 'this' for the scoped methods
|
||||
Object.defineProperty(modelFrom.prototype, relationName, {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
get: function() {
|
||||
var relation = new HasOne(relationDef, this);
|
||||
var relationMethod = relation.related.bind(relation)
|
||||
relationMethod.create = relation.create.bind(relation);
|
||||
relationMethod.build = relation.build.bind(relation);
|
||||
return relationMethod;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
HasOne.prototype.create = function(targetModelData, cb) {
|
||||
var modelTo = this.definition.modelTo;
|
||||
var fk = this.definition.keyTo;
|
||||
var pk = this.definition.keyFrom;
|
||||
var modelInstance = this.modelInstance;
|
||||
|
||||
targetModelData = targetModelData || {};
|
||||
targetModelData[fk] = modelInstance[pk];
|
||||
modelTo.create(targetModelData, function(err, targetModel) {
|
||||
if(!err) {
|
||||
cb && cb(err, targetModel);
|
||||
} else {
|
||||
cb && cb(err);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
HasOne.prototype.build = function(targetModelData) {
|
||||
var modelTo = this.definition.modelTo;
|
||||
var pk = this.definition.keyFrom;
|
||||
var fk = this.definition.keyTo;
|
||||
targetModelData = targetModelData || {};
|
||||
targetModelData[fk] = this.modelInstance[pk];
|
||||
return new modelTo(targetModelData);
|
||||
};
|
||||
|
||||
/**
|
||||
* Define the method for the hasOne relation itself
|
||||
* It will support one of the following styles:
|
||||
* - order.customer(refresh, callback): Load the target model instance asynchronously
|
||||
* - order.customer(customer): Synchronous setter of the target model instance
|
||||
* - order.customer(): Synchronous getter of the target model instance
|
||||
*
|
||||
* @param refresh
|
||||
* @param params
|
||||
* @returns {*}
|
||||
*/
|
||||
HasOne.prototype.related = function (refresh, params) {
|
||||
var modelTo = this.definition.modelTo;
|
||||
var fk = this.definition.keyTo;
|
||||
var pk = this.definition.keyFrom;
|
||||
var modelInstance = this.modelInstance;
|
||||
var relationName = this.definition.name;
|
||||
|
||||
if (arguments.length === 1) {
|
||||
params = refresh;
|
||||
refresh = false;
|
||||
} else if (arguments.length > 2) {
|
||||
throw new Error('Method can\'t be called with more than two arguments');
|
||||
}
|
||||
|
||||
var cachedValue;
|
||||
if (!refresh && modelInstance.__cachedRelations
|
||||
&& (modelInstance.__cachedRelations[relationName] !== undefined)) {
|
||||
cachedValue = modelInstance.__cachedRelations[relationName];
|
||||
}
|
||||
if (params instanceof ModelBaseClass) { // acts as setter
|
||||
params[fk] = modelInstance[pk];
|
||||
modelInstance.__cachedRelations[relationName] = params;
|
||||
} else if (typeof params === 'function') { // acts as async getter
|
||||
var cb = params;
|
||||
if (cachedValue === undefined) {
|
||||
var query = {where: {}};
|
||||
query.where[fk] = modelInstance[pk];
|
||||
modelTo.findOne(query, function (err, inst) {
|
||||
if (err) {
|
||||
return cb(err);
|
||||
}
|
||||
if (!inst) {
|
||||
return cb(null, null);
|
||||
}
|
||||
// Check if the foreign key matches the primary key
|
||||
if (inst[fk] === modelInstance[pk]) {
|
||||
cb(null, inst);
|
||||
} else {
|
||||
cb(new Error('Permission denied'));
|
||||
}
|
||||
});
|
||||
return modelInstance[pk];
|
||||
} else {
|
||||
cb(null, cachedValue);
|
||||
return cachedValue;
|
||||
}
|
||||
} else if (params === undefined) { // acts as sync getter
|
||||
return modelInstance[pk];
|
||||
} else { // setter
|
||||
params[fk] = modelInstance[pk];
|
||||
delete modelInstance.__cachedRelations[relationName];
|
||||
}
|
||||
};
|
||||
|
|
|
@ -157,3 +157,7 @@ RelationMixin.belongsTo = function (modelTo, params) {
|
|||
RelationMixin.hasAndBelongsToMany = function hasAndBelongsToMany(modelTo, params) {
|
||||
RelationDefinition.hasAndBelongsToMany(this, modelTo, params);
|
||||
};
|
||||
|
||||
RelationMixin.hasOne = function hasMany(modelTo, params) {
|
||||
RelationDefinition.hasOne(this, modelTo, params);
|
||||
};
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
// This test written in mocha+should.js
|
||||
var should = require('./init.js');
|
||||
|
||||
var db, Book, Chapter, Author, Reader;
|
||||
var db, Book, Chapter, Author, Reader, Publisher;
|
||||
|
||||
describe('relations', function () {
|
||||
before(function (done) {
|
||||
|
@ -180,6 +180,43 @@ describe('relations', function () {
|
|||
|
||||
});
|
||||
|
||||
describe('hasOne', function () {
|
||||
var Supplier, Account;
|
||||
|
||||
before(function () {
|
||||
db = getSchema();
|
||||
Supplier = db.define('Supplier', {name: String});
|
||||
Account = db.define('Account', {accountNo: String});
|
||||
});
|
||||
|
||||
it('can be declared using hasOne method', function () {
|
||||
Supplier.hasOne(Account);
|
||||
Object.keys((new Account()).toObject()).should.include('supplierId');
|
||||
(new Supplier()).account.should.be.an.instanceOf(Function);
|
||||
|
||||
});
|
||||
|
||||
it('can be used to query data', function (done) {
|
||||
// Supplier.hasOne(Account);
|
||||
db.automigrate(function () {
|
||||
Supplier.create({name: 'Supplier 1'}, function (e, supplier) {
|
||||
should.not.exist(e);
|
||||
should.exist(supplier);
|
||||
supplier.account.create({accountNo: 'a01'}, function (err, account) {
|
||||
supplier.account(function (e, act) {
|
||||
should.not.exist(e);
|
||||
should.exist(act);
|
||||
act.should.be.an.instanceOf(Account);
|
||||
supplier.account().should.equal(act.id);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('hasAndBelongsToMany', function () {
|
||||
var Article, Tag, ArticleTag;
|
||||
it('can be declared', function (done) {
|
||||
|
|
Loading…
Reference in New Issue