Customize DateField for unsupported locale

การ Customize DateField ให้แสดงเดือน/ปี แบบไทยๆ

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

สิ่งที่ควรรู้เกี่ยวกับการทำ Localization เบื้องต้น

http://www.adobe.com/devnet/flex/articles/flex_localization_pt1.html

http://devgirl.org/2011/03/15/flex-4-localization/

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

การพัฒนาระบบที่ต้องทำ Localization เพื่อรองรับการผู้ใช้หลายภาษา DateField จะสามารถปรับเปลี่ยนการแสดงผลได้อัตโนมัติ ดังตัวอย่าง

‘en_US’ :

————————-

‘zh_CN’ :

———————–

ซึ่งการปรับเปลี่ยนแบบอัตโนมัติของ DateField นี้ ใน Flex framework จะรองรับได้หลายภาษา แต่ไม่ทั้งหมด O_o’ ซึ่งภาษาไทยก็ภาษาที่ Flex framework ไม่ได้จัดการเตรียมไว้ให้ ดังนั้นจึงต้องมีการ Customize เล็กน้อยเพื่อให้สามารถแสดงผลให้เหมาะสม ดังต่อไปนี้

– สร้าง Locale Resources file ภาษาที่ต้องการ

– Config (SDK Apache Flex 4.8.0) : Additional compiler arguments with

-locale en_US -locale ja_JP -locale ko_KR -locale th_TH -locale zh_CN -source-path ./locale/{locale} -allow-source-path-overlap=true -services “services-config.xml”

– ทำ ButtonBar และทำ dataProvider binding ด้วย…


acFlag = new ArrayCollection(

[

{label:'', locale:'zh_CN', icon:'org/robotlegs/asia_garage/views/assets/flags/24/China.png'},

{label:'', locale:'th_TH', icon:'org/robotlegs/asia_garage/views/assets/flags/24/Thailand.png'},

{label:'', locale:'en_US', icon:'org/robotlegs/asia_garage/views/assets/flags/24/United-States.png'}

]

);

 

BindingUtils.bindProperty(view.btnBar, 'dataProvider', model, 'acFlag');

– ButtonBar MXML


<s:ButtonBar id="btnBar" iconField="icon"

width="90" height="28"

change="btnBar_changeHandler(event)"/>

– ButtonBar change event code


protected function btnBar_changeHandler(event:IndexChangeEvent):void

{

trace('MainAppView.btnBar_changeHandler(event:IndexChangeEvent):void');

// TODO Auto-generated method stub

if(btnBar.selectedIndex < 0)

return;

resourceManager.localeChain = [btnBar.selectedItem.locale];

}

– Customize DateField (ซะที)

////////////////////////////////////////////////////////////////////////////////

//

// DateFieldLocale_AS class is date field control for unsupported locale

//  by Noppadon Sodram

// email : knopsod@gmail.com

//

////////////////////////////////////////////////////////////////////////////////

package org.robotlegs.knopsod.components.datefield_th

{

import mx.controls.DateField;

import mx.controls.dataGridClasses.DataGridListData;

import mx.controls.listClasses.IListItemRenderer;

import mx.events.DateChooserEvent;

import mx.events.DropdownEvent;

public class DateFieldLocale_AS extends DateField implements IListItemRenderer

{

private var df:DateField;

private var dayNamesTH:Array;

private var monthNamesTH:Array;

public function DateFieldLocale_AS()

{

trace('DateFieldLocale_AS()');

super();

df = new DateField();

this.dayNamesTH = new Array('อา','จ','อ','พ','พฤ','ศ','ส');

this.monthNamesTH = new Array(

'มกราคม','กุมภาพันธ์','มีนาคม','เมษายน',

'พฤษภาคม','มิถุนายน','กรกฏาคม','สิงหาคม',

'กันยายน','ตุลาคม','พฤศจิกายน','ธันวาคม'

);

this.formatString = 'YYYY-MM-DD';

this.yearNavigationEnabled = true;

trace('resourceManager.getLocales():' + resourceManager.getLocales());

trace('resourceManager.getLocales()[0]:' + resourceManager.getLocales()[0]);

trace('resourceManager.localeChain:' + resourceManager.localeChain);

trace('resourceManager.localeChain[0]:' + resourceManager.localeChain[0]);

if(resourceManager.localeChain[0] == 'th_TH'){

this.dayNames = this.dayNamesTH;

this.monthNames = this.monthNamesTH;

}

this.addEventListener(DropdownEvent.OPEN, this.openHandler);

this.addEventListener(DateChooserEvent.SCROLL, this.scrollHandler);

}

private function openHandler(event:DropdownEvent):void{

trace('DateFieldLocale_AS.openHandler(event:DropdownEvent):void{');

setLocaleDateFieldSymbol();

}

private function scrollHandler(event:DateChooserEvent):void{

trace('DateFieldLocale_AS.scrollHandler(event:DateChooserEvent):void{');

setLocaleDateFieldSymbol();

}

private function setLocaleDateFieldSymbol():void{

trace('DateFieldLocale_AS.setLocaleDateFieldSymbol():void{');

trace('resourceManager.getLocales():' + resourceManager.getLocales());

trace('resourceManager.getLocales()[0]:' + resourceManager.getLocales()[0]);

trace('resourceManager.localeChain:' + resourceManager.localeChain);

trace('resourceManager.localeChain[0]:' + resourceManager.localeChain[0]);

if(resourceManager.localeChain[0] == 'th_TH'){

this.monthSymbol = '';

this.yearSymbol = '/' + (this.displayedYear + 543);

}else{

this.monthSymbol = df.monthSymbol;

this.yearSymbol = df.yearSymbol;

}

}

override public function set data(value:Object):void{

trace('DateFieldLocale_AS.set data(value:Object):void{');

trace('value[ ( this.listData as DataGridListData ).dataField ]:' + value[ ( this.listData as DataGridListData ).dataField ]);

this.text = value[ ( this.listData as DataGridListData ).dataField ];

}

override protected function resourcesChanged():void{

trace('DateFieldLocale_AS.resourcesChanged():void{');

super.resourcesChanged();

trace('resourceManager.getLocales():' + resourceManager.getLocales());

trace('resourceManager.getLocales()[0]:' + resourceManager.getLocales()[0]);

trace('resourceManager.localeChain:' + resourceManager.localeChain);

trace('resourceManager.localeChain[0]:' + resourceManager.localeChain[0]);

if(resourceManager.localeChain[0] == 'th_TH'){

this.dayNames = this.dayNamesTH;

this.monthNames = this.monthNamesTH;

}else{

df = null;

df = new DateField();

this.dayNames = (df)?df.dayNames:this.dayNames;

this.monthNames = (df)?df.monthNames:this.monthNames;

}

}

}

}

