From 0f40cf93b8998062b496b3482d68dfa5ab7f9d85 Mon Sep 17 00:00:00 2001 From: jValdron Date: Tue, 8 Oct 2013 09:09:14 -0300 Subject: [PATCH 1/7] added alwaysValidate added a alwaysValidate option to always do the other validations, even if it's empty and not required --- lib/Instance.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Instance.js b/lib/Instance.js index 68f624b8..818abc1f 100755 --- a/lib/Instance.js +++ b/lib/Instance.js @@ -62,7 +62,7 @@ function Instance(Model, opts) { required = false; if (Model.properties[k]) { - required = Model.properties[k].required; + required = Model.properties[k].alwaysValidate || Model.properties[k].required; } else { for (i = 0; i < opts.one_associations.length; i++) { if (opts.one_associations[i].field === k) { From 90e0e2fa77f64955a0c34086a84e9ba6972d8769 Mon Sep 17 00:00:00 2001 From: jValdron Date: Tue, 8 Oct 2013 09:23:14 -0300 Subject: [PATCH 2/7] added alwaysValidate on hasOne associations --- lib/Instance.js | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/lib/Instance.js b/lib/Instance.js index 818abc1f..248154e0 100755 --- a/lib/Instance.js +++ b/lib/Instance.js @@ -29,7 +29,7 @@ function Instance(Model, opts) { var pending = [], errors = [], required; Hook.wait(instance, opts.hooks.beforeValidation, function (err) { - var k, i; + var k, i, a; if (err) { return saveError(cb, err); } @@ -65,10 +65,12 @@ function Instance(Model, opts) { required = Model.properties[k].alwaysValidate || Model.properties[k].required; } else { for (i = 0; i < opts.one_associations.length; i++) { - if (opts.one_associations[i].field === k) { - required = opts.one_associations[i].required; - break; - } + for (a in opts.one_associations[i].field) { + if (a === k) { + required = opts.one_associations[i].alwaysValidate || opts.one_associations[i].required; + break; + } + } } } if (!required && instance[k] == null) { From 8487e48e433a3d24373d015739ee844cc54fbdf1 Mon Sep 17 00:00:00 2001 From: jValdron Date: Tue, 8 Oct 2013 16:15:41 -0300 Subject: [PATCH 3/7] Update Readme.md --- Readme.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Readme.md b/Readme.md index 3102c9ed..e239933c 100755 --- a/Readme.md +++ b/Readme.md @@ -586,7 +586,10 @@ See information in the [wiki](https://github.com/dresende/node-orm2/wiki/Model-V ## Associations -An association is a relation between one or more tables. +An association is a relation between one or more tables. This version of node-orm2 creates foreign keys with MySQL for hasOne associations. + +### foreign keys with hasOne +`Animal.hasOne('owner', Person, { onDelete: 'cascade' })` (or **set null**, or **restrict**) ### hasOne From 396d7708e5f16377804e2d3d47a52e88d69fa9a3 Mon Sep 17 00:00:00 2001 From: jValdron Date: Tue, 8 Oct 2013 16:16:23 -0300 Subject: [PATCH 4/7] added hasOne foreign keys in MySQL --- lib/ORM.js | 46 +++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 41 insertions(+), 5 deletions(-) diff --git a/lib/ORM.js b/lib/ORM.js index 65df1023..7e102f40 100644 --- a/lib/ORM.js +++ b/lib/ORM.js @@ -283,11 +283,15 @@ ORM.prototype.load = function () { return loadNext(); }; -ORM.prototype.sync = function (cb) { +ORM.prototype.sync = function (constraints, cb) { var modelIds = Object.keys(this.models); var syncNext = function () { if (modelIds.length === 0) { - return cb(); + if (constraints) { + return this.syncConstraints(cb); + } else { + return cb(); + } } var modelId = modelIds.shift(); @@ -303,10 +307,42 @@ ORM.prototype.sync = function (cb) { }); }.bind(this); - if (arguments.length === 0) { - cb = function () {}; - } + if (typeof constraints === 'function') { + cb = constraints; + constraints = false; + } + if (!cb) { + cb = function () {}; + } + + syncNext(); + + return this; +}; +ORM.prototype.syncConstraints = function (cb) { + var modelIds = Object.keys(this.models); + var syncNext = function () { + if (modelIds.length === 0) { + return cb(); + } + + var modelId = modelIds.shift(); + + this.models[modelId].syncConstraints(function (err) { + if (err) { + err.model = modelId; + + return cb(err); + } + + return syncNext(); + }); + }.bind(this); + if (arguments.length === 0) { + cb = function () {}; + } + syncNext(); return this; From df3ca3d9d296f91065c26233d051ba39210695fb Mon Sep 17 00:00:00 2001 From: jValdron Date: Tue, 8 Oct 2013 16:16:39 -0300 Subject: [PATCH 5/7] added hasOne foreign keys in MySQL --- lib/Drivers/DML/mysql.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/Drivers/DML/mysql.js b/lib/Drivers/DML/mysql.js index d73b5d08..971786ae 100755 --- a/lib/Drivers/DML/mysql.js +++ b/lib/Drivers/DML/mysql.js @@ -32,6 +32,10 @@ Driver.prototype.sync = function (opts, cb) { return require("../DDL/mysql").sync(this, opts, cb); }; +Driver.prototype.syncConstraints = function (opts, cb) { + return require("../DDL/mysql").syncConstraints(this, opts, cb); +}; + Driver.prototype.drop = function (opts, cb) { return require("../DDL/mysql").drop(this, opts, cb); }; From 82bea7257a9262339bc290d0f7c504a7384e3c5c Mon Sep 17 00:00:00 2001 From: jValdron Date: Tue, 8 Oct 2013 16:16:57 -0300 Subject: [PATCH 6/7] added hasOne foreign keys in MySQL --- lib/Drivers/DDL/mysql.js | 41 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 40 insertions(+), 1 deletion(-) diff --git a/lib/Drivers/DDL/mysql.js b/lib/Drivers/DDL/mysql.js index 1d521d4d..1f3d16c1 100755 --- a/lib/Drivers/DDL/mysql.js +++ b/lib/Drivers/DDL/mysql.js @@ -70,7 +70,7 @@ exports.sync = function (driver, opts, cb) { "CREATE TABLE IF NOT EXISTS " + driver.query.escapeId(opts.table) + " (" + definitions.join(", ") + ")" ); - + for (i = 0; i < opts.many_associations.length; i++) { definitions = []; @@ -116,6 +116,45 @@ exports.sync = function (driver, opts, cb) { } }; +exports.syncConstraints = function (driver, opts, cb) { + var queries = []; + var k, i, pending; + + for (i = 0; i < opts.one_associations.length; i++) { + if (opts.one_associations[i].extension) continue; + if (opts.one_associations[i].reversed) continue; + for (k in opts.one_associations[i].field) { + queries.push( + "ALTER TABLE " + driver.query.escapeId(opts.table) + " " + + "DROP FOREIGN KEY fk_" + k + "_" + opts.one_associations[i].name + ); + queries.push( + "ALTER TABLE " + driver.query.escapeId(opts.table) + " " + + "ADD CONSTRAINT fk_" + k + "_" + opts.one_associations[i].model.table + " FOREIGN KEY (" + driver.query.escapeId(k) + ") " + + "REFERENCES " + opts.one_associations[i].name + "(`id`) " + + "ON UPDATE " + (opts.one_associations[i].onUpdate ? opts.one_associations[i].onUpdate.toUpperCase() : 'CASCADE') + " " + + "ON DELETE " + (opts.one_associations[i].onDelete ? opts.one_associations[i].onDelete.toUpperCase() : 'RESTRICT') + ); + } + } + + for (i = 0; i < opts.many_associations.length; i++) { + // TODO: build this + } + + pending = queries.length; + + if (!queries.length) return cb(); + + for (i = 0; i < queries.length; i++) { + driver.execQuery(queries[i], function (err) { + if (--pending === 0) { + return cb(err && err.code === 'ER_CANT_CREATE_TABLE' ? null : err); + } + }); + } +}; + var colTypes = { integer: { 2: 'SMALLINT', 4: 'INTEGER', 8: 'BIGINT' }, floating: { 4: 'FLOAT', 8: 'DOUBLE' } From 4f95362e5e7e50e618406b51c9514320de71ddf5 Mon Sep 17 00:00:00 2001 From: jValdron Date: Tue, 8 Oct 2013 16:17:19 -0300 Subject: [PATCH 7/7] added hasOne foreign keys in MySQL --- lib/Model.js | 43 ++++++++++++++++++++++++++++++++----------- 1 file changed, 32 insertions(+), 11 deletions(-) diff --git a/lib/Model.js b/lib/Model.js index 188ba3a9..948c1d41 100644 --- a/lib/Model.js +++ b/lib/Model.js @@ -83,7 +83,7 @@ function Model(opts) { autoFetchLimit : inst_opts.autoFetchLimit, cascadeRemove : inst_opts.cascadeRemove }; - var pending = 2, create_err = null; + var pending = 2; var instance = new Instance(model, { uid : inst_opts.uid, // singleton unique id id : opts.id, @@ -104,12 +104,9 @@ function Model(opts) { association_properties : association_properties }); instance.on("ready", function (err) { - if (--pending > 0) { - create_err = err; - return; - } + if (--pending > 0) return; if (typeof cb === "function") { - return cb(err || create_err, instance); + return cb(err, instance); } }); if (model_fields !== null) { @@ -123,12 +120,9 @@ function Model(opts) { ManyAssociation.autoFetch(instance, many_associations, assoc_opts, function () { ExtendAssociation.autoFetch(instance, extend_associations, assoc_opts, function () { Hook.wait(instance, opts.hooks.afterAutoFetch, function (err) { - if (--pending > 0) { - create_err = err; - return; - } + if (--pending > 0) return; if (typeof cb === "function") { - return cb(err || create_err, instance); + return cb(err, instance); } }); }); @@ -236,6 +230,33 @@ function Model(opts) { return cb(ErrorCodes.generateError(ErrorCodes.NO_SUPPORT, "Driver does not support Model.sync()", { model: opts.table })); }; + model.syncConstraints = function (cb) { + if (arguments.length === 0) { + cb = function () {}; + } + if (typeof opts.driver.syncConstraints === "function") { + try { + opts.driver.syncConstraints({ + extension : opts.extension, + id : opts.id, + table : opts.table, + properties : opts.properties, + allProperties : allProperties, + indexes : opts.indexes || [], + customTypes : opts.db.customTypes, + one_associations : one_associations, + many_associations : many_associations, + extend_associations : extend_associations + }, cb); + } catch (e) { + return cb(e); + } + + return this; + } + + return cb(ErrorCodes.generateError(ErrorCodes.NO_SUPPORT, "Driver does not support Model.syncConstraint()", { model: opts.table })); + }; model.get = function () { var conditions = {};