Defining a model

Usually models are defined into separate files, so you can import (using require) the ones you need.

To define your model, you have to use ziti.define:

var ziti = require('ziti');

var Animal = ziti.define('Animal', {
    type: ziti.String
});

module.exports = Animal;

It will be mapped to a table animal

Field Type Null Key Default Extra
type varchar(255) YES NULL
id int(11) NO PRI NULL auto_increment

Since, it has to reflect a MySQL table, you can add options for each field:

var Animal = ziti.define('Animal', {
    type: ziti.String().notNull().unique()
});

Options

When defining a model, you can specify additional options as the third parameter. Take a look at the API for more information.

Primary key

You can also explicitly set the primary key yourself:

var Animal = ziti.define('Animal', {
    animal_id: ziti.Int().notNull().autoIncrement().primaryKey(),
    type: ziti.String
});

Or by simply set it in the model options:

var Animal = ziti.define('Animal', {
    type: ziti.STRING
}, {
    autoId: 'animal_id'
});

You can also prevent the creation of any primary key by setting autoId to false

The primary key can be multiple fields:

var Animal = ziti.define('Animal', {
    animal_id: ziti.Int().notNull().autoIncrement().primaryKey(),
    type: ziti.String().primaryKey().notNull()
});
Field Type Null Key Default Extra
animal_id int(11) NO PRI NULL auto_increment
type varchar(255) NO PRI NULL

It also works with foreign keys:

var AnimalRelationship = ziti.define('AnimalRelationship', {
    animal: ziti.ForeignKey(Animal).primaryKey(),
    owner: ziti.ForeignKey(User).primaryKey()
});

Default field value

You can speficy a default value for a field by setting the default option.

var Animal = ziti.define('Animal', {
    type: ziti.String().default(null)
});

It can also be a function. In that case, it will be evaluated before inserting data to the associated model table.

var Animal = ziti.define('Animal', {
    type: ziti.String().default(function () { return new Date().getTime(); })
});

Default values are set to model instances when not explicitly set.

Inserting data

To insert data into the model table, you can simply call save with your data:

Animal.save({ type: 'cat' });

or multiple:

Animal.save([ { type: 'cat' }, { type: 'dog' } ]);

It returns a promise fullfilled with an Animal instance or an array of Animal instances.

Retrieving data

When retrieving data, it results model instances.

at

This retrieves only one result, so it returns a single model instance or null if not found:

Animal.at({ type: 'cat' }).then(function (animal) {
    // SELECT `Animal`.`id`, `Animal`.`type` FROM `animal` `Animal` WHERE `Animal`.`type` = 'cat' LIMIT 1
    console.log(animal.raw());
    // { id: 1, type: 'cat' }
});

For conveniency, you can simply use the Promise#call method to have raw data instead of a model instance:

Animal.at({ type: 'cat' }).raw().then(function (animal) {
    console.log(animal);
    // { id: 1, type: 'cat' }
});

all

This allows to retrieve data as an array of model instances:

Animal.all({ id: { $lt: 10 } }).then(function (animals) {
    // SELECT `Animal`.`id`, `Animal`.`type` FROM `animal` `Animal` WHERE `Animal`.`id` > 10
    console.log(animals.forEach(function (animal) { return animal.raw(); }));
    /*
      [
        { id: 11, type: 'cat' },
        { id: 12, type: 'dog' },
        { id: 13, type: 'fish' }
      ]
    */
});

Filter the attributes

You may want to retrieve a subset of attributes.

Note that primary keys are always included to help identifying the data.

Scopes

You can define a scope within your Model definition by using the $ function.

var Animal = ziti.define('Animal', {
    type: ziti.String().default(null).$('profile'),
    name: ziti.String().notNull().$('profile'),
    age: ziti.Int().default(0).$('profile'),
    creation_date: ziti.Datetime().default(ziti.NOW)
});

Here a scope named profile has been defined and it includes fields: type, name, and age. It is now possible to retrieve only data included into this scope, so this excludes the field creation_date:

Animal.all({ age: { $lt: 0 } }).$('profile')
    .then(function (animals) {
        // SELECT `Animal`.`id`, `Animal`.`type`, `Animal`.`name`, `Animal`.`age`
        // FROM `animal` `Animal`
        // WHERE `Animal`.`age` > 0
    });

Notice that you can define multiple scopes by fields.

Specifying fields

You can use only to only get some fields:

Animal.all({ age: { $lt: 0 } }).only('type', 'creation_date')
    .then(function (animals) {
        // SELECT `Animal`.`id`, `Animal`.`type`, `Animal`.`creation_date`
        // FROM `animal` `Animal`
        // WHERE `Animal`.`age` > 0
    });

Additional or less fields

For additional fields, use with:

Animal.all({ age: { $lt: 0 } }).only('type', 'creation_date').with('age')
    .then(function (animals) {
        // SELECT `Animal`.`id, `Animal`.`type`, `Animal`.`creation_date`, `Animal`.`age`
        // FROM `animal` `Animal`
        // WHERE `Animal`.`age` > 0
    });

To exclude fields, use without:

