igo
Version:
Igo is a Node.js Web Framework based on Express
410 lines (314 loc) • 8.7 kB
Markdown
# Igo Model API
The Igo Model API for MySQL is the only part of Igo that is not just the integration of another module.
As you will notice, the syntax was very inspired by [Ruby on Rails Active Record](http://guides.rubyonrails.org/active_record_basics.html).
## MySQL Configuration
This is the default MySQL configuration (`config.mysql`) defined by Igo:
```
config.mysql = {
host : process.env.MYSQL_HOST || 'localhost',
port : process.env.MYSQL_PORT || 3306,
user : process.env.MYSQL_USERNAME || 'root',
password : process.env.MYSQL_PASSWORD || '',
database : process.env.MYSQL_DATABASE || 'igo',
debug : false, // mysql driver debug mode
connectionLimit : 5,
debugsql : false // show sql logs
};
```
This configuration is given to the [node.js driver for mysql](https://github.com/mysqljs/mysql) `mysql.createPool(config.mysql)` function, to initialize the connection pool.
You can override this configuration in your `/app/config.js` file:
```js
if (config.env === 'dev') {
// show sql logs
config.mysql.debugsql = true;
}
```
## Migrations
All the SQL files should be placed in the `/sql` directory, and will be played in the alphabetical order.
The SQL files names must follow this pattern: `YYYYMMDD-*.sql`.
To run the migrations, use:
```js
require('igo').db.migrate();
```
When a migration file has run successfully, it is saved in a `__db_migrations` table so it will not run again next time. (This table is automatically created by the framework.)
### CLI
The Igo CLI provides convenient functions to deal with the database migrations.
```sh
# run migrations
igo db migrate
# reset database (WARNING: data will be lost)
igo db reset
```
## Model Definition
### Basics
```js
const Model = require('igo').Model;
const schema = {
table: 'users',
columns: [
'id',
'email',
'password',
'first_name',
'last_name',
'created_at'
]
};
class User extends Model(schema) {
//override constructor if needed
constructor(values) {
super(values);
}
name() {
return this.first_name + ' ' + this.last_name;
}
};
module.exports = User;
```
The `schema` object defines the table name and the table structure, and how this model can be associated to other models.
### Columns Types
Column types can be specified.
```js
const schema = {
table: 'users',
columns: [
'id',
'first_name',
'last_name',
{name: 'is_validated', type: 'boolean'}
{name: 'details_json', type: 'json', attr: 'details'},
{name: 'pets_array', type: 'array', attr: 'pets'},
]
};
```
`boolean` will automatically be cast as boolean on instance.
`json` columns will automatically be stringified on creation and update and parsed on load (on the instance, the column key is set with the `attr` attribute).
`array` columns will automatically be stringified on creation and update and split on load (on the instance, the column key is set with the `attr` attribute).
### Associations
Add `associations` in the schema declaration.
Use `has_many` for one-to-many relationships, and `belongs_to` for many-to-one relationships.
```js
const Project = require('./Project');
const Country = require('./Country');
const schema = {
// ...
columns: [
'id',
'country_id',
// ...
]
associations: [
// [ type, attribute name, association model, model key, foreign key ('id' if not specified)]
[ 'has_many', 'projects', Project, 'id', 'user_id'],
[ 'belongs_to', 'country', Country, 'country_id' ],
]
};
```
`has_many` can also be used with an array of references.
In the following example, projects_ids should be an array of projects' ids.
```js
const schema = {
// ...
columns: [
'id',
{name: 'projects_ids_json', type: 'json', attr: 'projects_ids'}
// ...
]
associations: () => ([
[ 'has_many', 'projects', require('./Project'), 'projects_ids', 'id'],
])
};
```
### Scopes
Scopes can be used to specify extra queries options.
Scopes are added to the schema declaration.
```js
const schema = {
// ...
scopes: {
default: function(query) { query.order('`created_at` DESC'); },
validated: function(query) { query.where({ validated: true }); }
}
};
```
The `default` scope will be used on all queries.
(Use `.unscoped()` to not use the default scope.)
```js
//Scopes usage
User.scope('validated').list(function(err, validatedUsers) {
//..
});
```
### Callbacks
Callbacks are special hooks functions called by Igo during the life cycle of an Igo Model.
| Callback | Triggered |
|----------|-----------|
| `beforeCreate(callback)` | before object creation |
| `beforeUpdate(values, callback)` | before object update (modified attributes are given in the `values` parameter) |
Example:
```js
class User extends Model(schema) {
// hash password before creation
beforeCreate(callback) {
this.password = PasswordUtils.hash(this.password);
callback();
}
```
## Model API
### Create
```js
// create default user
User.create(function(err, user) {
//
});
// create with specified attributes
User.create({
first_name: 'John',
last_name: 'John',
}, function(err, user) {
console.log('Hi ' + user.first_name);
});
```
If the primary key is an `AUTO_INCREMENT` field, it will be set automatically in the object returned in the callback.
### Find
```js
User.find(id, function(err, user) {
console.log('Hi ' + user.first_name);
});
```
### Update
To update a specific object:
```js
User.find(id, function(err, user) {
user.update({
first_name: 'Jim'
}, function(err, user) {
console.log('Hi ' + user.first_name);
});
});
```
To update several objects:
```js
User.where({
country: 'France'
}). update({
language: 'French'
}, function(err) {
// Users are updated
});
```
To update all objects:
```js
User.update({
first_name: 'Jim'
}, function(err) {
// all users are now named Jim
});
```
### Delete
To delete a specific object:
```js
User.destroy(id, function(err) {
// user was deleted
});
```
```js
User.find(id, function(err, user) {
user.destroy(function(err) {
// user was deleted
});
});
```
```js
User.destroyAll(function(err) {
// all users were deleted
});
```
```js
User.where({first_name: 'Jim'}).destroy(function(err) {
// all users named Jim were deleted
});
```
### List
```js
User.list(function(err, users) {
// users is an array of User objects
});
```
#### Where
Examples:
```js
// filter with attribute values
User.where({type: 'foo', sub_type: 'bar'}).list(function(err, users) { ... });
// filter with sql
User.where('`last_name` IS NOT NULL').list(function(err, users) { ... });
// filter with sql and params
User.where('`created_at` BETWEEN ? AND ?', [date1, date2]).list(function(err, users) { ... });
```
#### Limit
```js
User.limit(10).list(function(err, users) {
// first ten users
console.dir(users);
});
```
#### Order
```js
User.order('`last_name` DESC').list(function(err, users) {
console.dir(users);
});
```
### Associations loading
The `includes()` function is used to eager load the objects' associations
```js
// include one association
User.includes('country']).first( ... );
// include multiple associations
User.includes(['country', 'projects']).first( ... );
// include nested associations (load projects's lead and tasks for each user)
User.includes({projects: ['lead', 'tasks']}).first( ... );
// mixed associations
User.includes(['country', {projects: ['lead', 'tasks']}]).first( ... );
```
### Count
`count()` allows you to count rows.
```js
User.count(function(err, count) {
// count all users
console.dir(count);
});
User.where({first_name: 'john'}).count(function(err, count) {
// count all users named John
console.dir(count);
});
```
### Distinct
```js
User.distinct('first_name').list(function(err, first_names) {
// list all distinct user first names
console.dir(first_names);
});
User.distinct([ 'first_name', 'last_name' ]).list(function(err, first_names) {
// list all distinct user first and last names combinations
console.dir(first_names);
});
```
### Select
`select()` allows you to customize `SELECT` (set by default to `SELECT *`).
```js
User.select('id, first_name').list(function(err, users) {
// select only id and first_name columns
console.dir(users);
});
User.select('*, YEAR(created_at) AS `year`').list(function(err, users) {
// add year (from created_at column) in user
console.dir(users);
});
```
### Group
```js
User.select('COUNT(*) AS `count`, YEAR(created_at) AS `year`').group('year').list(function(err, groups) {
// return users count by creation year
console.dir(groups);
});
```