Mini project : Send SMS with Flex App and Google Calendar

หัวข้อนี้เราจะมาลองทำ Mini project โดยประยุกต์ใช้ Flex ร่วมกับเทคโนโลยีอื่น ซึ่งในที่นี้เราจะใช้ feature ของ Google calendar ที่สามารถส่ง SMS แจ้งเตือนกิจกรรมที่เราบันทึกไว้ได้ มาใช้ประโยชน์ โดยใช้ Library ใน Zend Frameworks ช่วยในการส่งข้อมูลจาก Flex app ไปยัง Google calendar อีกที

(ซึ่งจริงๆ ผมได้รับอนิสงฆ์จากคนที่มาเรียนแล้วเอาความรู้และการใช้งานของเทคโนโลยีตัวนี้มาให้ผมช่วยดูเพื่อเอาไปประยุกต์ใช้ ต้องขอขอบคุณมากๆ ครับ การถ่ายทอดอะไรให้กับคนอื่นก็สามารถทำให้เราได้รับความรู้ได้เช่นกัน)

ขั้นตอนหลักๆ มีดังนี้

1. Google calendar setting

2. Create PHP client access file

3. Create Flex web app

————————————-

1. Google calendar setting

ก่อนอื่นเราต้องมี gmail account ก่อน จากนั้นให้เข้าไปที่ Your_Gmail_Account > Account Settings ดังภาพ

จากนั้นเลือก My product > Calendar – Settings ดังภาพ

จะมาที่หน้า Calendar Settings ให้เลือก Mobile Setting จากนั้นตั้งค่าต่างๆ ดังนี้

Country : Thailand

Phone number : ใส่เบอร์โทรโดยใส่ระหัสประเทศนำหน้า เช่น เบอร์โทร 0879999999 ให้ใส่เป็น +66879999999

จากนั้นกดปุ่ม Send Verification Code ทาง Google ก็จะส่ง SMS เป็นหมายเลขสำหรับ Verify เพื่อรับข้อมูลการแจ้งเตือนจาก Google calendar

————————————-

2. Create PHP client access file

การติดต่อกับ Google Calendar เราจะใช้ Library ใน Zend Frameworks โดยสามารถ ​Download ได้ที่ http://framework.zend.com/download/latest

เมื่อเรา Download มาแล้ว Unzip แล้วนำ Zend Frameworks ไปวางไว้ที่

LocalServerApp
|-frameworks
|    |-Zend
|    |-…
|-your_web_root_directory_such_as_www_or_htdocs_or_public_html_etc

ตัวอย่างในรูป

จากนั้นสร้างไฟล์ PHP client access file ดังนี้

<?php

// Set up debug
error_reporting(E_ALL | E_STRICT);
ini_set("display_errors", "on");

// Set up include path for Zend Framework, this path is assuming a frameworks
// folder contains the Zend package on the same level as your public_html or www folder.
//ini_set("include_path", ini_get("include_path") . ":../frameworks");
//ini_set("include_path", ini_get("include_path") . PATH_SEPARATOR ."../frameworks");
ini_set("include_path", ini_get("include_path") . PATH_SEPARATOR . $_SERVER['DOCUMENT_ROOT'] . "/../frameworks");

require_once 'Zend/Loader.php';

Zend_Loader::loadClass('Zend_Gdata');
Zend_Loader::loadClass('Zend_Gdata_AuthSub');
Zend_Loader::loadClass('Zend_Gdata_ClientLogin');
Zend_Loader::loadClass('Zend_Gdata_HttpClient');
Zend_Loader::loadClass('Zend_Gdata_Calendar');

