Ext JS に関しては,新しい記事は Sunvisor Lab. ExtJS 別館 にあります。そちらもよろしくお願いいたします。
08.DirectStore でサーバー更新をかける
2010/03/25 更新
DirectStore でサーバー側のデータを読むことができるのは知っていました。が,更新をかける方法がわからず,Storeの中の更新データを取り出すメソッドを読んで,そのデータをExt.Directの関数をコールすることでサーバー側に渡していました。
でも,ちゃんと設定すれば,DirectStore の save() メソッドをコールするだけで,サーバーに更新をかけることができます。
Extjs/SenchaTouch Perfect Day #002 に参加して,@kotsutsumi さんのお話を聞いてから,泊まったホテルと帰りの新幹線の中でExtJSのソースを読み直し,DirectStore の動作について調べてみました。その結果,DirectStore でデータを扱う場合の方法の全体像がわかってきたので,ここにメモします。
DirectStore の定義
DirectStoreを普通につくるとWriterがありません。更新作業をするためには Writer が必要ですのでJsonWriterを追加してやる必要があります。
var writer = new Ext.data.JsonWriter({ encode: false, writeAllFields: false // write all fields, not just those that changed }); MyStore = Ext.extend(Ext.data.DirectStore, { constructor: function(cfg) { cfg = cfg || {}; MyStore.superclass.constructor.call(this, Ext.apply({ storeId: 'MyStore', writer: writer,
この例では別に宣言してそれをコンフィグに設定していますが,もちろんコンフィグの中でオブジェクトリテラルで定義しても大丈夫です。
DirectProxy の場合は directFn にサーバー側のメソッドをセットすると思っていたのですが,それはやめて,api コンフィグを設定します。
api: { create: Ext.myApp.testdata.create, update: Ext.myApp.testdata.update, read: Ext.myApp.testdata.getStore, destroy: Ext.myApp.testdata.getStore },
サーバー側に4つのメソッドを用意してそれを api にセットします。api の create とか update とかをアクションと言いまして,4つ合わせて CRUD と言います。directFn にセットした場合は,CRUD すべてのアクションに directFn にセットしたメソッドがコールされます。でもこれは現実的ではありません。普通はそれぞれのアクションごとにメソッドを用意することになると思いますので, api にセットします。
CRUDのそれぞれの動きについて
Store の save() メソッドがコールされたときに,Store の更新内容に応じて,create, update ,destroy といった書き込み系のアクションが実行されます。これらは複数実行されることがあります。これらの書き込み系アクションの場合に,サーバーに渡される引数は一つのJsonデータです。サーバー側は受け取ったパラメータをJsonデータとして受け取り,create ならDBに insert,update ならDBを update など,適切に処理するようにします。(このようにサーバーに渡されるのはデータの入ったJsonオブジェクトだけなので,サーバー側でどんなアクションが発生したのかを知るには,メソッドを別なものにしなければなりません。ですから,やはり directFn だけを指定するのは,read しか使わない時ってことになります。)
update アクションが実行された時にサーバーに渡されるJsonデータは,変更のあったレコードのみです。受け取ったサーバーは,そのデータでDBを update します。Writer オブジェクトの writeAllFields コンフィグを true にした場合は,すべてのフィールドがセットされますが,false にすると,変更のあったフィールドとIDフィールドのデータのみがセットされます。
ひとつだけ違うのが読み込み系の read アクションです。read の場合は paramOrder にセットしてあるとおりのパラメータが渡されます。paramOrder は read アクションのための設定で,他のアクションでは使われません。サーバー側の関数がとる引数を paramOrder で指定します。サーバー側がとるパラメータがない(パラメータが0個の)場合は,paramOrder は無視されます。
ここまでの動きを理解して,サーバー側のメソッドを実装します。そうすれば,Store の save() メソッドをコールすれば,DBが更新されますので,クライアント側のコードを書く量がぐっと減ります。
更新系APIで渡されるデータについて
Create, Update, Delete といった更新系の API でサーバーに渡されるデータはどのような形式なのでしょうか。 以下,item とあるのは,root 要素が item である場合です。root 要素が違う名称の場合はそれに置き換えて読んでください。
Delete に渡されるデータ
1件だけの場合: { item: <id> } 複数の場合: { item: [<id1>, <id2>, ...] }
渡されたオブジェクトの item 要素に,削除されたレコードの id がセットされて渡されます。削除されたデータが1件の場合は id そのものが,複数の場合は配列でセットされます。
Create に渡されるデータ
1件だけの場合: { item: < 追加するデータオブジェクト> } 複数の場合] { item: [ < 追加するデータオブジェクト1>, < 追加するデータオブジェクト2>...] }
渡されたオブジェクトの item 要素に,追加されたレコードのオブジェクトがセットされて渡されます。追加されたデータが1件の場合は一つのオブジェクトが,複数の場合にはオブジェクトの配列がセットされます。オブジェクトは,フィールド名: 値 という構成になっています。
Update に渡されるデータ
1件だけの場合: { item: < 追加するデータオブジェクト> } 複数の場合] { item: [ < 追加するデータオブジェクト1>, < 追加するデータオブジェクト2>...] }
渡されたオブジェクトの item 要素に,更新されたレコードのオブジェクトがセットされて渡されます。更新されたデータが1件の場合は一つのオブジェクトが,複数の場合にはオブジェクトの配列がセットされます。オブジェクトは,フィールド名: 値 という構成になっています。Writer の writeAllFields が true の場合には,ストアの全てのフィールドのデータが渡されますが,writeAllFields が false の場合は,変更があったフィールドのデータだけが渡されます。
更新系APIの戻り値について
Create, Update, Delete といった更新系の API では,決まった形式でデータを返してやらないと,ExtJS側でエラーが発生します。これがわかるまで結構ハマりました。
基本としては,
{ success: true, items:[{<処理したレコード1>}, {<処理したレコード2>}, ...] }
という形式のオブジェクトを返します。success 要素は,失敗した場合にはもちろん false をセットしますが,ここに true を返した場合は item 要素も返します。item 要素は, クライアントから渡すデータと同じで,root 要素の名前になります。root 要素が違う名称の場合はそれに置き換えて読んでください。この item 要素は,処理したレコードを返します。Create や Update では,渡されたデータを元にして配列にして返せばいいですし,Delete の場合は,渡されたデータは id のみなので, { id: $id } というようにオブジェクトにして返します。
サンプルコード
サンプルコードを github に用意しました。(恥ずかしいけど)