– ผลลัพธ์


อ่านยากหน่อยนะครับ ขออภัย

Posted in AIR, Flex, ActionScript | Leave a comment

Create PHP database table mapped class concept

เนื้อหาส่วนนี้น่าจะมีประโยชน์สำหรับการวางมาตรฐานการพัฒนาฯ ที่ผมเองเห็นว่าไม่ได้มีแต่เฉพาะลูกค้าผมที่ไม่ได้วางกรอบหรือแนวทางนี้ไว้ และเนื่องจากว่าบ่อยครั้งที่สังเกตเห็นนักพัฒนามื่อใหม่หรือมืออาชีพบางส่วนก็ลุยตะบี้ตะบันเขียนอย่างเดียว พองานใหญ่ขึ้น ต้องทำงานเป็นทีม ก็เริ่มจะเกิดปัญหากับเรื่องเล็กๆ น้อยๆ เหล่านี้

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

ผมสังเกตุว่าเวลาที่ทีมงานเขียนโค้ด php ในส่วนที่มีการติดต่อกับ mysql ไม่ได้มีหรือนำเอา db table mapped class มาใช้ อาจจะเคยชินและชำนาญในการพัฒนางานด้วยสไตล์นี้ ซึ่งในบางครั้งเกิดข้อขัดข้องแล้วผมได้ดู ผมเองก็ไม่ได้มีความจำขั้นดีพอ ที่จะรู้ว่าเป็นเพราะชื่อ table ชื่อ field เขียนผิด และผมก็ไม่เคยให้ความสำคัญที่จะจำรายละเอียดเหล่านี้มากไปกว่าการเขียนให้เป็นไปตามคอนเซ็ปท์ที่ต้องการ แต่ยกภาระนี้ให้ editor เป็นตัวจัดการแทน ซึ่งในปัจจุบันนี้มีความสามารถสูงอยู่แล้ว เช่น มี auto complete ในการช่วยเขียนโค้ดต่างๆ ได้สะดวก จะปรับเปลี่ยนอะไรก็สามารถใช้ re factor ได้ง่ายๆ ตัวอย่าง editor ฟรีที่สามารถทำได้เช่น netbeans หรือ eclipse โดยเมื่อเราสร้าง directory project ระบบจะมองเห็น class เหล่านี้ได้อัตโนมัติ

database table mapped class (ไม่รู้ว่านักพัฒนาสมัยนี้เขาเรียกอะไร) คือ class ที่มีชื่อ properties ตรงกับชื่อ field ใน table โดยมีจุดมุ่งหมายหนึ่งคือใช้ class นี้สร้าง object แทน row ที่ fetched ออกมาจากการ query ข้อมูลใน table และ class นี้อาจมีคุณสมบัติอื่นๆ ที่นักพัฒนาออกแบบเพื่อประโยชน์ในการอำนวยความสะดวกในการเขียนโค้ด เช่น constant ที่เก็บชื่อ field, ชื่อ table, ชื่อ field เป็นต้น การเขียน class ดังกล่าวมีแนวทางหลักๆ ดังนี้ครับ

สมมติผมมี table ชื่อ icd_employees

มี fields ชื่อ emp_auto_id, emp_firstname, emp_lastname

จะมี class ในไฟล์ชื่อ Icd_Employees_VO.php ตามรายละเอียดนี้


class Icd_Employees_VO{

const EMP_AUTO_ID = "emp_auto_id";

const EMP_FIRSTNAME = "emp_firstname";

const EMP_LASTNAME = "emp_lastname";



var $emp_auto_id;

var $emp_firstname;

var $emp_lastname;

}