function createEvent($client, $title = 'Title', $desc='Description content', $where = 'Somewhere',
        $startDateTime = '2010-01-20T00:00+07:00', $endDateTime = '2010-01-20T00:00+07:00')
{
    $gdataCal = new Zend_Gdata_Calendar($client);
    $newEvent = $gdataCal->newEventEntry();

    $newEvent->title = $gdataCal->newTitle($title);
    $newEvent->where = array($gdataCal->newWhere($where));
    $newEvent->content = $gdataCal->newContent("$desc");

    $when = $gdataCal->newWhen();
    $when->startTime = "{$startDateTime}";
    $when->endTime = "{$endDateTime}";

    $reminder = $gdataCal->newReminder();
    $reminder->method = "sms";
    $reminder->minutes = "10";

    $when->reminders = array($reminder);

    $newEvent->when = array($when);

    // Upload the event to the calendar server
    // A copy of the event as it is recorded on the server is returned
    $createdEvent = $gdataCal->insertEvent($newEvent);

    return $createdEvent->id->text;
}

$user = $_REQUEST["user"];
$pass = $_REQUEST["pass"];
$service = Zend_Gdata_Calendar::AUTH_SERVICE_NAME; // predefined service name for calendar

$client = Zend_Gdata_ClientLogin::getHttpClient($user, $pass, $service);

$strStartDateTime = date(DATE_ATOM, mktime(date("H"), date("i") + 14, date("s"), date("m"), date("d"), date("Y")));
$strEndDateTime = date(DATE_ATOM, mktime(date("H"), date("i") + 15, date("s"), date("m"), date("d"), date("Y")));

createEvent($client, $_REQUEST["sms"],
                'Title',
                'Detail',
                $strStartDateTime, $strEndDateTime);

echo $_REQUEST['sms'];

————————————-

3. Create Flex web app

<?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/mx" xmlns:views="org.robotlegs.sawasdeesms.views.*">
	<s:layout>
		<s:VerticalLayout paddingLeft="4" paddingRight="4" paddingBottom="4" paddingTop="4"/>
	</s:layout>

	<fx:Declarations>
		<!-- Place non-visual elements (e.g., services, value objects) here -->
	</fx:Declarations>

	<views:MainView width="{this.width}" height="{this.height}"/>

</s:Application>

MainView.mxml

