Base class for all models.
This is used through patio.addModel, NOT directly.
Extends Static PropertiesProperty | Type | Default Value | Description |
MANY_TO_MANY | property | "manyToMany" | String for to signify an association as many to many. |
MANY_TO_ONE | property | "manyToOne" | String for to signify an association as many to one. |
ONE_TO_MANY | property | "oneToMany" | String for to signify an association as one to many. |
ONE_TO_ONE | property | "oneToOne" | String for to signify an association as one to one. |
camelize | {Boolean} |
false | Set to true if this models column names should be use the "underscore" method when sending keys to the database and to "camelize" method on columns returned from the database. If set to false see patio.Model#underscore. |
columns | String[] | A list of columns this models table contains. | |
dataset | patio.Dataset | A dataset to use to retrieve instances of this {@link patio.Model{ from the database. The dataset has the {@link patio.Dataset#rowCb} set to create instances of this model. | |
db | patio.Database | The database all instances of this patio.Model use. | |
emitOnAssociationAdd | property | true
| Set to false to prevent an event from being emitted when an association is added to the model |
emitOnColumnSet | property | true
| Set to false to prevent the emitting of an event on the setting of a column value |
emitOnLoad | property | true
| Set to false to prevent the emitting of an event on load |
emitOnRemove | property | true
| Set to false to prevent the emitting on an event when a model is removed. |
emitOnSave | property | true
| Set to false to prevent the emitting on an event when a model is saved. |
emitOnUpdate | property | true
| Set to false to prevent the emitting on an event when a model is updated. |
fetchType | property | fetch | |
identifierInputMethod | property | null
| |
identifierOutputMethod | property | null
| |
isRestrictedPrimaryKey | property | false
| Set to false to allow the setting of primary keys. |
patio | function | A reference to the global patio. | |
primaryKey | function | The primaryKey column/s of this patio.Model | |
raiseOnTypecastError | property | true
| Set to false to prevent errors thrown while type casting a value from being propogated. |
reloadOnSave | property | true
| Set to false to prevent the reload of a model after saving. |
reloadOnUpdate | property | true
| Set to false to prevent the reload of a model after updating. |
restrictedColumns | property | null | Columns that should be restriced when setting values through the patio.Model#set method. |
schema | Object | The schema of this patio.Model's table. See patio.Database#schema for details on the schema object. | |
table | property | null | The table that this Model represents. READ ONLY |
tableName | String | The name of the table all instances of the this patio.Model use. | |
typecastEmptyStringToNull | property | true
| Set to false to prevent empty strings from being type casted to null |
typecastOnAssignment | property | true
| Set to false to prevent properties from being type casted when manually set. See patio.Database#typecastValue |
typecastOnLoad | property | true
| Set to false to prevent properties from being type casted when loaded from the database. See patio.Database#typecastValue |
underscore | {Boolean} |
false | Set to true if this models column names should be use the "camelize" method when sending keys to the database and to "underscore" method on columns returned from the database. If set to false see patio.Model#underscore. |
useTransactions | property | true | Set to false to prevent models from using transactions when saving, deleting, or updating. This applies to the model associations also. |
Property | Type | Default Value | Description |
__isChanged | property | false | Signifies if the model has changed |
__isNew | property | true | Whether or not this model is new |
associations | function | List of associations on the patio.Model | |
columns | String[] | a list of columns this models table contains. | |
dataset | patio.Dataset | a dataset to use to retrieve models from the database. The dataset has the patio.Dataset#rowCb set to create instances of this model. | |
hasAssociations | function | Returns true if this patio.Model has associations. | |
isChanged | Boolean | true if the model has been changed and not saved. | |
isNew | Boolean | true if this model is new and does not exist in the database. | |
patio | patio | null | patio - read only |
primaryKeyValue | * | the value of this models primaryKey | |
schema | Object | the schema of this models table. | |
tableName | String | the table name of this models table. | |
type | String | null | The database type such as mysql |
function (options,fromDb){ if (this.synced) { this.__emitter = new EventEmitter(); this._super(arguments); this.patio = patio || require("./index"); fromDb = isBoolean(fromDb) ? fromDb : false; this.__changed = {}; this.__values = {}; if (fromDb) { this._hook("pre", "load"); this.__isNew = false; this.__setFromDb(options, true); if (this._static.emitOnLoad) { this.emit("load", this); this._static.emit("load", this); } } else { this.__isNew = true; this.__set(options); } } else { throw new ModelError("Model " + this.tableName + " has not been synced"); } }
Associates a related model with the current model. The following types are supported:
the type of association that is to be created.
the name of the association, the name specified will be exposed as a property on instances of the model.
additional options. The following options can be supplied:
ManyToMany additional options:
optional function to filter the dataset after all other options have been applied.
function (type,name,options,filter){ if (comb.isFunction(options)) { filter = options; options = {}; } this.__associations = this.__associations || {manyToMany: {}, oneToMany: {}, manyToOne: {}, oneToOne: {}}; var assoc = new associations[type](comb.merge({model: name}, options), this.patio, filter); assoc.inject(this, name); this.__associations[type][name] = assoc; this.emit("associate", type, this); return this; }
Create a new model initialized with the specified values.
Argumentsthe values to initialize the model with.
Model
instantiated model initialized with the values passed in.
function (values){ //load an object from an object var Self = this; return new Self(values, false); }
Finds a single model according to the supplied filter. See patio.Dataset#filter for filter options.
Argumentsfunction (id){ return this.filter.apply(this, arguments).first(); }
Retrieves a record by the primaryKey/s of a table.
Examplevar User = patio.getModel("user"); User.findById(1).chain(function(userOne){ }); //assume the primary key is a compostie of first_name and last_name User.findById(["greg", "yukon"]).chain(function(userOne){ });Arguments
the primary key of the record to find.
comb.Promise
called back with the record or null if one is not found.
function (id){ var pk = this.primaryKey; pk = pk.length === 1 ? pk[0] : pk; var q = {}; if (isArray(id) && isArray(pk)) { if (id.length === pk.length) { pk.forEach(function (k, i) { q[k] = id[i]; }); } else { throw new ModelError("findById : ids length does not equal the primaryKeys length."); } } else { q[pk] = id; } return this.filter(q).one(); }
Finds a single model according to the supplied filter. See patio.Dataset#filter for filter options. If the model does not exist then a new one is created as passed back.
Argumentsfunction (q){ var self = this; return this.find(q).chain(function (res) { return res || self.create(q); }); }
Stub for plugins to notified of model inheritance
Argumentsa model class to inherit from
function (model){ }
Creates a MANY_TO_MANY relationship between models. See patio.plugins.AssociationPlugin.associate for options.
Examplepatio.addModel("class", { static:{ init:function(){ this._super("arguments"); this.manyToMany("students", {fetchType:this.fetchType.EAGER, order : [sql.firstName.desc(), sql.lastName.desc()]}); } } }); patio.addModel("student", { instance:{ enroll:function(clas){ if (comb.isArray(clas)) { return this.addClasses(clas); } else { return this.addClass(clas); } } }, static:{ init:function(){ this._super("arguments"); this.manyToMany("classes", {fetchType:this.fetchType.EAGER, order : sql.name.desc()}); } } });Arguments
function (name,options,filter){ return this.associate(this.MANY_TO_MANY, name, options, filter); }
Creates a MANY_TO_ONE association. See patio.plugins.AssociationPlugin.oneToMany. See patio.plugins.AssociationPlugin.associate
Argumentsfunction (name,options,filter){ return this.associate(this.MANY_TO_ONE, name, options, filter); }
Creates a ONE_TO_MANY association. See patio.plugins.AssociationPlugin.associate for options.
Example//define the BiologicalFather model patio.addModel("biologicalFather", { static:{ init:function () { this._super("arguments"); this.oneToMany("children"); } } }); //define Child model patio.addModel("child", { static:{ init:function () { this._super("arguments"); this.manyToOne("biologicalFather"); } } });Arguments
function (name,options,filter){ return this.associate(this.ONE_TO_MANY, name, options, filter); }
Creates a ONE_TO_ONE relationship between models. See patio.plugins.AssociationPlugin.associate for options.
Examplepatio.addModel("state", { static:{ init:function () { this._super("arguments"); this.oneToOne("capital"); } } }); patio.addModel("capital", { static:{ init:function () { this._super("arguments"); this.manyToOne("state"); } } });Arguments
function (name,options,filter){ return this.associate(this.ONE_TO_ONE, name, options, filter); }
Remove(delete) models. This can be used to do a mass delete of models.
Examplevar User = patio.getModel("user"); //remove all users User.remove(); //remove all users who's names start with a. User.remove({name : /A%/i}); //remove all users who's names start with a, without a transaction. User.remove({name : /A%/i}, {transaction : false});Arguments
query to filter the rows to remove. See patio.Dataset#filter.
additional options.
Boolean
: boolean indicating if a transaction should be used when removing the models.
true
] Boolean
: boolean set to prevent the loading of each model. This is more efficient but the pre/post remove hooks not be notified of the deletion.
comb.Promise
called back when the removal completes.
function (q,options){ options = options || {}; var loadEach = isBoolean(options.load) ? options.load : true; //first find all records so we call alert the middleware for each model var ds = this.dataset.filter(q); return this._checkTransaction(options, function () { if (loadEach) { return ds.map(function (r) { //todo this sucks find a better way! return r.remove(options); }); } else { return ds.remove(); } }); }
Similar to remove but takes an id or an array for a composite key.
ExampleUser.removeById(1);Arguments
id or an array for a composite key, to find the model by
additional options.
Boolean
: boolean indicating if a transaction should be used when removing the model.
comb.Promise
called back when the removal completes.
function (id,options){ var self = this; return this._checkTransaction(options, function () { return self.findById(id).chain(function (model) { return model ? model.remove(options) : null; }); }); }
Save either a new model or list of models to the database.
Examplevar Student = patio.getModel("student"); Student.save([ { firstName:"Bob", lastName:"Yukon", gpa:3.689, classYear:"Senior" }, { firstName:"Greg", lastName:"Horn", gpa:3.689, classYear:"Sohpmore" }, { firstName:"Sara", lastName:"Malloc", gpa:4.0, classYear:"Junior" }, { firstName:"John", lastName:"Favre", gpa:2.867, classYear:"Junior" }, { firstName:"Kim", lastName:"Bim", gpa:2.24, classYear:"Senior" }, { firstName:"Alex", lastName:"Young", gpa:1.9, classYear:"Freshman" } ]).chain(function(users){ //work with the users }); Save a single record MyModel.save(m1);Arguments
additional options.
patio.Model|Object|patio.Model[]|Object[]
: the record/s to save.
Boolean
: boolean indicating if a transaction should be used when saving the models.
comb.Promise
called back with the saved record/s.
function (items,options){ options = options || {}; var isArr = isArray(items), Self = this; return this._checkTransaction(options, function () { return asyncArray(items) .map(function (o) { if (!isInstanceOf(o, Self)) { o = new Self(o); } return o.save(null, options); }) .chain(function (res) { return isArr ? res : res[0]; }); }); }
Update multiple rows with a set of values.
Examplevar User = patio.getModel("user"); //BEGIN //UPDATE `user` SET `password` = NULL WHERE (`last_accessed` <= '2011-01-27') //COMMIT User.update({password : null}, function(){ return this.lastAccessed.lte(comb.date.add(new Date(), "year", -1)); }); //same as User.update({password : null}, {lastAccess : {lte : comb.date.add(new Date(), "year", -1)}}); //UPDATE `user` SET `password` = NULL WHERE (`last_accessed` <= '2011-01-27') User.update({password : null}, function(){ return this.lastAccessed.lte(comb.date.add(new Date(), "year", -1)); }, {transaction : false});Arguments
a hash of values to update. See patio.Dataset#update.
additional options.
a filter to apply to the UPDATE. See patio.Dataset#filter.
Boolean
: boolean indicating if a transaction should be used when updating the models.
Promise
a promise that is resolved once the update statement has completed.
function (vals,/*?object*/query,options){ options = options || {}; var dataset = this.dataset; return this._checkTransaction(options, function () { if (!isUndefined(query)) { dataset = dataset.filter(query); } return dataset.update(vals); }); }
set a transaction property to override the patio.Model#useTransaction.
function (opts){ opts = opts || {}; return isBoolean(opts.transaction) ? opts.transaction === true : this.useTransactions === true; }
Forces the reload of the data for a particular model instance. The Promise returned is resolved with the model.
ExamplemyModel.reload().chain(function(model){ //model === myModel });Returns
comb.Promise
resolved with the model instance.
function (){ if (!this.__isNew) { var self = this; return this.dataset.naked().filter(this._getPrimaryKeyQuery()).one().chain(function (values) { self.__setFromDb(values, true); return self; }); } else { return when(this); } }
function (options){ options || (options = {}); var reload = isBoolean(options.reload) ? options.reload : this._static.reloadOnSave; return reload ? this.__reload() : when(this); }
function (options){ options = options || {}; options || (options = {}); var reload = isBoolean(options.reload) ? options.reload : this._static.reloadOnUpdate; return reload ? this.__reload() : when(this); }
This method removes the instance of the model. If the model patio.Model#isNew then the promise is resolved with a 0 indicating no rows were affected. Otherwise the model is removed, primary keys are cleared and the model's isNew flag is set to true.
ExamplemyModel.remove().chain(function(){ //model is deleted assert.isTrue(myModel.isNew); }); //dont use a transaction to remove this model myModel.remove({transaction : false}).chain(function(){ //model is deleted assert.isTrue(myModel.isNew); });Arguments
additional options.
Boolean
: boolean indicating if a transaction should be used when removing the model.
comb.Promise
called back after the deletion is successful.
function (options){ if (this.synced) { if (!this.__isNew) { var self = this; return this._checkTransaction(options, function () { return self.__callHooks("remove", [options], function () { return self._remove(options); }) .chain(function () { self._clearPrimaryKeys(); self.__isNew = true; if (self._static.emitOnRemove) { self.emit("remove", this); self._static.emit("remove", this); } }) .chain(function () { return self; }); }); } else { return when(0); } } else { throw new ModelError("Model " + this.tableName + " has not been synced"); } }
Saves a model. This action checks if the model is new and values have changed. If the model is not new then the patio.Model#update action is called.
When saving a model you can pass values you want set as the first argument.
someModel.save({ myVal1 : "newValue1", myVal2 : "newValue2", myVal3 : "newValue3" }).chain(function(){ //do something }, errorHandler);
Or you can set values on the model directly
someModel.myVal1 = "newValue1"; someModel.myVal2 = "newValue2"; someModel.myVal3 = "newValue3"; //update model with current values someModel.save().chain(function(){ //do something });
Save also accepts an options object as the second argument allowing the overriding of default behavior.
To override useTransactions
you can set the transaction
option.
someModel.save(null, {transaction : false});
You can also override the reloadOnSave
property by setting the reload
option.
someModel.save(null, {reload : false});Arguments
optional values hash to set on the model before saving.
additional options.
Boolean
: boolean indicating if a transaction should be used when saving the model.
Boolean
: boolean indicating if the model should be reloaded after the save. This will take precedence over patio.Model.reloadOnSave
comb.Promise
resolved when the save action has completed.
function (vals,options){ if (this.synced) { if (this.__isNew) { var self = this; return this._checkTransaction(options, function () { if (isHash(vals)) { self.__set(vals); } return self.__callHooks("save", [options], function () { return self._save(options); }) .chain(function () { return self._saveReload(options); }) .chain(function () { if (self._static.emitOnSave) { self.emit("save", self); self._static.emit("save", self); } }) .chain(function () { return self; }); }); } else { return this.update(vals, options); } } else { throw new ModelError("Model " + this.tableName + " has not been synced"); } }
Set multiple values at once. Useful if you have a hash of properties that you want to set.
NOTE: This method will use the static restrictedColumns property of the model.
ExamplemyModel.setValues({firstName : "Bob", lastName : "yukon"}); //this will throw an error by default, assuming id is a pk. myModel.setValues({id : 1, firstName : "Bob", lastName : "yukon"});Arguments
value to set on the model.
patio.Model
return this for chaining.
function (values){ this.__set(values, false); return this; }
Convert this model to JSON, containing column, value pairs.
ReturnsJSON
the JSON version of this model.
function (){ return this.toObject(); }
Convert this model to an object, containing column, value pairs.
ReturnsObject
the object version of this model.
function (){ return this._toObject(false); }
Convert this model to a string, containing column, value pairs.
ReturnsString
the string version of this model.
function (){ return JSON.stringify(this.toObject(), null, 4); }
Updates a model. This action checks if the model is not new and values have changed. If the model is new then the patio.Model#save action is called.
When updating a model you can pass values you want set as the first argument.
someModel.update({ myVal1 : "newValue1", myVal2 : "newValue2", myVal3 : "newValue3" }).chain(function(){ //do something }, errorHandler);
Or you can set values on the model directly
someModel.myVal1 = "newValue1"; someModel.myVal2 = "newValue2"; someModel.myVal3 = "newValue3"; //update model with current values someModel.update().chain(function(){ //do something });
Update also accepts an options object as the second argument allowing the overriding of default behavior.
To override useTransactions
you can set the transaction
option.
someModel.update(null, {transaction : false});
You can also override the reloadOnUpdate
property by setting the reload
option.
someModel.update(null, {reload : false});Arguments
optional values hash to set on the model before saving.
additional options.
Boolean
: boolean indicating if a transaction should be used when updating the model.
Boolean
: boolean indicating if the model should be reloaded after the update. This will take precedence over patio.Model.reloadOnUpdate
comb.Promise
resolved when the update action has completed.
function (vals,options){ if (this.synced) { if (!this.__isNew) { var self = this; return this._checkTransaction(options, function () { if (isHash(vals)) { self.__set(vals); } var saveChange = !isEmpty(self.__changed); return self.__callHooks("update", [options], function () { return saveChange ? self._update(options) : null; }) .chain(function () { return self._updateReload(options); }) .chain(function () { if (self._static.emitOnUpdate) { self.emit("update", self); self._static.emit("update", self); } }) .chain(function () { return self; }); }); } else if (this.__isNew && this.__isChanged) { return this.save(vals, options); } else { return when(this); } } else { throw new ModelError("Model " + this.tableName + " has not been synced"); } }
Convert this model to a string, containing column, value pairs.
ReturnsString
the string version of this model.
function (){ return this.toObject(); }