Backbone.jsのSyncで起こっていること(Colleciton編)


前々回、ModelのSyncについて調べましたが、今回はCollectionです。Syncを伴うCollection特有のメソッドとしてはfetchとcreateでしょうか。ざっくりと説明すると下記です。

・Collection.fetch():url属性に設定されているアドレスへGETメソッドで通信する。サーバーからはモデルのデータが幾つか入った配列が帰ってくることを期待し、その配列でCollection内のデータをマージする。

・Collection.create():createには生のオブジェクトか、サーバーに保存されていないモデルを指定します。すると、Collectionに保存しつつ、サーバーにPOSTメソッドで新規作成の命令を出します。

Collectionは基本的に上記だけなので、Collection内のModelの更新をしたい場合は、1つ1つ取り出してsave(), fetch(), destroy()する感じです。

 



下記に色々とテストしたときのソースを貼り付けます。前々回のソースを元にしているので、実行する場合は下記を差し替えて下さい。
また、コメントアウトで色々と補足をつけています。



サーバー:app.js
----テストコード---から---ここまで---を差し替えて下さい。

// ----------ここからテストコード---------
// GET(read)
// Collection自体がfetchする場合の通信先
app.get('/foo2', function(req, res){
console.log('---------GET: the collection--------');
console.log('query: ',req.query);
// -> query: {}
console.log('params: ',req.params);
// -> params: []
console.log('body: ',req.body);
// -> body: {}

// ...DBなんかを使ったりして、ごにょごにょやる。

// Collectionに含めるオブジェクトの配列で返す。
res.status(200).send([
{id: 1, data: 'before'},
{id: 2, data: 'before'},
{id: 3, data: 'before'}
]);
});

// GET(read)
// Collection内のモデルを参照する場合
app.get('/foo2/:id', function(req, res){
console.log('---GET: a model in the collection---');
console.log('query: ',req.query);
// -> query: {}
console.log('params: ',req.params);
// -> params: [ id: '1' ]
console.log('body: ',req.body);
// -> body: {}

// ...DBなんかを使ったりして、ごにょごにょやる。

res.status(200).send({data: 'after', data2: 'add'});
});

// GET(read)
// ModelにurlRootが指定してある場合、こちらを優先。
app.get('/foo/:id', function(req, res){
console.log('---------GET: the model---------');
console.log('urlRootが指定してある場合');

res.status(200).send({test:'the urlRoot exists.'});
});


// POST(create)
app.post('/foo2', function(req, res){
console.log('---POST: a model in the collection---');
console.log('query: ',req.query);
// -> query: {}
console.log('params: ',req.params);
// -> params: []
console.log('body: ',req.body);
// -> body: { data: 'create', hoge: 'hoge' }

// ...DBなんかを使ったりして、ごにょごにょやる。

// POSTは新規作成なのでサーバーでidを生成して返してあげる。
res.status(201).send({"id": 4});
});

// PUT(update)
app.put('/foo2/:id', function(req, res){
console.log('---PUT: a model in the collection---');
console.log('query: ',req.query);
// -> query: {}
console.log('params: ',req.params);
// -> params: [ id: '4' ]
console.log('body: ',req.body);
// -> body: { data: 'update', hoge: 'hoge', id: 4 }

// ...DBなんかを使ったりして、ごにょごにょやる。

res.status(200).send({});
});

// Delete(delete)
app.delete('/foo2/:id', function(req, res){
console.log('--------collection delete-------');
console.log('query: ',req.query);
// -> query: {}
console.log('params: ',req.params);
// -> params: [ id: '1' ]
console.log('body: ',req.body);
// -> body: {}

// ...DBなんかを使ったりして、ごにょごにょやる。

res.status(200).send({});
});


// ----------ここまで---------


クライアント:client.js
こちらの内容で全て書き換えて下さい。