<?xml version="1.0" encoding="utf-8"?>
<s:VGroup xmlns:fx="http://ns.adobe.com/mxml/2009"
		  xmlns:s="library://ns.adobe.com/flex/spark"
		  xmlns:mx="library://ns.adobe.com/flex/mx"
		  horizontalAlign="center"
		  preinitialize="mainViewInit()">

	<fx:Script>
		<![CDATA[
			import mx.collections.ArrayCollection;
			import mx.controls.Alert;
			import mx.core.FlexGlobals;
			import mx.events.CloseEvent;
			import mx.managers.PopUpManager;
			import mx.rpc.AsyncToken;
			import mx.rpc.events.ResultEvent;
			import mx.rpc.http.HTTPService;

			private var arr:Array = [
				{fullname:'John Doe', email:'johndoe@gmail.com', pwd:'mypassword', selected:true}
			];

			[Bindable] private var ac:ArrayCollection;

			private var mainViewPopUp:MainPopUp;

			private var hs:HTTPService;
			private var urlVar:URLVariables;

			private function mainViewInit():void{
				ac = new ArrayCollection( arr );

				hs = new HTTPService();
				hs.resultFormat = HTTPService.RESULT_FORMAT_TEXT;
				hs.url = '../php_service/sms.php';
				hs.addEventListener(ResultEvent.RESULT, httpServiceResult );

				urlVar = new URLVariables();

			}

			public function onSelect(obj:Object, selected:Boolean):void{
				obj.selected = selected;
				ac.setItemAt( obj, ac.getItemIndex( obj ) );

			}

			protected function btAdd_clickHandler(event:MouseEvent):void
			{
				mainViewPopUp = PopUpManager.createPopUp( FlexGlobals.topLevelApplication as Sprite, MainPopUp, true ) as MainPopUp;
				PopUpManager.centerPopUp( mainViewPopUp );
				mainViewPopUp.addEventListener( 'formSubmit', onFormSubmit );
			}

			private function onFormSubmit( event:Event ):void{
				if( mainViewPopUp.useForm == MainPopUp.FORM_ADD ){
					ac.addItem(
						{
							fullname:mainViewPopUp.txtInName.text,
							email:mainViewPopUp.txtInEmail.text,
							pwd:mainViewPopUp.txtInPwd.text,
							selected:true
						}
					);
				}else{
					ac.setItemAt(
						{
							fullname:mainViewPopUp.txtInName.text,
							email:mainViewPopUp.txtInEmail.text,
							pwd:mainViewPopUp.txtInPwd.text,
							selected:dg.selectedItem.selected
						},
						dg.selectedIndex
					);
				}

			}

			protected function btRemove_clickHandler(event:MouseEvent):void
			{
				Alert.show('ต้องการลบรายชื่อ ใช่หรือไม่?', 'คำเตือน', 3, this, onConfirmRemove );
			}

			private function onConfirmRemove( event:CloseEvent ):void{
				if( event.detail == Alert.YES ){
					ac.removeItemAt( dg.selectedIndex );
				}
			}

			protected function dg_doubleClickHandler(event:MouseEvent):void
			{
				mainViewPopUp = PopUpManager.createPopUp( FlexGlobals.topLevelApplication as Sprite, MainPopUp, true ) as MainPopUp;
				PopUpManager.centerPopUp( mainViewPopUp );
				mainViewPopUp.useForm = MainPopUp.FORM_EDIT;
				mainViewPopUp.txtInName.text = dg.selectedItem.fullname;
				mainViewPopUp.txtInEmail.text = dg.selectedItem.email;
				mainViewPopUp.txtInPwd.text = dg.selectedItem.pwd;
				mainViewPopUp.addEventListener( 'formSubmit', onFormSubmit );
			}

			protected function btSend_clickHandler(event:MouseEvent):void
			{
				urlVar.sms = txtAr.text;
				urlVar.user = ac.getItemAt( 0 ).email;
				urlVar.pass = ac.getItemAt( 0 ).pwd;
				var token:AsyncToken = hs.send(urlVar);
				token.index = 0;
			}

			protected function httpServiceResult( event:ResultEvent ):void{
				var token:AsyncToken;

				trace( event.token.index );
				trace( event.result );
				if( ( event.token.index + 1 ) < ac.length ){
					urlVar.user = ac.getItemAt( event.token.index + 1 ).email;
					urlVar.pass = ac.getItemAt( event.token.index + 1 ).pwd;
					token = hs.send(urlVar);
					token.index = event.token.index + 1;
				}else{
					Alert.show('ส่ง SMS แล้วค่ะ', 'ขอบคุณที่ใช้บริการ');
				}
			}

		]]>
	</fx:Script>

	<fx:Declarations>
		<!-- Place non-visual elements (e.g., services, value objects) here -->
	</fx:Declarations>

	<s:VGroup>
		<s:HGroup width="100%" height="100%">
			<mx:Spacer width="100%"/>
			<mx:Button height="28" id="btAdd" icon="@Embed(source='assets/aesthetica-version-2/png/24x24/add.png')"
					   click="btAdd_clickHandler(event)"/>
			<mx:Button height="28" id="btRemove" icon="@Embed(source='assets/aesthetica-version-2/png/24x24/remove.png')"
					   enabled="{dg.selectedIndex + 1}"
					   click="btRemove_clickHandler(event)"/>
		</s:HGroup>

		<mx:DataGrid id="dg" dataProvider="{ac}"
					 doubleClickEnabled="true" doubleClick="dg_doubleClickHandler(event)">
			<mx:columns>
				<mx:DataGridColumn width="200" dataField="fullname" headerText="ชื่อ - นามสกุล"/>
				<mx:DataGridColumn width="200" dataField="email" headerText="อีเมล"/>
				<!--
				<mx:DataGridColumn dataField="selected" headerText="ส่งข้อความ ">
					<mx:itemRenderer>
						<fx:Component>
							<mx:Box horizontalAlign="center">
								<s:CheckBox id="cb" selected="{data.selected}"
											change="outerDocument.onSelect(data, cb.selected)"/>
							</mx:Box>
						</fx:Component>
					</mx:itemRenderer>
				</mx:DataGridColumn>
				-->
			</mx:columns>
		</mx:DataGrid>

		<s:HGroup width="100%">
			<mx:Spacer width="100%"/>
			<s:Label text="*หมายเหตุ Double-click เพื่อแก้ไข"/>
		</s:HGroup>

		<mx:Spacer height="50"/>

	</s:VGroup>

	<s:VGroup>
		<s:TextArea id="txtAr" width="400"/>

		<s:HGroup width="100%">
			<mx:Spacer width="100%"/>
			<mx:Button height="28" id="btSend" icon="@Embed(source='assets/aesthetica-version-2/png/24x24/next.png')"
					   label="ส่ง SMS"
					   click="btSend_clickHandler(event)"/>
		</s:HGroup>
	</s:VGroup>

