前編では、Sequelizeをインスタンス化するために自前でdbconn
モジュールを作って、app.js
はこんな感じにシンプルに書けるようになった。
var express = require('express') , resource = require('express-resource') , config = require('config') , sequelize = require('dbconn')(config) , app = module.exports = express.createServer(); app.resource('users', require('./routes/users'), { id: 'id' });
実際にSequelizeを使ってDBに対するCRUD操作を行うAPIの実装を書く以前に、まだDBすら存在しないのでmysql
コマンドでDBだけ作っておく。
$ mysql -u root -p mysql> create database test; mysql> grant all privileges on test.* to 'h2plus'@'%' identified by 'password'; mysql> \q
次にテーブルの定義をするわけだが、もうSQL文を直接書くことはない。Sequelizeのimport
メソッドを使えば、モジュールとして実装されたモデルを定義することができる。サンプルとして、以下のSQLで定義されるユーザーテーブルを作ってみよう。
create table users ( username varchar(255), password char(40) );
対応するモデル定義のモジュールはこうなる。
module.exports = function(sequelize, DataTypes) { return sequelize.define('user', { username: DataTypes.STRING , password: 'char(40)' }); };
これをmodels/user.js
というファイルに保存しておく。sequelize.define()
の第一引数に指定しているモデル名は単数形になっているが、Sequelizeが実際に定義するテーブル名は複数形になる。
いま定義したUser
モデルを動的に読み込むmodels
モジュールをlibs/models/index.js
に実装していく。libs
ディレクトリは前編でdbconn
モジュールを実装するときに作ったものだ。
module.exports = function(sequelize) { var fs = require('fs') , path = require('path') , directory = 'models' , models = exports; try { fs.lstatSync(directory); } catch(e) { fs.mkdirSync(directory); } models.__cache = {}; fs.readdirSync(directory).forEach(function(fname) { if(/\.js$/.test(fname)) { var name = path.basename(fname, '.js') , realpath = fs.realpathSync(directory+'/'+fname); models.__defineGetter__(name, function() { if(models.__cache[name] === undefined) { models.__cache[name] = sequelize.import(realpath); } return models.__cache[name]; }); } }); return models; };
これで、models
ディレクトリにテーブル定義モジュールを作るだけで、アプリケーションにモデルを追加することができるようになる。アプリケーションで定義したモデルをDBに反映させるにはSequelizeのsync
メソッドを使う。アプリ開発中にモデルが更新されることは多いだろうから、sync
メソッドの呼び出し自体をAPI化してしておくと便利だ。
sync
メソッドを実装したAPIを用意する前に、今までapp.js
に書いてあったexpress-resource
を使ったコードや、Sequelizeを使うコードをroutes/index.js
にカプセル化してしまおう。app.js
の最終形はこうなる。
var express = require('express') , resource = require('express-resource') , config = require('config') , app = module.exports = express.createServer(); // // Configuration // app.configure(function() { app.set('views', __dirname + '/views'); app.set('view engine', 'ejs'); app.use(express.bodyParser()); app.use(express.cookieParser()); app.use(express.methodOverride()); app.use(app.router); app.use(express.static(__dirname + '/public')); }); app.configure('development', function() { app.use(express.errorHandler({ dumpExceptions: true, showStack: true })); }); app.configure('production', function() { app.use(express.errorHandler()); }); // // Routes // require('./routes')(app, config); // // Launch // app.listen(process.env.PORT || 3000, function() { console.log("Express server listening on port %d in %s mode", app.address().port, app.settings.env); });
冒頭に紹介したコードでは、Sequelizeをapp.js
内でインスタンス化していたが、実際にSequelize(モデル)を使うのはroutes
の中になるので、Sequelizeのインスタンス化に必要なconfig
オブジェクトをroutes
に渡すようにしている。config
オブジェクト自体はアプリケーションの設定を保持するものであり、Sequelize以外のモジュールが必要とする設定が含まれることを考えると、app.js
内でインスタンス化するのが適切だろう。
30行目でまだ存在しないroutes
モジュールを呼び出すコードを先に書いてしまったが、routes
モジュールはroutes/index.js
で以下のように実装している。
module.exports = function(app, config) { var sequelize = require('dbconn')(config) , models = require('models')(sequelize); app.resource('users', require('./users'), { id: 'id' }); };
これでしばらくapp.js
にコーディングすることはなくなるだろう。アプリケーションロジックのメインはroutes
モジュールが請け負うことになる。
結局三部作に(笑)後編に続く…
2 コメント