Animal.all({ age: { $lt: 0 } }).only('type', 'creation_date', 'age').without('age')
    .then(function (animals) {
        // SELECT `Animal`.`id, `Animal`.`type`, `Animal`.`creation_date`
        // FROM `animal` `Animal`
        // WHERE `Animal`.`age` > 0
    });

This is mainly useful when it's combined with a scope or to retrieve association of associations (see here for more details):

Animal.all({ age: { $lt: 0 } }).$('profile').with('creation_date').without('age')
    .then(function (animals) {
        // SELECT `Animal`.`id`, `Animal`.`type`, `Animal`.`name`, `Animal`.`creation_date`
        // FROM `animal` `Animal`
        // WHERE `Animal`.`age` > 0
    });

Plain objects

By default when retrieving data, model instances are built with these data. To retrieve data as plain objects instead, use raw.

Animal.all({ age: { $lt: 0 } }).raw()
    .then(function (animals) {
         /*
          [
             { id: 1, type: 'lion', name: 'simba', age: 1, creation_date: '...' },
             { id: 2, type: 'lion', name: 'mufasa', age: 10, creation_date: '...' }
          ]
         */
    });

This is equivalent to:

Animal.all({ age: { $lt: 0 } })
    .then(function (animals) {
         return animals.map(function (animal) { return animal.raw(); });
    }).then(function (animals) {
         /*
          [
             { id: 1, type: 'lion', name: 'simba', age: 1, creation_date: '...' },
             { id: 2, type: 'lion', name: 'mufasa', age: 10, creation_date: '...' }
          ]
         */
    });

Except that the second form model instances are built before calling raw

Updating data

This updates rows with new values in the associated database table.

Animal.update({ type: 'horse' }, { $or: [ { type: 'dog' }, { type: 'cat' } ] })
   .then(function () {
        // UPDATE `animal` `Animal` SET `type` = 'horse'
        // WHERE `Animal`.`type` = 'dog' OR `Animal`.`type` = 'cat'
        console.log('Cats and dogs turned into horses! Without any drugs!');
   });

It is also possible to use the at syntax:

Animal.at({ $or: [ { type: 'dog' }, { type: 'cat' } ] }).update({ type: 'horse' })
   .then(function () {
        // UPDATE `animal` `Animal` SET `type` = 'horse'
        // WHERE `Animal`.`type` = 'dog' OR `Animal`.`type` = 'cat'
        console.log('Cats and dogs turned into horses! Without any drugs!');
   });

Removing data

This removes rows in the associated database table.

Animal.remove({ type: 'horse' }).then(function () {
    // DELETE FROM `Animal` USING `animal` `Animal`
    // WHERE `Animal`.`type` = 'horse'
    console.log('Horses are all dead!');
});

Or with the at syntax:

Animal.at({ type: 'horse' }).remove().then(function () {
    // DELETE FROM `Animal` USING `animal` `Animal`
    // WHERE `Animal`.`type` = 'horse'
    console.log('Horses are all dead!');
});

Insert or update

If you want to insert OR update existing rows by using a unique / primary key field, you can use the Model#upsert()

// Here the "type" field is supposed to be a primary key

Animal.upsert({ type: 'squirrel', name: 'scrat' });
// INSERT INTO `animal` SET `type`='squirrel', `name`='scrat' ON DUPLICATE VALUE UPDATE `type`='squirrel', name='scrat'
// This adds a new squirrel named scrat

Animal.upsert({ type: 'squirrel', name: 'alvin' });
// INSERT INTO `animal` SET `type`='squirrel', `name`='alvin' ON DUPLICATE VALUE UPDATE `type`='squirrel', name='alvin'
// This updates the name of the previous squirrel to alvin

Building a model instance

var dog = Animal.build({ type: 'dog' });
console.log('I am a %s', dog.get('type')); // 'dog'
dog.save().call('raw').then(function (dog) {
    // The dog has been inserted into the database and it fills the id field
    console.log(dog); // { id: 1, type: 'dog' }
});

Take a look at model instances for more information.

Custom methods

Model methods

If the method is global to all model, you can use ziti.statics as explained here

If the method is for a specific model, you can directly assign the method to the Model:

Animal.getDogs = function () {
    return this.all({ type: 'dog' });
};

// ...

Animal.getDogs().then(function (dogs) {
   // dogs is an array of dogs
});

If you want to override a method already defined, you can use this.constructor.prototype:

Animal.at = function () {
    console.log('Please! Get me an animal!');
    return this.constructor.prototype.at.apply(this, arguments);
};

Model instance methods

If the method is global to all model instances, you can use ziti.methods as explained here

If the method is for a specific model instance, you can use Model.methods as well:

Animal.methods.sayHello = function () {
    console.log('Hello there!');
};

// ...

Animal.at({ type: 'cat' }).then(function (cat) {
    cat.sayHello(); // Hello there!
});

If you want to override a method already defined, you can use this.constructor.prototype as well:

Animal.methods.refresh = function () {
    console.log('I am young again!');
    return this.constructor.prototype.refresh.apply(this, arguments);
};