</s:VGroup>

MainPopUp.mxml

<?xml version="1.0" encoding="utf-8"?>
<s:TitleWindow xmlns:fx="http://ns.adobe.com/mxml/2009"
			   xmlns:s="library://ns.adobe.com/flex/spark"
			   xmlns:mx="library://ns.adobe.com/flex/mx" close="titlewindow1_closeHandler(event)">

	<fx:Metadata>
		[Event(name="formSubmit", type="mx.events.Event")]
	</fx:Metadata>

	<s:layout>
		<s:VerticalLayout/>
	</s:layout>

	<fx:Script>
		<![CDATA[
			import mx.events.CloseEvent;
			import mx.managers.PopUpManager;

			public static const FORM_ADD:String = 'formAdd';
			public static const FORM_EDIT:String = 'formEdit';

			public var useForm:String = FORM_ADD;

			protected function titlewindow1_closeHandler(event:CloseEvent):void
			{
				PopUpManager.removePopUp( this );
			}

			protected function btSubmit_clickHandler(event:MouseEvent):void
			{
				dispatchEvent( new Event( 'formSubmit' ) );
				PopUpManager.removePopUp( this );
			}

		]]>
	</fx:Script>
	<fx:Declarations>
		<!-- Place non-visual elements (e.g., services, value objects) here -->
	</fx:Declarations>
	<s:controlBarContent>
		<mx:Spacer width="100%"/>
		<mx:Button height="28" id="btSubmit"
				    icon="@Embed(source='assets/aesthetica-version-2/png/24x24/accept.png')"
					click="btSubmit_clickHandler(event)"/>
	</s:controlBarContent>
	<mx:Form>
		<mx:FormItem label="ชื่อ">
			<s:TextInput id="txtInName" width="200"/>
		</mx:FormItem>
		<mx:FormItem label="อีเมล">
			<s:TextInput id="txtInEmail" width="200"/>
		</mx:FormItem>
		<mx:FormItem label="รหัสผ่าน">
			<s:TextInput displayAsPassword="true" id="txtInPwd" width="200"/>
		</mx:FormItem>
	</mx:Form>

</s:TitleWindow>

————————————-

งานนี้จะเอาไปใช้อะไรได้บ้าง ก็อย่างเช่น เราไม่สามารถอยู่หน้าคอมฯ ได้ตลอด สมมติเราทำระบบสำหรับสั่งซื้อสินค้าเมื่อมีออเดอร์เข้ามาก็ส่ง SMS ให้แจ้งเตือนให้เรารู้ เพื่อจะได้บริการลูกค้าได้เร็วขึ้น หรือจะทำเป็นระบบแจ้งเตือนการนัดประชุมในองค์กรขนาดเล็ก ช่วยประหยัดค่าใช้จ่ายค่าบริการ SMS หรือจะทำเป็นแจ้งเตือนการนัดหมายกับลูกค้า ฯลฯ แล้วแต่จะเอาไปประยุกต์ แต่ก็นะของฟรี บางทีก็ missing text บ้างไรบ้าง แต่ก็ไม่หนักหนาอะไรเกินไป ;)