และในตัว php service ใดๆ ควรจะมีข้อมูลที่เรียกใช้เป็น table name และ primary key field name ดังนี้


class IcdEmployeesService{

const TABLE = "icd_employees";

const PK = "emp_auto_id";

//...

}

ทั้งนี้เพื่อความสะดวกในการเรียก field ในกรณีต่างๆ เช่น

เมื่อต้องการเขียน query message


require_once (“Icd_Employees_VO.php”);

require_once (“IcdEmployeesService.php”);



$query = "SELECT ".Icd_Employees_VO::EMP_AUTO_ID." FROM ".IcdEmployeesService::TABLE;

เมื่อต้องการ fetch result ออกมาเช่น


$result = mysql_query($query);

$json = "";

while($row = mysql_fetch_assoc($result)){

$json .= $json

?',{'.Icd_Employees_VO::EMP_AUTO_ID.':"'.$row[Icd_Employees_VO::EMP_AUTO_ID].'"}'

:'{'.Icd_Employees_VO::EMP_AUTO_ID.':"'.$row[Icd_Employees_VO::EMP_AUTO_ID].'"}';

}

$json .= "[".$json."]";

echo $json;

โดยการสร้าง db table mapped class นี้เป็น routine job ตามชื่อ table และชื่อ field อยู่แล้ว ทางทีมงานอาจจะเอา http://php4amf.asia-garage.com ใน text area ช่องที่ 3. PHP VO class ไปใช้ได้ หรือจะพัฒนาเพิ่มเติมเพื่อเหมาะกับงาน สำหรับเครื่องมือตามลิ้งค์ที่ผมส่งมาสามารถ view source (คลิ๊กขวา -> View source) เพื่อนำไป build เพื่อนำไปใช้งาน โดยมีไฟล์ php service ที่จำเป็นมาด้วยแล้ว

ฝากทีมงานพิจารณาตามที่ผมเสนอสำหรับงานข้างหน้าด้วยครับ

Posted in PHP, Zend Framwork | 1 Comment

RemoteObject concurrency property

ปกติเวลาเราใช้ RemoteObject จะมีค่า default ของ property ต่างๆ ที่เหมาะสมมาให้อยู่แล้ว สำหรับหัวข้อผมจะมาแนะนำการตั้งค่า property ใน RemoteObject ที่ชื่อ concurrency และอธิบายการทำงานที่แตกต่างกันในแต่ละค่า

concurrency property มีประเภทเป็น String สามารถกำหนดค่าได้อยู่ 3 ค่าดังนี้ multiple, single, last โดยมี multiple เป็นค่า default

แสดงโค้ด mxml


<s:RemoteObject id="roMultiple" concurrency="multiple"/>

<s:RemoteObject id="roSingle" concurrency="single"/>

<s:RemoteObject id="roLast" concurrency="last"/>

อธิบายการทำงานเมื่อตั้งค่าเป็น multiple, single, last

ถ้า concurrency=”multiple” เมื่อส่ง request ใดๆ ไปยัง hosting server มันจะมี response ทุก request ซึ่งสามารถเช็ค response เหล่านี้ได้ด้วยการ add event listener ชื่อ ResultEvent หรือ FaultEvent ได้

ถ้า concurrency=”single” เมื่อส่ง request ใดๆ ไปยัง hosting server ถ้ายังไม่มี response กลับมา แล้วมีการส่ง request ใดๆ ไป RemoteObject จะไม่สนในส่ง request อันล่าสุด

ถ้า concurrency=”last” เมื่อส่ง request ใดๆ ไปยัง hosting server แล้วในระหว่างที่รอ response กลับมามีการส่ง request ใดๆ ไป RemoteObject จะรอรับ response ของ request อันล่าสุดเท่านั้น จากที่ผมเคยใช้ในระบบ การกำหนดค่านี้ไม่ได้หมายความว่าจะไม่มีการดำเนินการใดๆ ที่ hosting server นะครับ จะยังมีการประมวลผลปกติ เพียงแต่ที่ Client จะสนใจเฉพาะ response ล่าสุด

คำแนะนำ สำหรับผมนิยมใช้ concurrency=”last” เพราะโดยส่วนใหญ่ต้องการข้อมูลที่ล่าสุดเท่านั้น

Posted in AIR, Flex, ActionScript | Leave a comment

Flex DateField component (Year and Month in Thai)

DateField ที่แสดงเดือนเป็นภาษาไทย และปี พ.ศ. ครับ

DateFieldTH_AS.as


