アシアルブログ

アシアルの中の人が技術と想いのたけをつづるブログです

続・Flex4とsymfonyを連携させて遊んでみた。

屋内の寒さと、屋外の暑さの差で完全に夏バテ気味の橋本です、こんにちは。

早く秋がきてほしいですね。
秋いいよ、秋…。

はい。というわけで、みなさんも体調管理には気をつけてくださいませ。

さて、今回は前回に引き続き、Flex4 × Symfonyのお話です。

前回はsfAmfPluginの使い方と、データの保存処理について書きました。今回は引き続きデータの取得について書いていこうと思います。

(ちなみに、sfAmfPluginの2009/07/22現在の最新版はver.1.4.1です。前回使っていたver.1.3.0のバグがいくつか修正されているようなので、皆様こちらを使用しましょう。)

データの取得についても、前回と同様に、まずsymfony側でサービスのメソッドの作成をします。

とりあえず、前回の記事で作成した保存用のメソッドに加え、取得用のメソッドを作成します。

サービス


<?php

// lib/services/frontend/user/RegisterService.class.php

class RegisterService extends sfAmfService {
  // ユーザデータを保存する
  public function saveUserData($user_data) {
    $user = new User();
    $user->fromArray($user_data);
    $user->save();

    return 'OK';
  }

  // ユーザデータを取得する
  /**
     * @AmfReturnType("ArrayCollection")
     */
  public function getUserData() {
    $user_list = Doctrine::getTable('User')->getUserData();
    return $user_list;
  }
}


モデル


<?php

// lib/model/doctrine/UserTable.class.php

class UserTable extends Doctrine_Table
{
  public function getUserData() {
    $ret = array();

    $rec = $this->createQuery('u')
      ->execute();

    foreach ($rec as $user) {
      $ret[] = array($user->id, $user->name, $user->email, $user->phone);
    }
    return $ret;
  }
}


RegisterService.class.phpの保存用メソッド、getUserData()のコメント。
このコメントはアノテーションになっており、Flex側で受け取った際の戻り値の型を指定することができます。
今回は、戻り値をDataGridの中で使用するため、AllayCollection型で返します。

次にFlex側です。
ソースはこんな感じ。(またまた適当ですみません。)



<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
	           xmlns:s="library://ns.adobe.com/flex/spark"
	           xmlns:mx="library://ns.adobe.com/flex/halo"
	           minWidth="1024"
	           minHeight="768"
	           creationComplete="loadUserData()">
<fx:Script>
<![CDATA[
	import mx.rpc.AsyncResponder;
	import mx.rpc.AsyncToken;
	import mx.collections.ArrayCollection;
	import mx.automation.IAutomationObject;
	import mx.controls.Alert;
	import mx.rpc.remoting.RemoteObject;
	import mx.rpc.events.FaultEvent;
	import mx.rpc.events.ResultEvent;
   	
   	[Bindable]
	private var userDataList:ArrayCollection;
	
   	private function loadUserData():void {
   		// サービスの呼び出し
   		var token:AsyncToken = remote.getUserData();
   		// ResultEventとFaultEventの処理を登録
        token.addResponder(new AsyncResponder(successLoadFunc, faultLoadFunc));
   	}
   	
   	private function saveUserData():void {
   		// フォームで取得したデータを入れる配列
   		var data:Array = new Array();
   		
   		data['name'] = nameForm.text;
   		data['email'] = emailForm.text;
   		data['phone'] = phoneForm.text;
   		
   		// サービスの呼び出し
   		var token:AsyncToken = remote.saveUserData(data);
   		// ResultEventとFaultEventの処理を登録
   		token.addResponder(new AsyncResponder(successSaveFunc, faultSaveFunc));
   	}
   	
   	private function successLoadFunc(e:ResultEvent,obj:Object = null):void {
		// 明示的にキャストしないとエラーが出て、FlashBuilderさんがコンパイルしてくれません…
		userDataList = ArrayCollection(e.result);
	}
	
	private function faultLoadFunc(e:FaultEvent,obj:Object=null):void{
		Alert.show(e.toString(), "Error");
	}
	
	private function successSaveFunc(e:ResultEvent,obj:Object = null):void {
		Alert.show(e.result.toString(), "Registerd");
		// フォームをリセットして、データを再読み込みします。
		resetUserData();
		loadUserData();
	}
	
	private function faultSaveFunc(e:FaultEvent,obj:Object=null):void{
		Alert.show(e.toString(), "Error");
	}
	
	private function resetUserData():void {
		nameForm.text = "";
		emailForm.text = "";
		phoneForm.text = "";
	}

]]>
</fx:Script>