(function() {

// Backbone.sync Collectionテスト
var TestModel = Backbone.Model.extend({
// このコメントアウトを外すと
// CollectionのurlよりModelのurlRootの方が優先される。
// urlRoot: '/foo',
defaults:{
hoge: 'hoge'
}
});

var TestCollection = Backbone.Collection.extend({
model: TestModel,
url: '/foo2',
parse: function(response){
console.log('---------parse--------');
console.log('collection:',this.toJSON());
// -> collection: []
// parseはサーバーからのレスポンスでマージする前に実行されるので、
// この場合まだCollectionには何もデータが無い状態。

console.log('response:',response);
// -> response: [
// {id: 1, data: 'before'},
// {id: 2, data: 'before'},
// {id: 3, data: 'before'}
// ]
// サーバからのデータはresponseに配列で格納されている。
// このデータを色々といじってreturnすることで初めて
// Collectionにマージされる。
return response
}
});

var testCollection = new TestCollection();

// 例によって非同期を順番に行うため、async.jsを活用する。
async.series([
function(callback){
// GET(read)
testCollection.fetch({
success: function(collection, response, options){
console.log('-----fetch the collection-----');
console.log('success: ', collection.toJSON());
// サーバーからのデータがCollection内に保存されている。
// -> success: [
// {id: 1, data: 'before', hoge: "hoge"},
// {id: 2, data: 'before', hoge: "hoge"},
// {id: 3, data: 'before', hoge: "hoge"}
// ]

// asyncの次の関数へ移る。
callback(null);
},
error: function(collection, response, options){
// エラーの場合(ステータスコードが200以外の場合?)
}
});
},

function(callback){
// GET(read)
// Collection内のモデル1つについてfetchする場合
testCollection.at(0).fetch({
success: function(model, response, options){
console.log('---fetch a model in the collection---');
console.log('success: ', model.toJSON());
// サーバーからの結果はマージされる。
// -> success: Object {id: 1, data: "after",
// hoge: "hoge", data2: "add"}

callback(null);
}
});
},

function(callback){
// POST(create)
// Collectionにmodelを追加しつつ、サーバー側にも保存の指示を出す。
testCollection.create({
data: 'create'
},{
success: function(model, response, options){
console.log('---create a model in the collection--');
console.log('success: ', model.toJSON());
// -> success: Object {data: "create",
// hoge: "hoge", id: 4}

callback(null);
}
});
},

function(callback){
// PUT(update)
// 先のCreateでidを受け取っているので、
// save()するとPUT(update)メソッドで通信する
testCollection.at(3).save({
data: 'update'
},{
success: function(model, response, options){
console.log('---update a model in the collection--');
console.log('success: ', model.toJSON());
// -> success: Object {data: "update",
// hoge: "hoge", id: 4}

callback(null);
}
});
},

function(callback){
// DELETE(delete)
testCollection.at(0).destroy({
success: function(model, response, options){
console.log('---delete a model in the collection--');
console.log('success: ', model.toJSON());
// -> success: Object {id: 1, data: "after",
// hoge: "hoge", data2: "add"}

callback(null);
}
});
},

function(callback){
// modelをdestroyするとサーバー側へ削除命令と
// collection内の該当のmodelを削除する。
console.log(testCollection.toJSON());
// -> [
// {id: 2, data: 'before', hoge: "hoge"},
// {id: 3, data: 'before', hoge: "hoge"},
// {id: 4, data: 'update', hoge: "hoge"}
// ]
callback(null);
},

function(callback){
testCollection.fetch({
reset: true,
success: function(collection, response, options){
console.log('----reset the collection----');
console.log('success: ', collection.toJSON());
// resetオプションをつけると、Collectionを入れ替える。
// -> success: [
// {id: 1, data: 'before', hoge: "hoge"},
// {id: 2, data: 'before', hoge: "hoge"},
// {id: 3, data: 'before', hoge: "hoge"}
// ]

callback(null);
}
});
}
],

// asyncの最後に実行される関数。
function(err, results){
if(err) console.log(err);
else console.log('Completed!');
});

})();


リファレンス読んだだけでは何が起こっているかよくわかりませんでしたが、自分なりにテストしてみて多少どのように通信しているか分かるようになりました。