package org.robotlegs.knopsod.components.datefield_th
{
import mx.controls.DateField;
import mx.controls.dataGridClasses.DataGridListData;
import mx.controls.listClasses.IListItemRenderer;
import mx.events.DateChooserEvent;
import mx.events.DropdownEvent;

public class DateFieldTH_AS extends DateField implements IListItemRenderer
{
public function DateFieldTH_AS()
{
super();
this.formatString = 'YYYY-MM-DD';
this.yearNavigationEnabled = true;

this.dayNames = ['อา','จ','อ','พ','พฤ','ศ','ส'];
this.monthNames = [
'มกราคม','กุมภาพันธ์','มีนาคม','เมษายน',
'พฤษภาคม','มิถุนายน','กรกฏาคม','สิงหาคม',
'กันยายน','ตุลาคม','พฤศจิกายน','ธันวาคม'
];

this.addEventListener( DropdownEvent.OPEN, this.openHandler );
this.addEventListener( DateChooserEvent.SCROLL, this.scrollHandler );
}

private function openHandler( event:DropdownEvent ):void{
this.yearSymbol = '/' + ( this.displayedYear + 543 );
}

private function scrollHandler( event:DateChooserEvent ):void{
this.yearSymbol = '/' + ( this.displayedYear + 543 );
}

override public function set data(value:Object):void{
trace('value[ ( this.listData as DataGridListData ).dataField ]:' + value[ ( this.listData as DataGridListData ).dataField ]);

this.text = value[ ( this.listData as DataGridListData ).dataField ];
}
}
}

การแสดงผล

Posted in AIR, Flex, ActionScript | Leave a comment

Flex pagination component

ขอแนะนำคอมโพเนนท์ที่ช่วยในการแบ่งหน้า และสรุปวิธีการใช้ รูปร่างคอมโพเนนท์ตัวที่นำมาแนะนำจะมีหน้าตาตามรูปด้านบน

CMPageNavigator.mxml


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

<mx:HBox xmlns:mx="http://www.adobe.com/2006/mxml" verticalAlign="middle"

creationComplete="init()">

<mx:Metadata>

[Event(name="eventCurrent", type="flash.events.Event")]

</mx:Metadata>

<mx:Script>

<![CDATA[

import mx.controls.Alert;

/** ------------------------------------------- */

[Bindable] private var _boolCreated:Boolean;

/** ------------------------------------------- */

[Bindable] private var _iOffset:int = 1;

/** ------------------------------------------- */

[Bindable] private var _iRow:int;

[Bindable] public function get iRow():int{ return _iRow; }

public function set iRow( i:int ):void{

_iRow = i;

if( _boolCreated ) init();

}

/** ------------------------------------------- */

[Bindable] private var _iPageNav:int;

[Bindable] public function get iPageNav():int{ return _iPageNav; }

public function set iPageNav( i:int ):void{

_iPageNav = i;

if( _boolCreated ) init();

}

/** ------------------------------------------- */

[Bindable] private var _iRowTotal:int;

[Bindable] public function get iRowTotal():int{ return _iRowTotal; }

public function set iRowTotal( i:int ):void{

_iRowTotal = i;

if( _boolCreated ) init();

}

/** ------------------------------------------- */

[Bindable] private var _boolLastPage:Boolean;

[Inspectable(enumeration="true,false", defaultValue="false")]

[Bindable] public function get boolLastPage():Boolean{ return _boolLastPage; }

public function set boolLastPage( bool:Boolean ):void{

_boolLastPage = bool;

if( _boolCreated ) init();

}

/** ------------------------------------------- */

[Bindable] private var _iPage:int;

[Bindable] public function get iPage():int{ return _iPage }

public function set iPage( i:int ):void{

_iPage = i;

if( _boolCreated ){

numStepPage.value = _iPage;

numStepPageChange();

}

}

/** ------------------------------------------- */

private function init():void{

_iRow = (_iRow)?_iRow:10;

_iPageNav = (_iPageNav)?_iPageNav:5;

_iRowTotal = (_iRowTotal)?_iRowTotal:1;

numStepRow.value = _iRow;

numStepRowChange();

_boolCreated = true;

}

/** ------------------------------------------- */

private function numStepRowChange():void{

try{

_iRow = numStepRow.value;

ac.removeAll();

for( var i:int = 0; i < _iPageNav && i < Math.ceil( _iRowTotal/_iRow ); i++ ){

ac.addItem( new Object() );

}

if( numStepPage.value == 1 ){

labelFirstPageClick();

}else if( _boolLastPage ){

labelLastPageClick();

}else{

numStepPage.value = _iPage =

( Math.ceil( _iRowTotal/_iRow ) < numStepPage.value )?

Math.ceil( _iRowTotal/_iRow ):numStepPage.value;

numStepPageChange();

}

}catch( err:Error ){

trace( err ); Alert.show("กรุณาตรวจสอบความถูกต้องของข้อมูล");

}

}

/** ------------------------------------------- */

private function labelPageClick( event:MouseEvent ):void{

var labelObj:Label = event.currentTarget as Label;

numStepPage.value = _iPage = int( labelObj.text );

numStepPageChange();

}

/** ------------------------------------------- */

private function labelFirstPageClick():void{

numStepPage.value = _iPage = 1;

numStepPageChange();

}

/** ------------------------------------------- */

private function labelLastPageClick():void{

try{

numStepPage.value = _iPage = Math.ceil( _iRowTotal/_iRow );

numStepPageChange();

}catch( err:Error ){

trace( err ); Alert.show("กรุณาตรวจสอบความถูกต้องของข้อมูล");

}

}

/** ------------------------------------------- */

private function numStepPageChange():void{

try{

_iPage = numStepPage.value;

var i:int = 0;

for( i = 0; i < ac.length; i++ ){

labelPageNav[i].setStyle( "textDecoration", "none" );

}

if( numStepPage.value <= Math.ceil(ac.length/2) ){

_iOffset = 1;

for( i = 0; i < ac.length; i++ ){

if( labelPageNav[i].text == numStepPage.value ){

labelPageNav[i].setStyle( "textDecoration", "underline" );

break;

}

}

}else if(

numStepPage.value >=

( Math.ceil(_iRowTotal/numStepRow.value ) - Math.ceil( ac.length/2 ) )

){

_iOffset = Math.ceil(_iRowTotal/_iRow) - ac.length + 1;

for( i = 0; i < ac.length; i++ ){

if( labelPageNav[i].text == numStepPage.value ){

labelPageNav[i].setStyle( "textDecoration", "underline" );

break;

}

}

}else{

_iOffset = numStepPage.value - Math.ceil( ac.length/2 ) + 1;

labelPageNav[Math.ceil( ac.length/2 ) - 1].setStyle( "textDecoration", "underline" );

}

labelFirstPage.visible = _iOffset != 1;

labelLastPage.visible = ( _iOffset + ac.length ) != Math.ceil( _iRowTotal/_iRow ) + 1;

_boolLastPage = numStepPage.value == Math.ceil( _iRowTotal/_iRow );

this.dispatchEvent( new Event( "eventCurrent" ) );

}catch( err:Error ){

trace( err ); Alert.show("กรุณาตรวจสอบความถูกต้องของข้อมูล");

}

}

/** ------------------------------------------- */

]]>