<fx:Declarations>
<mx:RemoteObject 
	id="remote"
	endpoint="http://amf_sample/amfgateway/amf"
	source="frontend.user.RegisterService"
	destination="hoge" 
	showBusyCursor="true"
/>	
</fx:Declarations>
	<mx:TabNavigator x="80" y="35" width="485" height="430">
		<mx:Canvas label="save" width="100%" height="100%">
			<s:Button x="80" y="270" label="Save" id="btnSaveUserData" click="saveUserData()"/>
			<s:Button x="200" y="270" label="reset" id="btnResetUserData" click="resetUserData()"/>
			<mx:Label x="50" y="40" text="Name"/>
			<mx:Label x="50" y="120" text="Email"/>
			<mx:Label x="50" y="200" text="Phone"/>
			<s:TextInput x="150" y="35" id="nameForm" width="240"/>
			<s:TextInput x="150" y="120" id="emailForm" width="240"/>
			<s:TextInput x="150" y="200" id="phoneForm" width="240"/>
		</mx:Canvas>
		<mx:Canvas label="Load" width="100%" height="100%">
			<mx:DataGrid x="20" y="20" height="340" width="450" dataProvider="{userDataList}">
				<mx:columns>
					<mx:DataGridColumn headerText="ID" dataField="0"/>
					<mx:DataGridColumn headerText="Name" dataField="1"/>
					<mx:DataGridColumn headerText="Email" dataField="2"/>
					<mx:DataGridColumn headerText="Phone" dataField="3"/>
				</mx:columns>
			</mx:DataGrid>
		</mx:Canvas>
	</mx:TabNavigator>
</s:Application>


タブナビゲータを使って、入力画面と表示画面を分けてあります。

Flex側のポイントはAsyncTokenクラスです。
RemoteObjectを使ってサーバのサービスを呼び出すと、ResultEventやFaultEventにレスポンスが返ってきます。そのレスポンスを受けるのがAsyncTokenクラスになります。(なんだか説明がわかりにくくてすみません。)
また、AsyncTokenのインスタンスには、addResponderメソッドを使ってレスポンダを登録することができます。レスポンダ(AsyncResponderクラス)には、レスポンスがresultの場合の処理と、faultの場合のメソッドをそれぞれ登録することができます。

サービスからの戻り値は、ResultEvent、FaultEventのresultというプロパティに格納されています。

その他は簡単です。サービスから取得したデータをDataGridのdataProviderに指定した変数(今回はArrayCollection型。Array型でも大丈夫です。)に入れてるだけ。

では、早速動かしてみます。

まずは、前回作った登録側から。

最初はデータがありません。



データを登録します。



保存成功。



データを確認。更新されています。やったね。




というわけで、二回にわたって書いてきたFlex4 x Symfony。よく分からないエラーに悩まされた日々もありましたが、結果的にデータの登録と取得が問題なく出来るようになりました。ここまで出来れば、あとは応用でいろいろと面白いことができそうです。

意外と簡単ですので、皆さんも是非やってみてください。


※ちなみに、前回の作成例とコードが違うのは、間違って前回のデータを消してしまったためです。泣きました。皆さんも気をつけてください…。