Using AsyncToken class by RemoteObject or HTTPService

การใช้ RemoteObject หรือ HTTPService ติดต่อไปยัง Server เพื่อเอาข้อมูลมาใช้งาน ในบางครั้งเราอาจมีความจำเป็นบางอย่างที่ต้องการจำแนกแยกแยะว่าข้อมูลที่ได้มาด้วยเมธอด(method)หรือโอเปอเรชั่น(operation)นั้น เกิดขึ้นจากกรณีใด ในการแก้ปัญหานี้เราสามารถกำหนด Token ให้กับ RemoteObject เพื่อเป็นตัวช่วยในการตัดสินใจได้

(เคยเข้าใจผิดกับคำว่า Token กับ Totem ในหนังเรื่อง Inception ดีนะที่มีคนให้ความชัดเจนเรื่องนี้ไม่งั้นคงทำให้คนที่มาเรียนเข้าใจอะไรผิดๆ ไป)

Example how to use AsyncToken by RemoteObject


<?xml version="1.0" encoding="utf-8"?>

<!-- TokenExampleApp.mxml -->

<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/mx"

applicationComplete="initApp()">

<s:layout>

<s:VerticalLayout/>

</s:layout>

<fx:Script>

<![CDATA[

import mx.collections.ArrayCollection;

import mx.messaging.messages.RemotingMessage;

import mx.rpc.AsyncToken;

import mx.rpc.events.FaultEvent;

import mx.rpc.events.ResultEvent;

[Bindable] private var ac:ArrayCollection;

private function initApp():void{

ac = new ArrayCollection(

[

{datetime:"-"},

{datetime:"-"},

{datetime:"-"},

{datetime:"-"},

{datetime:"-"}

]

);

}

protected function bt_clickHandler(event:MouseEvent):void

{

var token:AsyncToken = ro.getOperation('getDateTime').send( dg.selectedItem );

token.selectedIndex = dg.selectedIndex; // Set token property and value here

}

protected function ro_faultHandler(event:FaultEvent):void

{

}

protected function ro_resultHandler(event:ResultEvent):void

{

var rMsg:RemotingMessage = event.token.message as RemotingMessage;

switch( rMsg.operation ){

case 'getDateTime':

ac.setItemAt( event.result, event.token.selectedIndex ); //Check token property value here

break;

}

}

]]>

</fx:Script>

<fx:Declarations>

<!-- Place non-visual elements (e.g., services, value objects) here -->

<s:RemoteObject id="ro" destination="zendamf" source="TokenExService"

fault="ro_faultHandler(event)"

result="ro_resultHandler(event)"/>

</fx:Declarations>

<s:Button id="bt" label="Send" enabled="{dg.selectedIndex + 1}" click="bt_clickHandler(event)"/>

<mx:DataGrid id="dg" dataProvider="{ac}">

<mx:columns>

<mx:DataGridColumn dataField="datetime" width="200"/>

</mx:columns>

</mx:DataGrid>

</s:Application>


<?php

/**
 * Description of TokenExService
 *
 * @author noppadonsodram
 */

class TokenExService {
    //put your code here

    function getDateTime($vo){
        $vo->datetime = date("F j, Y, g:i:s a");
        return $vo;
    }

}

หัวข้อเกี่ยวกับการใช้ ​RemoteObject หรือ HTTPService

http://thaiflexdev.wordpress.com/2009/10/04/introduction-to-zendamf/

http://thaiflexdev.wordpress.com/2009/07/11/amfphp-beginnign-level/

http://thaiflexdev.wordpress.com/2010/07/15/php-variable-value-when-send-by-httpservice-or-remoteobject-2/