</mx:Script>

<mx:ArrayCollection id="ac"/>

<mx:Label text="Show :"/>

<mx:NumericStepper id="numStepRow" minimum="1" maximum="100"

change="numStepRowChange()"/>

<mx:Spacer width="100%"/>

<mx:Label  id="labelFirstPage"

text="1..."

buttonMode="true" useHandCursor="true" mouseChildren="false"

click="labelFirstPageClick()"/>

<mx:Repeater id="rptr" dataProvider="{ac}">

<mx:Label id="labelPageNav" text="{rptr.currentIndex + _iOffset}"

buttonMode="true" useHandCursor="true" mouseChildren="false"

click="labelPageClick(event)"/>

</mx:Repeater>

<mx:Label id="labelLastPage"

text="...{Math.ceil( _iRowTotal/numStepRow.value )}"

buttonMode="true" useHandCursor="true" mouseChildren="false"

click="labelLastPageClick()"/>

<mx:Label text="Page :"/>

<mx:NumericStepper id="numStepPage" minimum="1"

maximum="{Math.ceil( _iRowTotal/numStepRow.value )}"

change="numStepPageChange()"/>

</mx:HBox>

การนำไปใช้มีขั้นตอน ดังนี้

1. Binding ค่าที่เก็บจำนวนข้อมูล(ในตัวอย่างนี้เก็บไว้ที่ model.iRowTotal)ไว้กับ property ของ CMPageNavigator ชื่อ iRowTotal ตามโค้ด


BindingUtils.bindProperty(view.pn, "iRowTotal", model, "iRowTotal"); //typeof(view.pn) == CMPageNavigator

2. เรียก service นับจำนวนข้อมูล


service.searchCount(model.searchVO);

ส่งค่าที่ได้ให้กับตัวแปรที่เก็บจำนวนแถว


model.iRowTotal = int(e.result);

3. เขียนโค้ด Handler function เพื่อเรียกข้อมูลของหน้าที่ต้องการ


eventMap.mapListener(view.pn, "eventCurrent", onCurrent, Event);


private function onSearch():void{

trace("Mongoas3_Employees_Mediator.onSearch():void{");

model.limit = String(view.pn.iRow);

model.offset = String((view.pn.iPage - 1)*view.pn.iRow);

service.search(model.searchVO, model.limit, model.offset);

}

private function onCurrent(e:Event):void{

trace("Mongoas3_Employees_Mediator.onCurrent(e:Event)");

onSearch();

}

4. เอาผลลัพธ์ที่ได้จากการเรียกข้อมูลจากหน้าที่ต้องการใส่ไว้ในตัวแปรที่จะ Binding ไว้กับ dataProvider (ในที่นี้ืคือ model.ac)


if(e.result is Array){

model.ac = new ArrayCollection(e.result as Array);

}

5. Binding ตัวแปรที่เก็บข้อมูล(ในที่นี้คือ model.ac)ไว้กับ dataProvider ของตารางที่ต้องการแสดง

BindingUtils.bindProperty(view.pn, "iRowTotal", model, "iRowTotal"); //typeof(view.pn) == CMPageNavigator

BindingUtils.bindProperty(view.dg, "dataProvider", model, "ac");//typeof(view.dg) == DataGrid

6. การแสดงผล

Posted in AIR, Flex, ActionScript | Leave a comment

PHP Service for MongoDB prototype class

PHP Service ต้นแบบสำหรับติดต่อกับ Mongodb และไฟล์ที่เกี่ยวข้อง

