Ext JS に関しては,新しい記事は Sunvisor Lab. ExtJS 別館 にあります。そちらもよろしくお願いいたします。
09.サーバーサイドでDirectStoreの更新処理を書く
08. で DirectStore でのサーバー更新について調べましたが,それでは実際にサーバー側のルーチンはどう書いたらいいのでしょうか。ここでは,PHPとxFrameworkPXを使った場合を例示します。xFrameworkPXでは,ExtDirectコントローラーを使えば,モジュールのメソッドがそのままExtDirectで使えるという便利な機能があります。ここでは,そのモジュールのメソッドの記述例を示してExtDirectでのサーバー側の実装を示します。ただ,他のフレームワークを使った場合でも,API周りの処理に違いがあるとは思いますが,実際に処理するメソッド自体は大同小異だと思いますので,参考にはなると思います。
クライアント側 JavaScript
DirectStore の定義
次のような DirectStore を定義して,このストアの save メソッドをコールしたときに,ちゃんとサーバー側で更新がかかるようにします。例では autoSave を false にして自動保存されないようにしていますが,自動保存する場合でもほぼ同じです。この例では,保存するときの beforewrite イベントでローディングマスクを表示する処理を加えて, write イベントでマスクを解除しています。API コンフィグで指定しているのは,ExtDirect で公開されているメソッドです。Ext.remote というネームスペースに定義されるようになっています。CRUDそれぞれのメソッドが定義されています。
Create = insertDataメソッド
Read = readDataメソッド
Update = updateData メソッド
Delete = deleteData メソッド
Store へのレコードの追加
ここでは参考までに Store にデータを追加するコードを紹介します。
var newRec; newRec = new me.store.recordType(obj, null); me.store.add( newRec );
このように,recordType でレコードを作り,add メソッドで追加します。上記の例ではobjに追加したいレコードのデータが入っているという前提です。レコードの id プロパティが空の時に ExtJS はそれが追加されたレコードであると判断します。
サーバー側 PHP(xFrameworkPX)
次にサーバー側のコードを書いていきます。
ExtDirect コントローラー
.extdirect.php を webapp フォルダに作成します。
class extdirect extends xFrameworkPX_Controller_ExtDirect { public $direct = array( 'namespace' => 'Ext.remote' ); public $modules = array( 'employee' => array( 'conn' => 'default' ) ); }
ネームスペースを設定して,利用するモジュール(employee)をuseしています。これだけで xFrameworkPX がいろいろと世話をしてくれるので, employee モジュールのメソッドを ExtDirect から利用可能になります。
employee モジュール
class employee extends xFrameworkPX_Model { public $usetable = 'employee'; // メソッドを記述 }
xFrameworkPX でのモジュールは,modules ディレクトリに配置します。
readData メソッド
データを読み出すための readData メソッドを実装します。
public function readData() { $fields = array( 'id', 'name_kanji', 'name_kana', 'birth_date' ); $order = array( 'id' ); $result = $this->get( 'all', array( 'fields' => $fields, 'order' => $order ) ); return array( 'success' => true, 'total' => count( $result ), 'items' => $result ); }
データベースから従業員データをとってきて,その一覧を返しています。返却するデータは最後の return 文のような形式で返します。
この中の items というキー名は任意ですが,このキー名を Store の root コンフィグにセットしなければなりません。他のキー名にした場合には,Store の root コンフィグにその名前をセットします。
deleteData メソッド
destroy アクションが発行された場合に実行されます。この場合,サーバに渡されるオブジェクトの items プロパティに削除すべきレコードの id が渡されます。削除するレコードが複数ある場合には items は配列になります。この例での items となっている要素ですが,この items というキーは前述の通り, root コンフィグに設定されている値になります。root コンフィグの値が違っていればこのキー名も変わります。(この点については他のメソッドでも同様です)
実際の deleteData メソッドのコードです。
public function deleteData( $pData ) { $data = $pData->items; if( ! is_array($data) ){ $data = array($data); } $result = true; foreach( $data as $id ){ $result = $result && $this->_deleteOne( $id ); } return array( 'success' => $result, 'items' => $data ); } private function _deleteOne( $id ) { $where = array( 'id = :id' ); $bind = array( 'id' => $id ); $result = $this->delete( array( 'where' => $where, 'bind' => $bind ) ); return $result; }
サーバーに渡されるのはJSONのデータですが,PHPで受け取ったときには StdObject型のデータになります。配列の場合は配列で受け取れるのですが,オブジェクトの場合は StdObject のデータが渡されます。そこで最初に渡されたデータを配列にキャストしています。
Store の中で一つのレコードが削除された場合と,複数のレコードが削除された場合で,渡されるデータが違います。複数の場合のみデータは配列になっています。渡されたデータが配列であるかどうか判断して,配列でない場合は要素が一つの配列に格納してます。そしてその後,配列の各要素を引数にして _deleteOne メソッドをコールしています。
このメソッドの戻り値ですが,次のような内容を返す必要があります。
{ success: <flag>, items: [ {id : <id1>}, {id: <id2>}, ....] }
success プロパティには実行結果をセットします。true の場合は成功,false の場合は失敗ですね。成功した場合には,items (例によって root コンフィグの値によって変わります)プロパティに,削除されたレコードの id フィールドの値をプロパティに持つオブジェクトの配列をセットします。戻り値の方は1件の削除でも配列にする必要があります。_deleteOne メソッドの最後の方でこの1件ごとのオブジェクトを作成して返しています。deleteData メソッドの最後では,それらの結果の配列と success プロパティをつけて返しています。
渡されるオブジェクトと返すオブジェクトの形式は,update や insert でもほぼ同じですので,よく覚えておいてください。
updateData メソッド
update アクションの時には,更新されたレコードのデータが渡されます。ここでも1レコードの場合は1件のオブジェクトが,複数レコードの場合はオブジェクトの配列が渡されます。Store の writeAllFields コンフィグの値によって,渡されるオブジェクトの内容が変わります。この値が true の場合は,すべてのフィールドが渡されますし,この値が false の場合はキーのフィールドと変更があったフィールドだけが渡されます。
public function updateData( $pData ) { $data = $pData->items; if( ! is_array($data) ){ $data = array($data); } $result = true; foreach( $data as $rec ){ $result = $result && $this->_updateOne( $rec ); } return array( 'success' => $result, 'items' => $data ); } private function _updateOne( $pRec ) { $rec = (array)$pRec; $fields = array( 'name_kanji', 'name_kana', 'birth_date' ); $values = array( ':name_kanji', ':name_kana', ':birth_date' ); $where = array( 'id = :id' ); return $this->update( array( 'field' => $fields, 'value' => $values, 'where' => $where, 'bind' => $rec ) ); }
このメソッドでは渡されたデータをひもといて update メソッドでレコードの更新をかけています。ここでも適切な値を返さなければなりません。やはり success プロパティと items プロパティが必要です。
ToDo:(items が id さえあればいいのかどうか確認。writeAllFields が false の場合も検証)
insertData メソッド
create アクションの時には,追加されたレコードのデータが渡されます。ここでも1レコードの場合は1件のオブジェクトが,複数レコードの場合はオブジェクトの配列が渡されます。戻り値も同様です。
public function addData( $pData ) { $data = $pData->items; if( ! is_array($data) ){ $data = array($data); } $result = true; $rData = array(); foreach( $data as $rec ){ $rRec = (array)$rec; $id = $this->_addOne( $rRec ); $rRec['id'] = $id; $rData[] = $rRec; } return array( 'success' => $result, 'items' => $rData ); } private function _addOne( $rec ) { $fields = array( 'name_kanji', 'name_kana', 'birth_date' ); $values = array( ':name_kanji', ':name_kana', ':birth_date' ); // 挿入実行 $result = $this->insert( array( 'field' => $fields, 'value' => $values, 'bind' => $rec ) ); $id = $this->lastId(); return $id; }
このメソッドでは,id フィールドは auto increment になっているという前提です。
ここのコードは,記事に書くためにちょっと短くしています。こちらのGitHubリポジトリですべてのソースをご覧になれます。