http://thaiflexdev.wordpress.com/2010/07/15/send-array-to-flex-app/

ZamfBrowser is Zend Amf Browser

http://stackoverflow.com/questions/2628416/service-browser-for-zend-amf

เอาไว้ดู คล้ายๆ กับใน amfphp/browser/

วิธีการติดตั้งและทดสอบ

1. สร้างโฟลเดอร์ frameworks ไว้ที่เดียวกับ Web root directory เช่น Applications/XAMPP/xamppfiles/frameworks ใน Mac OS X ที่ติดตั้ง Xampp หรือ C:\AppServ\frameworks ใน Windows ที่ติดตั้ง AppServ

2. ดาวน์โหลด Zend Frameworks จาก http://framework.zend.com/  จากนั้น Unzip  แล้ว Copy โฟลเดอร์ชื่อ Zend ซึ่งอยู่ที่ตำแหน่ง  ZendFrameworks-#.#.#/library/Zend ไปไว้ในโฟลเดอร์ frameworks ที่สร้างไว้ตามข้อ 1. ดังนี้ Applications/XAMPP/xamppfiles/frameworks/Zend หรือ C:\AppServ\frameworks\Zend

3. ดาวน์โหลดโปรแกรม ZamfBrowser จาก http://zamfbrowser.riaforge.org/index.cfm แล้ว Unzip จะได้โฟลเดอร์ ZamfBrowser_v.#.# เปลี่ยนชื่อเป็น ZamfBrowser และนำไปไว้ที่ Applications/XAMPP/xamppfiles/htdocs/ZamfBrowser หรือ C:\AppServ\www\ZamfBrowser จากนั้นติดตั้งโปรแกรมโดยใช้ไฟล์ ZamfBrowser/ZamfBrowser.air แล้วเปิดโปรแกรม

4. ตั้งค่า Services gateway คลิกที่ [Settings] จากนั้นคลิ๊ก [+]

ใส่ข้อมูลสำหรับ Add ZendAMF Server

ZendAMF Server Name : ZamfBrowser

Services Gateway : http://127.0.0.1/ZamfBrowser/zendamf_gateway_example.php

คลิก [Add Server]

ที่ Combo Box เลือก ZendAmfServiceBrowser และ getServices ตามลำดับ แล้วคลิก [Invoke Method] เพื่อทดสอบ

5. สร้างตารางในฐานข้อมูลเพื่อทดสอบ