Amf gateway 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". PATH_SEPARATOR . $_SERVER["DOCUMENT_ROOT"] . "/../../frameworks");

date_default_timezone_set("Asia/Bangkok");

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

require_once "Zend/Loader/Autoloader.php";
$zla = Zend_Loader_Autoloader::getInstance();
$zla->registerNamespace("Zend_");

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

if (!Zend_Session::isStarted())
 Zend_Session::start();

$service_dir_name = "mongoas3";

require_once $service_dir_name . "/const/DbMongoConst.php";

$dh = opendir($service_dir_name . "/vo/");
while (($file = readdir($dh)) !== false) {
 if (is_file($service_dir_name . "/vo/" . $file))
 require_once $service_dir_name . "/vo/" . $file;
}
closedir($dh);

$dh = opendir($service_dir_name);
while (($file = readdir($dh)) !== false) {
 if (is_file($service_dir_name . "/" . $file))
 require_once $service_dir_name . "/" . $file;
}
closedir($dh);

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

// Register ZendAMF Service classes
//$server->setClass("YourServiceClass");
//$server->setClass("AnotherServiceClass");

$dh = opendir($service_dir_name);
while (($file = readdir($dh)) !== false) {
 if (is_file($service_dir_name . "/" . $file))
 $server->setClass(str_replace(".php", "", $file));
}
closedir($dh);

// *ZAMFBROWSER IMPLEMENTATION*
// Add the ZendAmfServiceBrowser class to the list of available classes.
$server->setClass("ZendAmfServiceBrowser");

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

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

// End of gateway file
// With string length 2249

Database connection class


<?php

/**
 *
 * Enter description here ...
 * @package mongoas3.zendamf.mongoas3.const
 */
class DbMongoConst {

//const HOST = "localhost";
 //const USERNAME = "root";
 //const PASSWORD = "";
//const DB_NAME = "mongoas3";
 const HOST = "mongodb://username:password@ds033797.mongolab.com:33797,username:password@flame.mongohq.com:27074/knopsod";
 const USERNAME = "username";
 const PASSWORD = "password";
 const DB_NAME = "knopsod";
 const PREFIX = "";
 const MONGOAS3_EMPLOYEES = "mongoas3_employees";

}

PHP Service


<?php

/**
 *
 * Enter description here ...
 * @package mongoas3.zendamf.mongoas3
 */
class Mongoas3EmployeesService {

const TABLE = DbMongoConst::MONGOAS3_EMPLOYEES;
 const PK = "emp_auto_id";

var $db;
 var $now;
 var $session;
 var $m;
 var $collection;

function __construct() {
 try {
 $this->m = new Mongo(DbMongoConst::HOST);

$this->db = $this->m->selectDB(DbMongoConst::DB_NAME);

$this->collection = new MongoCollection($this->db, self::TABLE);
 } catch (Zend_Exception $exc) {
 return $exc->getTraceAsString();
 }
 }

function __destruct() {
 try {
 $this->m->close();
 } catch (Zend_Exception $exc) {
 return $exc->getTraceAsString();
 }
 }

/**
 *
 * Enter description here ...
 * @param array $arr
 * @return array
 */
 private function _clearEmpty($arr) {
 try {
 $arr_result = array();
 if (gettype($arr) != gettype($arr_result) || !count($arr))
 return $arr_result;

foreach ($arr as $key => $value) {
 if (!is_null($value) && $value != "")
 $arr_result[$key] = $value;
 }
 return $arr_result;
 } catch (Zend_Exception $exc) {
 return $exc->getTraceAsString();
 }
 }

/**
 *
 * Enter description here ...
 * @param string $value
 * @return boolean
 */
 private function _hadValue($value) {
 try {
 return $value && !is_null($value);
 } catch (Zend_Exception $exc) {
 return $exc->getTraceAsString();
 }
 }

/**
 *
 * Enter description here ...
 * @param object $vo
 * @return int
 */
 public function insert($vo) {
 try {
 $arr = get_object_vars($vo);

$arr = $this->_clearEmpty($arr);

 return $this->collection->insert($arr);
 } catch (Zend_Exception $exc) {
 return $exc->getTraceAsString();
 }
 }

/**
 *
 * Enter description here ...
 * @param array $voArr
 * @return array
 */
 public function insertRows($voArr) {
 try {
 $arr = array();

if (is_array($voArr)) {
 foreach ($voArr as $vo) {
 $arr[] = $this->insert($vo);
 }
 }

return $arr;
 } catch (Zend_Exception $exc) {
 return $exc->getTraceAsString();
 }
 }

/**
 *
 * Enter description here ...
 * @param object $vo
 * @return int
 */
 public function update($vo) {
 try {
 $arr = get_object_vars($vo);

 $arr = $this->_clearEmpty($arr);

 return $this->collection->update(array(self::PK=>$arr[self::PK]),
 $arr);
 } catch (Zend_Exception $exc) {
 return $exc->getTraceAsString();
 }
 }

/**
 *
 * Enter description here ...
 * @param array $voArr
 * @return array
 */
 public function updateRows($voArr) {
 try {
 $arr = array();

if (is_array($voArr)) {
 foreach ($voArr as $vo) {
 $arr[] = $this->update($vo);
 }
 }

return $arr;
 } catch (Zend_Exception $exc) {
 return $exc->getTraceAsString();
 }
 }

/**
 *
 * Enter description here ...
 * @param object $vo
 * @return int
 */
 public function delete($vo) {
 try {
 $arr = get_object_vars($vo);

 //return $this->collection->remove();

 return $this->collection->remove(array(self::PK=>$arr[self::PK]));
 } catch (Zend_Exception $exc) {
 return $exc->getTraceAsString();
 }
 }

/**
 *
 * Enter description here ...
 * @param array $voArr
 * @return array
 */
 public function deleteRows($voArr) {
 try {
 $arr = array();
 if (is_array($voArr)) {
 foreach ($voArr as $vo) {
 $arr[] = $this->delete($vo);
 }
 }

return $arr;
 } catch (Zend_Exception $exc) {
 return $exc->getTraceAsString();
 }
 }

/**
 *
 * Enter description here ...
 * @return int
 */
 public function selectCount() {
 try {
 return $this->collection->count();
 } catch (Zend_Exception $exc) {
 return $exc->getTraceAsString();
 }
 }

/**
 *
 * Enter description here ...
 * @param string $limit
 * @param string $offset
 * @return array
 */
 public function select($limit, $offset) {
 try {
 $cursor = $this->collection->find()->limit($limit)->skip($offset);

 $arr = array();
 foreach ($cursor as $doc) {
 $obj = (object) $doc;
 $arr[] = $obj;
 }

return $arr;
 } catch (Zend_Exception $exc) {
 return $exc->getTraceAsString();
 }
 }

/**
 *
 * Enter description here ...
 * @param object $vo
 * @return int
 */
 public function searchCount($vo) {
 try {
 $arr = get_object_vars($vo);
 $arr = $this->_clearEmpty($arr);

$criteria = array();
 foreach ($arr as $key => $value) {
 $regexp = new MongoRegex("/".$value."/"); //Same SQL : LIKE '%'.$value.'%'
 $criteria[$key] = $regexp;
 }

return $this->collection->count($criteria);
 } catch (Zend_Exception $exc) {
 return $exc->getTraceAsString();
 }
 }

/**
 *
 * Enter description here ...
 * @param object $vo
 * @param string $limit
 * @param string $offset
 * @return array
 */
 public function search($vo, $limit, $offset) {
 try {
 $arr = get_object_vars($vo);
 $arr = $this->_clearEmpty($arr);

$criteria = array();
 foreach ($arr as $key => $value) {
 $regexp = new MongoRegex("/".$value."/"); // Same SQL : '%'.$value.'%'
 $criteria[$key] = $regexp;
 }

$cursor = $this->collection->find($criteria)->limit($limit)->skip($offset);

 $arr = array();
 foreach ($cursor as $doc) {
 $obj = (object) $doc;
 $arr[] = $obj;
 }

return $arr;
 } catch (Zend_Exception $exc) {
 return $exc->getTraceAsString();
 }
 }

}

//End of service with text length = 11684

Reference : http://php.net/manual/en/mongo.sqltomongo.php

Posted in PHP, Zend Framwork | Leave a comment

อย่าลืม!! การเขียน PHPDocument ถ้าจะใส่ Tags (@params) ใน DocBlock ต้องใส่ให้ครบ

เวลาเราเขียน method ต่างๆ ขึ้นมาใน PHP Service เมื่อต้องการเขียนคำอธิบายบางอย่างไว้บน method เพื่อเวลากลับมาดูจะได้เข้าใจได้เร็วยิ่งขึ้น

ซึ่งมันมีข้อควรระวังตรงที่ว่า ถ้าไม่เขียน Zend_Amf ก็จะทำงานเป็นปกติ ไม่มีข้อผิดพลาดอะไร แต่ถ้าเขียนแล้วต้องเขียนให้ครบ ไม่เช่นนั้นแล้วจะไม่สามารถเรียกใช้งาน PHP Service ใดๆ ได้เลย เพราะเวลา Flex app. เรียก PHP Service gateway มันจะดูทุก Service class และทุก method ที่มี

ตย. ถ้าไม่เขียน Tags params ก็ไม่ต้องเขียน