CREATE DATABASE zamfbrowser;
USE zamfbrowser;
DROP TABLE IF EXISTS `zamfbrowser_z`;
CREATE TABLE `zamfbrowser_z` (
`z_auto_id` int(11) NOT NULL AUTO_INCREMENT,
`z_data` varchar(255) NOT NULL,
`z_label` varchar(255) NOT NULL,
`z_timestamp` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`z_auto_id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;

6. สร้างไฟล์ Service file ไว้ที่ [Web root directory]/ZamfBrowser/zendamf/

DbMySQLConst.php

<?php
/* PHP GENERATOR VERSION : MySQL AMFPHP 1.2 Direct connect to DB */
/** 		 * DbMySQLConst class */
class DbMySQLConst {
//put your code here
const HOST = "localhost";
const USERNAME = "root";
const PASSWORD = "";
const DB_NAME = "zamfbrowser";
}

ZamfbrowserVO.php

<?php
/*  * To change this template, choose Tools | Templates * and open the template in the editor. */
/** * Description of ZamfbrowserVO * * @author noppadonsodram */
class ZamfbrowserVO {
//put your code here
var $z_auto_id;
var $z_data;
var $z_label;
var $z_timestamp;
}

ZamfbrowserService.php

<?php
/* * To change this template, choose Tools | Templates * and open the template in the editor. */
/** * Description of ZamfbrowserService * * @author noppadonsodram */require_once 'Zend/Db/Adapter/Pdo/Mysql.php';require_once 'DbMySQLConst.php';require_once 'ZamfbrowserVO.php';
class ZamfbrowserService {
 var $db;
var $table = "zamfbrowser_z";
var $pk = "z_auto_id";
 //put your code here
function __construct() {
$this->db = new Zend_Db_Adapter_Pdo_Mysql(array(
'host' => DbMySQLConst::HOST,
'username' => DbMySQLConst::USERNAME,
'password' => DbMySQLConst::PASSWORD,
'dbname' => DbMySQLConst::DB_NAME
));
}
 function __destruct() {
$this->db->closeConnection();
}
 function insertRow($vo) {
$vo = get_object_vars($vo);
return $this->db->insert($this->table, $vo);
}
 function updateRow($vo) {
$vo = get_object_vars($vo);
return $this->db->update($this->table, $vo, $this->pk."='". $vo[$this->pk]."'");
}
 function deleteRow($vo) {
$vo = get_object_vars($vo);
return $this->db->delete($this->table, $this->pk."='". $vo[$this->pk]."'");
}
 function selectRow() {
$rows = $this->db->select()
->from($this->table)
->order($this->pk)
->query()->fetchAll();
$ary = array();
foreach ($rows as $row) {
$vo = new ZamfbrowserVO();
$vo->z_auto_id = $row['z_auto_id'];
$vo->z_data = $row['z_data'];
$vo->z_label = $row['z_label'];
$vo->z_timestamp = $row['z_timestamp'];
$ary[] = $vo;
}
 return $ary;
}
}

7. ระบุ Service file ไว้ใน Gateway file

zendamf_gateway_example.php

<?php
// Set up debug
error_reporting(E_ALL | E_STRICT);
ini_set("display_errors", "on");

// Set up include path for Zend Framework, this path is assuming a frameworks
// folder contains the Zend package on the same level as your public_html or www folder.
//ini_set("include_path", ini_get("include_path") . ":../frameworks");
//ini_set("include_path", ini_get("include_path") . PATH_SEPARATOR ."../frameworks");
ini_set("include_path", ini_get("include_path") . PATH_SEPARATOR . $_SERVER['DOCUMENT_ROOT'] ."/../frameworks");

// Require Zend_Amf_Server
require_once( "Zend/Amf/Server.php" );

// *ZAMFBROWSER IMPLEMENTATION*
// Require the ZendAmfServiceBrowser class, required to retrieve the list of methods on the ZendAMF server.
require_once( "browser/ZendAmfServiceBrowser.php" );
require_once( "zendamf/ZamfbrowserService.php" );

// Start Server
$server = new Zend_Amf_Server();

// Register ZendAMF Service classes
//$server->setClass( "YourServiceClass" );
//$server->setClass( "AnotherServiceClass" );
// *ZAMFBROWSER IMPLEMENTATION*
// Add the ZendAmfServiceBrowser class to the list of available classes.
$server->setClass("ZendAmfServiceBrowser");
$server->setClass("ZamfbrowserService");

// *ZAMFBROWSER IMPLEMENTATION*
// Set this reference the class requires to the server object.
ZendAmfServiceBrowser::$ZEND_AMF_SERVER = $server;

// Handle the AMF request
echo($server->handle());

8. ทดสอบ Service file ด้วย ZamfBrowser

Class : ZamfbrowserService

Method : insertRow

Class : ZamfbrowserService

Method : selectRow

9. สร้าง Flex Project

Project : ZamfApp

คลิ๊กขวา ZamfApp->Properties->Flex Compiler

Edit ค่า Additional compiler arguments จาก -locale en_US เป็น locale en_US -services “services-config.xml”

คลิ๊ก [Apply]->[OK]

ZamfApp.mxml

<?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/mx">

<s:layout>
<s:VerticalLayout/>
</s:layout>

<fx:Script>
<![CDATA[
import mx.collections.ArrayCollection;
import mx.controls.Alert;
import mx.events.FlexEvent;
import mx.messaging.messages.RemotingMessage;
import mx.rpc.events.FaultEvent;
import mx.rpc.events.ResultEvent;
import mx.utils.ArrayUtil;
import zendamf.ZamfbrowserVO;

[Bindable] public var ac:ArrayCollection;

protected function faultHandler(event:FaultEvent):void
{
Alert.show('fault');
}

protected function rmtObjZamfbrowser_resultHandler(event:ResultEvent):void
{
var rMsg:RemotingMessage = event.token.message as RemotingMessage;
switch( rMsg.operation ){
case 'insertRow':
break;
case 'selectRow':
ac = new ArrayCollection( ArrayUtil.toArray( event.result ) );
break;

case 'deleteRow':
break;
}
}

protected function btSubmit_clickHandler(event:MouseEvent):void
{
var zamfValObj:ZamfbrowserVO = new ZamfbrowserVO();

zamfValObj.z_auto_id = txtInID.text;
zamfValObj.z_data = txtInData.text;
zamfValObj.z_label = txtInLabel.text;
zamfValObj.z_timestamp = txtInTimestamp.text;

rmtObjZamfbrowserService.getOperation('insertRow').send(zamfValObj);
}

protected function btCall_clickHandler(event:MouseEvent):void
{
rmtObjZamfbrowserService.getOperation('selectRow').send();
}

]]>

</fx:Script>

<fx:Declarations>
<s:RemoteObject id="rmtObjZamfbrowserService"
showBusyCursor="true"
source="ZamfbrowserService"
destination="zend"
fault="faultHandler(event)"
result="rmtObjZamfbrowser_resultHandler(event)"/>

</fx:Declarations>

<mx:Form>
<mx:FormItem label="ID">
<s:TextInput id="txtInID"/>
</mx:FormItem>

<mx:FormItem label="Data">
<s:TextInput id="txtInData"/>
</mx:FormItem>

<mx:FormItem label="Label">
<s:TextInput id="txtInLabel"/>
</mx:FormItem>
<mx:FormItem label="Time">
<s:TextInput id="txtInTimestamp"/>
</mx:FormItem>

<s:Button id="btSubmit"
click="btSubmit_clickHandler(event)" label="Submit"/>
</mx:Form>

<s:Button id="btCall"
click="btCall_clickHandler(event)" label="Call"/>

<mx:AdvancedDataGrid id="avd" dataProvider="{ac}"/>

</s:Application>

ZamfbrowserVO.as

package zendamf
{
[RemoteClass(alias="zendamf.ZamfbrowserVO")]
[Bindable]
public class ZamfbrowserVO
{
public var z_auto_id:String;
public var z_data:String;
public var z_label:String;
public var z_timestamp:String;
}
}

services-config.xml

<?xml version="1.0" encoding="UTF-8"?>
<services-config>
<services>
<service id="amfphp-flashremoting-service" messageTypes="flex.messaging.messages.RemotingMessage">
<destination id="zend">
<channels>
<channel ref="my-zend"/>
</channels>
<properties>
<source>*</source>
</properties>
</destination>
</service>
</services>
<channels>
<channel-definition id="my-zend">
<endpoint uri="../zendamf_gateway_example.php"/>
</channel-definition>
</channels>
</services-config>

10. Run Flex with Zend Amf

UTF-8 Character Encoding Problem

UTF-8 Character Encoding Problem

ในบางกรณีการตั้งค่าภาษาใช้
<meta http-equiv=”Content-Type” content=”text/html; charset=UTF-8″ />
อาจจะยังไม่สามารถแสดงผลภาษาไทยได้ ต้องใช้การเปลี่ยนค่า Character server encoding ด้วยวิธีต่อไปนี้

header('Content-Type:text/html; charset=UTF-8');
หรือ
ini_set('default_charset', 'UTF-8');

Ref : http://htmlpurifier.org/docs/enduser-utf8.html

Follow

Get every new post delivered to your Inbox.