public function search($vo, $limit, $offset) {
 try {

...

} catch (Zend_Db_Exception $exc) {
 return $exc->getTraceAsString();
 }

ตย. ถ้าเขียน Tags params ต้องเขียนให้ครบ


/**
 *
 * @param object $vo
 * @param string $limit
 * @param string $offset
 * @return array
 */

public function search($vo, $limit, $offset) {
try {

...

} catch (Zend_Db_Exception $exc) {
return $exc->getTraceAsString();
}

Posted in PHP, Zend Framwork | Leave a comment

Google Map หยุดพัฒนา ActionScript API

นักพัฒนาจากทาง Google ได้ประกาศออกมาแล้วว่าจะไม่มีการพัฒนา ActionScript API แล้ว ซึ่งทางบทความนี้ได้เผยแพร่ในบล็อก เมื่อวันที่ 2 ก.ย. ที่ผ่านมา

http://googlegeodevelopers.blogspot.com/2011/09/maps-api-for-flash-deprecation.html

นั่นหมายความว่าฟีเจอร์หรือความสามารถใหม่ๆ ที่มีอยู่หรือที่จะออกมาในอนาคต จะไม่สามารถเรียกใช้งานได้จากแอพลิเคชั่นที่สร้างจาก Flash Plateform ได้อีกต่อไป แต่ทาง Google ก็ได้เสนอทางเลือกใหม่ๆ ให้ใช้ JavaScript Map API v3 แทน โดยเหตุผลว่าทราฟฟิคที่เรียกใช้งานจาก Flash Plateform Application น้อยในขณะที่การใช้งานโดย JavaScript Map Application มีมากขึ้นเรื่อยๆ ซึ่งคงจะดีกว่าหากมุ่งเน้นพัฒนาให้รองรับกับความต้องการตรงนี้

อย่างไรก็ตามยังมีนักพัฒนาบางท่านได้สอบถาม Google ว่าถ้าไม่พัฒนาก็อยากให้ Open Source Flash API เสียเลย อาจจะเป็นประโยชน์ต่อนักพัฒนาที่อยากนำไปต่อยอด แต่ทาง Google ก็ยังไม่มีข้อสรุปว่าจะทำหรือไม่

รายละเอียดเพิ่มเติม http://code.google.com/apis/maps/documentation/flash/

Posted in AIR, Flex, ActionScript | Leave a comment

การพิจารณาเลือก ActionScript Framework เพื่อพัฒนา Flex App

ขอยาวหน่อย
ผมเคยใช้ MVC คือ Cairngorm
แล้วก็มาใช้ IoC คือ Parsley แล้วก็ RobotLegs (ตอนนี้ใช้ตัวนี้ ยังไม่ได้ศึกษาตัวอื่นเพิ่ม)
ทั้ง MVC และ IoC เป็น framework ที่เข้ามาช่วยในการพัฒนางานให้เป็นระบบ เคยหาอ่านตามเว็บฝรั่งเพื่อหาเหตุผลว่าทำไมต้องใช้ และประกอบการพิจารณาว่า framework แต่ละอันจะใช้กับงานอะไร พอสรุปได้คร่าวๆ ว่า

ข้อดี
-ทุกอันมีดีของตัวเอง เพียงแต่ทางทีมได้ทำความเข้าใจการพัฒนาระบบที่ตรงกัน
-สมาชิกในทีมสามารถทำงานแทนกันได้
-สะดวกในการกลับมาปรับปรุงในภายหลัง
-แยกโค้ดกับดีไซน์ออกจากกัน
-สนับสนุนความเป็นอิสระต่อกันระหว่างระบบย่อยต่างๆ
-เหมาะกับระบบใหญ่ที่ต้องใช้ทีมงานหลายคน เช่น พวก enterprise software
-ส่วนใหญ่จะเทคะแนนไปทาง IoC ซึ่งไม่แน่ใจว่าเพราะมันถูกจริตกับผมหรือเปล่า เลยเชียร์ 😀

ข้อเสีย
-มันเหมาะกับงานระบบ ซึ่ง flex เองอาจสู้ภาษาอื่นไม่ได้ตรงนี้ แต่ก็ใช่ว่าจะทำไม่ได้ ซึ่งก็เอาจุดเด่นเรื่องอื่น เช่น ความสวยงาม การแสดงผลเอฟเฟล็ค ฯลฯ มาแทน ซึ่งงานน่าจะไปในทางดีไซน์
-ต้องใช้เวลา ซึ่งบางทีเราทำคนเดียวหรือทีมงานขนาดเล็กอาจกระทบกับงานที่ทำอยู่
-การยึดตามรูปแบบของ framework บางทีอาจจำกัดอิสระในการสร้างสรรค์ความแปลกใหม่
———————-
การพิจารณาเลือก เขาบอกว่า no matter to choose ประมาณว่า ไม่มีสาระที่จะตัดสินว่าอะไรดีกว่ากัน เอามาพูดกันไม่จบหรอกว่าจะเลือกอะไร ขอให้เลือกเพื่อเป็นตัวบังคับกลายๆ ว่าพัฒนาไปในแนวทางเดียวกัน ดูเป็นระบบ ช่วยปรับปรุงแก้ไขง่าย ฯลฯ ตัวอย่างการถกเถียงก็อย่างเช่นว่า

บางตัวพัฒนาเวอร์ชั่นใหม่ๆ สนับสนุนการทำงานด้านต่างๆ หลากหลาย ซึ่งบางคนอาจกังวลว่าต้องใช้เวลาการเรียนรู้อยู่เรื่อยๆ อาจกระทบกับงานที่ทำอยู่ framework ไม่นิ่ง

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

Posted in AIR, Flex, ActionScript | Leave a comment

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 บ้างไรบ้าง แต่ก็ไม่หนักหนาอะไรเกินไป 😉

Posted in AIR, Flex, ActionScript, PHP, Zend Framwork | 1 Comment