Mini Cookbook
UPDATED: 10/19/2008 I have updated this for easier viewing of the recipes, and I have also added more "Chapters" to this Mini Cookbook. See below for those links to the "Chapters".
- AMFPHP Recipes
- Web Service Recipes
Air RecipesCairngorm Recipes
Introduction
Here is a little thing that I made up for people using Flex/Flash and amfphp a little cheat sheet to use as a reference.
This has been completely re-done since it has been so popular. I have commented all of the code very well so that does most of the explaining of what code does what. I also added a few more calls such as sending email, sending images, sending files, searching and pagination.
For use without any framework and without a services-config.xml file in the compiler.
These examples are snippets from my Flash Remoting presentation and Snippr Air application. Source files will be provided at the end, also there will be a PDF version of this for easy printing.
So Please print and share with everyone who would find any use in this.
Table of Contents
AMFPHP Recipes
- Recipe 1.1. Installing AMFPHP
- Recipe 1.2. Connecting to MySQL
- Recipe 1.3. Using Classes with AMFPHP
- Recipe 1.4. Class Mapping
- Recipe 1.5. Building CRUD for AMFPHP
- Recipe 1.6. Making Calls
- Recipe 1.7. Creating a Service Proxy
- Recipe 1.8. Sending Emails with AMFPHP
- Recipe 1.9. Sending Images with AMFPHP
- Recipe 1.10. PHP to ActionScript Datatypes
Recipe 1.1 - AMFPHP Installation
Problem: You want to start using amfphp, but you do not know where to start.
Solution: Download amfphp1.9 from http://amfphp.org, unzip the folder, then drop it into your web root.
Server folder structure:
Recipe 1.2 - Connecting to MySQL
Problem: I have amfphp installed, now how they heck to I connect to my database?
Solution: Create a simple class file for connecting to MySQL, and include it in any of our classes that need database interaction.
Code: For connecting to a database make your connection inside the constructor or you could include it in a separate file.
Here is both ways of doing this.
Separate File
Database.php:
1: class Database
2: {
3:
4: public function Database(){}
5:
6: public function connect()
7: {
8: define( "db_host", "localhost" );
9: define( "db_user", "username" );
10: define( "db_pass", "password" );
11: define( "db_name", "snippr" );
12:
13: $dbc = mysql_connect( db_host, db_user, db_pass );
14: mysql_select_db( db_name );
15:
16: return $dbc;
17: }
18: }
Inside the separate file you return a variable that you will use as a link to connect to the database, very handy for connecting to one database in multiple files.
Inside Constructor
1: class SnipprService
2: {
3:
4: //Specify our table name
5: private $table = "snippr";
6:
7:
8: public function SnipprService()
9: {
10: mysql_connect( "localhost", "username", "password" );
11: mysql_select_db( "snippr" );
12: }
13: }
If you were to only have one service class for making calls, then it is very reasonable to include your connection inside your constructor.
Recipe 1.3 - Using AMFPHP with PHP Classes
Problem: You want to connect Flex to PHP code via AMFPHP, you need to write your PHP code as a PHP class. The remote methods that your ActionScript code calls are the methods of your custom classes.
Solution: Creating a class both in PHP and ActionScript.
A PHP class uses the following syntax:
1: class MyClass
2: {
3:
4: var local1 = 1;
5: var local2 = 2;
6:
7: public function MyClass()
8: {
9: // do something
10:
11: }
12:
13: public function someMethod( $arg )
14: {
15: // do something else
16:
17: }
18: }
An ActionScript class uses this following syntax:
1: package
2: {
3: class MyClass
4: {
5: public var someVariable:Number;
6: public var anotherVariable:Number;
7:
8: public function MyClass()
9: {
10: // do something
11:
12: }
13:
14: public function someFunction( arg:Object )
15: {
16: // do something else
17:
18: }
19: }
20: }
Recipe 1.4 - Class Mapping
Problem: You want to specify the data that is being returned from AMFPHP, but do not know how to go about doing so.
Solution: Class mapping allows you to use a remote PHP object to be mapped directly to your ActionScript object, without having to build generic code to iterate through all of the results coming back.
This is very useful when coding in Flex Builder, for one you have code hinting that will help you if you don’t remember the exact name of some of the objects and such, and for two it enforces the type that you have defined in your value object in ActionScript is what you are using it for. If that makes any sense.
If you specify that snippet_id inside your value object is a ByteArray.
ex. public var snippet_id:ByteArray;Then when you go to create a new snippet to send to AMFPHP you cannot set snippet_id to a String or Number.
ex. var snippet:SnippetVO = new SnippetVO();
snippet.snippet_id = txt_name.text;
Code:
SnippetVO.php
1: class SnippetVO
2: {
3: var $_explicitType="com.jonniespratley.snippr.vo.SnippetVO";
4:
5: var $snippet_id;
6: var $snippet_title;
7: var $snippet_code;
8: var $snippet_type;
9: var $snippet_created;
10: var $snippet_user;
11:
12: public function SnippetVO( $snip )
13: {
14: $this->snippet_id = $snip[snippet_id];
15: $this->snippet_title = $snip[snippet_title];
16: $this->snippet_code = $snip[snippet_code];
17: $this->snippet_type = $snip[snippet_type];
18: $this->snippet_created = $snip[snippet_created];
19: $this->snippet_user = $snip[snippet_user];
20: }
21: }
On this value object we have a mapObject function that maps our specified variables to the names of the table columns, so we will know exactly what is coming back and we can call those fields by the there variable.
Our explicitType is the location of our client side value object. Because when Flex sends the vo its "id" is on the client, so we are letting php know that this explicitType is what we are excepting.
SnippetVO.as
1: package com.jonniespratley.snippr.vo
2: {
3: [RemoteClass(alias="vo.SnippetVO")]
4:
5: /**
6: * VOs are used to create a layer of business objects that can be
7: * transferred between tiers, instead of using records, results sets, and datasets.
8: */
9: [Bindable]
10: public class SnippetVO
11: {
12: public var snippet_id:int;
13: public var snippet_title:String;
14: public var snippet_code:String;
15: public var snippet_type:String;
16: public var snippet_created:String;
17: public var snippet_user:String;
18:
19: public function SnippetVO()
20: {
21: }
22: }
23: }
For the ActionScript value object we have the same variables that we have inside of our php object, but in our constructor we have a function that loops through every item so later on when we pass this array to our array collection in our model, we will use a special little function for parsing up that data.
Our remote alias is set the the location of the server side value object.
Recipe 1.5 - Building CRUD for AMFPHP
Problem: You want to create, read, update and delete records in your MySQL database from AMFPHP.
Solution: Creating a CRUD service class for AMFPHP.
Code: All of the code is commented, and this should help you solve your problem and have full CRUD of your database.
SnipprService.php
1: class SnipprService
2: {
3:
4: //Specify our table name
5: private $table = "snippets";
6:
7: public function SnipprService()
8: {
9: mysql_connect("localhost", "spratley_guest", "guest");
10: mysql_select_db("spratley_snippr") ;
11: }
12:
13:
14: private function mapRecordSet( $recordset )
15: {
16: require_once( "../vo/SnippetVO.php" );
17: $list = array();
18:
19: while( $data = mysql_fetch_array( $recordset ) )
20: {
21: $vo = new SnippetVO( $data );
22: array_push( $list, $vo );
23:
24: }
25: return $vo;
26: }
27:
28:
29: public function getSnippets()
30: {
31: //We must specify our vo, because we need to map correctly
32: require_once( "../vo/SnippetVO.php" );
33:
34: $sql = mysql_query( "SELECT * FROM ". $this->table. "" );
35:
36: $result = array();
37:
38: while( $snip = mysql_fetch_array( $sql ) )
39: {
40: //Create a new snippet vo
41: $snippet = new SnippetVO( $snip );
42:
43: //Result is a snippet
44: $result[] = $snippet;
45: }
46: //Print out the result
47: return $result;
48: }
49:
50: //This is used for returning the created or updated snippet for flex
51: public function getOne( $id )
52: {
53: $rs = mysql_query( "SELECT * FROM ".$this->table." WHERE snippet_id = ".$id );
54: //Map the recordset to our vo
55: $list = $this->mapRecordSet( $rs );
56: //Return our vo
57: return $list;
58: }
59:
60: //Creates a new snippet
61: public function saveSnippet( $snippet )
62: {
63: require_once( "../vo/SnippetVO.php" );
64: //Check to see if the snippet has an id of 0
65: if ( $snippet[snippet_id] == 0 )
66: {
67: $query = "INSERT INTO ".$this->table."
68: ( snippet_title,
69: snippet_code,
70: snippet_type,
71: snippet_created,
72: snippet_user )
73: VALUES (
74: '".mysql_real_escape_string($snippet[snippet_title])."',
75: '".mysql_real_escape_string($snippet[snippet_code])."',
76: '".mysql_real_escape_string($snippet[snippet_type])."',
77: '".mysql_real_escape_string($snippet[snippet_created])."',
78: '".mysql_real_escape_string($snippet[snippet_user])."')";
79: if( !mysql_query( $query ) )
80: {
81: return false;
82: }
83: return $this->getOne( mysql_insert_id() );
84:
85: } else {
86: $id = $snippet[snippet_id];
87:
88: $query = "UPDATE ".$this->table." SET
89: snippet_title = '".mysql_real_escape_string($snippet[snippet_title])."',
90: snippet_code = '".mysql_real_escape_string($snippet[snippet_code])."',
91: snippet_type = '".mysql_real_escape_string($snippet[snippet_type])."',
92: snippet_created = '".mysql_real_escape_string($snippet[snippet_created])."',
93: snippet_user = '".mysql_real_escape_string($snippet[snippet_user])."'
94:
95: WHERE snippet_id =". $id;
96:
97: if( !mysql_query( $query ) )
98: {
99: return false;
100: }
101: //Return the created snippet
102: return $this->getOne( $id );
103: }
104: }
105:
106: public function removeSnippet($id)
107: {
108: $sql = mysql_query( "DELETE FROM ".$this->table." WHERE snippet_id = ".$id );
109:
110: if( !$sql )
111: {
112: // trigger_error("Unable to delete Snippets", E_USER_ERROR);
113: return "There was an error removing this snippet";
114: }
115: else return $id;
116: }
117: }
Recipe 1.6 - Making Calls
Problem:You want to actually start using AMFPHP since you have the database set up, a full CRUD service and all necessary value objects.
Solution: Before we can make any calls to AMFPHP we have to let Flex know where our gateway.php is, connecting to AMFPHP is probably one of the easiest things to do. It only takes 3 steps to connect to our server side remoting.
1. A NetConnection variable
ex. private var service:NetConnection = new NetConnection();
2. A String variable
ex. private var gateway:String = "http://localhost/amfphp/gateway.php";3. Now connect
ex. service.connect( gateway );
Code:
1: public function getSomeData():void
2: {
3: service.call ( "Path.ServiceName.MethodName",
4: new Responder( someResultHandler, someFaultHandler ), Arguments ) );
5: }
And we are now connected to AMFPHP, no service-config.xml, no <mx:RemoteObject/> tags, just as above and you will be connected.
To make actual calls to a method that you have created you use syntax like the following:
Recipe 1.7 - Creating a Service Proxy
Problem: You have all service methods on your server working as expected, but you have no access to them from Flex.
Solution: Create a Service Proxy in ActionScript containing all necessary functions for connecting, calling methods and handling the result for your views
Code:
SnipprService.as
1: package com.jonniespratley.snippr.services
2: {
3: import com.jonniespratley.snippr.model.ModelLocator;
4: import com.jonniespratley.snippr.vo.EmailVO;
5: import com.jonniespratley.snippr.vo.SnippetVO;
6:
7: import flash.net.NetConnection;
8: import flash.net.Responder;
9: import flash.utils.ByteArray;
10:
11: import mx.collections.ArrayCollection;
12: import mx.controls.Alert;
13: import mx.rpc.events.ResultEvent;
14:
15: /**
16: * This file is for use without! using the services-config.xml file
17: * @author Jonnie Spratley
18: * @website http://jonniespratley.com
19: *
20: */
21: public class SnipprService
22: {
23: /** NetConnection variable for creating our amfphp connection */
24: private static var _service:NetConnection;
25:
26: /** Location of our gateway for amfphp */
27: private var gateway:String = "http://localhost/snippr/amfphp/gateway.php";
28:
29: /** Our model so we can update it when we receive our data */
30: private var model:ModelLocator = ModelLocator.getInstance();
31:
32: /**
33: * Here we are creating a new connection to our amfphp service, when this is instantiated,
34: * it connects to our service.
35: *
36: */
37: public function SnipprService()
38: {
39: _service = new NetConnection();
40: _service.connect( gateway );
41: }
42:
43: /* **********************************************************************
44: * All Service Calls to AMFPHP (updated)
45: *
46: * This is where all of our service calls are taken, when our
47: * outside componets calls these functions all required arguemtns
48: * must be passed to properly send/update/delete data.
49: *
50: * If arguments are not present Flex wont compile. In all of our
51: * calls we attach assigned result handlers for the specific calls
52: * that we are making. They all use the same fault handler.
53: ************************************************************************/
54:
55:
56: /**
57: * Here we are calling the getSnippets on our server (amfphp)
58: * and setting the result and fault handlers
59: *
60: */
61: public function getSnippets():void
62: {
63: _service.call( "snippr.SnipprService.getSnippets",
64: new Responder( snippetResultHandler, snipprFaultHandler ) );
65: trace( "Gettings Snippets" );
66: }
67:
68: /**
69: * We take one argument here, and that is a snippet,
70: * because our server (amfphp) is expecting a snippetVO
71: *
72: * @param snippet snippetVO object
73: *
74: */
75: public function saveSnippet( snippet:SnippetVO ):void
76: {
77: _service.call( "snippr.SnipprService.saveSnippet",
78: new Responder( snippetSavedHandler, snipprFaultHandler ), snippet );
79: trace( "Saving Snippet" );
80: }
81:
82: /**
83: * We take one argument here, and that is the id of the snippet we are wanting to remove
84: *
85: * @param snippet_id the id to be removed
86: *
87: */
88: public function removeSnippet( snippet_id:uint ):void
89: {
90: _service.call( "snippr.SnipprService.removeSnippet",
91: new Responder( snippetRemoveHandler, snipprFaultHandler ), snippet_id );
92: trace( "Removing Snippet" );
93: }
94:
95: public function searchSnippets( args:Array ):void
96: {
97: _service.call( "snippr.SnipprService.searchSnippets",
98: new Responder( snippetResultHandler, snippetSearchHandler ), args );
99: }
100:
101: /* ***********************************************************
102: * Result and Fault Handlers
103: *
104: * This is where all of our result and fault handling is
105: * going to take place, we updating the model on the results
106: * that we get back. Or simply displaying to the user what
107: * comes back to Flex.
108: **************************************************************/
109:
110: /**
111: * We are handling the result coming back as an array of snippets,
112: * then we add our snippets to our model
113: *
114: * @param data the array of snippets
115: *
116: */
117: private function snippetResultHandler( data:Array ):void
118: {
119: model.snippetCollection = new ArrayCollection( data );
120: }
121:
122: private function snippetSearchHandler( data:Array ):void
123: {
124: trace( data );
125: }
126:
127:
128: /**
129: * Here we are handling the result and adding it to the value
130: * of serviceResponse in our model
131: *
132: * @param data the result from amfphp
133: */
134: private function snippetSavedHandler( data:Object ):void
135: {
136: ModelLocator.getInstance().serviceResponse = data.toString();
137: }
138:
139: /**
140: * Here we are handling the result that is being returned,
141: * which will be the id of the removed snippet,
142: * removing it from our model, at the snippet index
143: *
144: * @param data we are just refreshing/calling for the snippets again.
145: */
146: private function snippetRemoveHandler( data:Object ):void
147: {
148: getSnippets();
149: }
150:
151: /**
152: * Here we are alerting the user that there was an error connection to our server
153: *
154: * @param fault the fault object from the call
155: */
156: private function snipprFaultHandler( fault:Object ):void
157: {
158: Alert.show( "There was an error connecting to the server.",
159: "Snippr Service Error" );
160: }
161: }
162: }
Use the following example for a reference when creating your own service proxy class for the methods that you created.
Now if you wanted to use any of these calls in your view use the following.
SnippetForm.mxml
1: <!--SnippetFormProxyService-->
2: <mx:VBox xmlns:mx="http://www.adobe.com/2006/mxml" width="100%" height="100%"
3: creationComplete="init()"
4: xmlns:components="com.jonniespratley.snippr.view.components.*">
5:
6: <mx:Script>
7: <![CDATA[
8: import mx.validators.Validator;
9: import mx.controls.Alert;
10: import mx.rpc.events.FaultEvent;
11: import mx.rpc.events.ResultEvent;
12:
13: import com.jonniespratley.snippr.vo.SnippetVO;
14: import com.jonniespratley.snippr.model.ModelLocator;
15: import com.jonniespratley.snippr.services.SnipprService;
16:
17: /* Out Model so we can bind to the selectedSnippet */
18: [Bindable] private var model:ModelLocator = ModelLocator.getInstance();
19:
20: /* Our validation array to hold the values of our validators */
21: [Bindable] private var validators:Array = new Array();
22:
23: /* Our custom remote proxy service for connection to amfphp */
24: private var service:SnipprService;
25:
26: /*
27: On init we create a new instance of our service proxy
28: We alway set our validators to our validator array
29: */
30: private function init():void
31: {
32: service = new SnipprService();
33:
34: validators = [ titleV, authorV, codeV, typeV ];
35: }
36:
37: /*
38: When the save button is clicked instead of sending the data right away
39: we first check it to see if it is indeed valid. If our validation array
40: is empty, then we can go ahead and send our value object to amfphp, other
41: wise we need to alert the user that there are some errors in the form
42: */
43: private function checkForm():void
44: {
45: var vals:Array = new Array();
46: vals = Validator.validateAll( validators );
47:
48: //If no errors
49: if ( vals.length == 0 )
50: {
51: saveSnippet();
52: //cleanForms();
53: } else {
54: Alert.show( "Please correct invalid form entries", "Validation Error" );
55: }
56: }
57:
58: /* Clears all form inputs, and resets the selected index of the snippet list */
59: private function cleanForms():void
60: {
61: //Set the model.selectedSnippet to null, so we dont have any fields used up
62: //model.selectedSnippet = null;
63: txt_title.text = "";
64: txt_author.text = "";
65: txt_code.text = "";
66: txt_type.text = "";
67: }
68:
69: /*
70: The saveSnippet function that gets called when there is no errors in our form
71: This is one function that is going to handle both creating a new snippet, and
72: updating an existing one. Our server side php script says that if the snippetVO[snippet_id]
73: is equal to 0, then go ahead and insert it as a new snippet. But if the snippetVO[snippet_id]
74: is not set to 0, then update that snippet where the recieved id is equal to the id we are updating.
75: */
76: private function saveSnippet():void
77: {
78: /* If the selectedSnippet is empty create a new snippet */
79: if ( model.selectedSnippet == null )
80: {
81: var createS:SnippetVO = new SnippetVO();
82: createS.snippet_id = 0;
83: createS.snippet_title = txt_title.text;
84: createS.snippet_code = txt_code.text;
85: createS.snippet_user = txt_author.text;
86: createS.snippet_type = txt_type.text;
87:
88: /* Service proxy */
89: service.saveSnippet( createS );
90:
91: } else {
92: /* Set the snippet id to the value of the selected snippet_id */
93: var updateS:SnippetVO = new SnippetVO();
94: updateS.snippet_id = model.selectedSnippet.snippet_id;
95: updateS.snippet_title = txt_title.text;
96: updateS.snippet_code = txt_code.text;
97: updateS.snippet_user = txt_author.text;
98: updateS.snippet_type = txt_type.text;
99:
100: service.saveSnippet( updateS );
101:
102: }
103: /* Do nothing */
104: return;
105: }
106:
107:
108: /* ******************* MXML Result and Fault handlers ********************* */
109: private function onResult( event:ResultEvent ):void
110: {
111: Alert.show(event.result.toString(), "Success" );
112: }
113:
114: private function onFault( event:FaultEvent ):void
115: {
116: Alert.show( event.fault.faultString );
117: }
118:
119:
120:
121: ]]>
122: </mx:Script>
123:
124: <!--
125: MXML Remote Object This is the way you could make the same calls to amfphp, the only difference is
126: you need to use a service-config.xml file or just specify the endpoint inside the RemoteObject tag.
127: -->
128: <!--Remote Object-->
129: <mx:RemoteObject id="snipprSvc" source="snippr.SnipprService"
130: destination="amfphp"
131: showBusyCursor="true"
132: fault="onFault( event )">
133: <!--Methods that are on our server-->
134: <mx:method name="saveSnippet" result="onResult( event )"/>
135: <mx:method name="getSnippets" result="onResult( event )"/>
136: </mx:RemoteObject>
137:
138: <mx:ApplicationControlBar width="100%" styleName="formBar">
139: <mx:HBox width="100%" verticalAlign="middle">
140: <mx:Label text="Author:" fontWeight="bold"/>
141: <mx:TextInput id="txt_author"
142: text="{ model.selectedSnippet.snippet_user }"
143: width="100%"/>
144: </mx:HBox>
145: </mx:ApplicationControlBar>
146:
147: <mx:ApplicationControlBar width="100%" styleName="formBar">
148: <mx:HBox width="100%" verticalAlign="middle">
149:
150: <mx:Label text="Title:" fontWeight="bold"/>
151: <mx:TextInput id="txt_title"
152: text="{ model.selectedSnippet.snippet_title }"
153: width="100%"/>
154:
155: <mx:Label text="Type:" fontWeight="bold"/>
156: <mx:TextInput id="txt_type"
157: text="{ model.selectedSnippet.snippet_type }"
158: width="100%"/>
159:
160: <mx:Button id="btn_clear"
161: click="cleanForms()"
162: label="Clear"/>
163: <mx:Button id="btn_save"
164: click="checkForm()"
165: label="Save"/>
166: </mx:HBox>
167: </mx:ApplicationControlBar>
168: <mx:VBox width="100%" height="100%" label="Edit">
169: <mx:TextArea id="txt_code"
170: text="{ model.selectedSnippet.snippet_code }"
171: width="100%"
172: height="100%"
173: styleName="codeView"/>
174: </mx:VBox>
175:
176: <!-- Validators -->
177: <mx:StringValidator id="titleV"
178: source="{ txt_title }"
179: minLength="1"
180: maxLength="200"
181: required="true"
182: property="text"/>
183: <mx:StringValidator id="authorV"
184: source="{ txt_author }"
185: minLength="1"
186: maxLength="200"
187: required="true"
188: property="text"/>
189: <mx:StringValidator id="codeV"
190: source="{ txt_code }"
191: minLength="5"
192: required="true"
193: property="text"/>
194: <mx:StringValidator id="typeV"
195: source="{ txt_type }"
196: minLength="1"
197: maxLength="200"
198: required="true"
199: property="text"/>
200: </mx:VBox>
SnippetList.mxml
1: <!--SnippetList-->
2: <mx:VBox xmlns:mx="http://www.adobe.com/2006/mxml" width="200" height="100%" creationComplete="init()">
3:
4: <mx:Script>
5: <![CDATA[
6: import mx.controls.Alert;
7: import mx.events.CloseEvent;
8: import com.jonniespratley.snippr.services.SnipprService;
9: import com.jonniespratley.snippr.vo.SnippetVO;
10: import com.jonniespratley.snippr.events.SnippetGetEvent;
11: import com.jonniespratley.snippr.model.ModelLocator;
12:
13: /* Make a instance of our model for our data display */
14: [Bindable] private var model:ModelLocator = ModelLocator.getInstance();
15:
16: /* Make a variable to check weither there is a selected snippet or not */
17: [Bindable] private var isSelected:Boolean = false;
18:
19: /* Make variable of our service */
20: private var service:SnipprService;
21:
22: private function init():void
23: {
24: service = new SnipprService();
25: getSnippets();
26: }
27:
28: /* Send a call to get the snippets */
29: private function getSnippets():void
30: {
31: service.getSnippets();
32: }
33:
34: /*
35: * Alert the user and see what the response is
36: * if the response is yes, then delete, else return
37: */
38: private function removeSnippet():void
39: {
40: Alert.show( "Are you sure?", "Remove Snippet", 3, null, removeSnippetAlertHandler );
41: }
42:
43:
44: /*
45: * Remove snippet handler when the alert box is a yes
46: */
47: private function removeSnippetAlertHandler( event:CloseEvent ):void
48: {
49: if ( event.detail == Alert.YES )
50: {
51: service.removeSnippet( lt_snippets.selectedItem.snippet_id );
52: }
53: }
54:
55: /* Make sure we handle the selected snippet and bind it to our model */
56: private function selectHandler( event:Event ):void
57: {
58: isSelected = true;
59: model.selectedSnippet = event.target.selectedItem as SnippetVO;
60: }
61:
62: ]]>
63: </mx:Script>
64:
65: <!--Helper for Data Binding-->
66: <mx:Binding destination="lt_snippets.selectedItem" source="model.selectedSnippet"/>
67:
68: <!--List of Snippets-->
69: <mx:List id="lt_snippets"
70: dataProvider="{ model.snippetCollection }"
71: change="selectHandler( event )"
72: labelField="snippet_title"
73: width="100%"
74: height="100%"/>
75:
76: <!--Remove Button-->
77: <mx:Button label="Remove"
78: click="removeSnippet()"
79: enabled="{ isSelected }"
80: width="100%"/>
81: </mx:VBox>
Recipe 1.8 - Sending Emails with AMFPHP
Problem: You want to be able to stay in touch with your users and to be able to do this, sending emails would be a big plus.
Solution: Create a new method in your service class that allows AMFPHP to receive and send out email. Then add the same functions to Flex where the magic happens.
We use a file called eMail.php the information about that file is provided inside the source code at the end if you want to look.
Code:
EmailService.php:
1: class EmailService
2: {
3:
4: public function EmailService()
5: {
6: }
7: /**
8: * -> We take one argument here that is emailVO,
9: * Flex is sending over a value object that has all
10: * of the details needed to successfully send an email
11: * to the specified address in the vo.
12: *
13: * @param emailVO The email object from Flex.
14: */
15: public function sendEmail( $emailVO )
16: {
17: //Create a new email Object
18: $email = new eMail( "Flex Mail Form", $emailVO[email_from] );
19: {
20: //Create Subject Line
21: $email->subject( $emailVO[email_subject] );
22:
23: //To ( email address )
24: $email->to( $emailVO[email_to] );
25:
26: //From ( email address )
27: $email->bcc( $emailVO[email_from]);
28:
29: //HTML Message
30: $email->html( $emailVO[email_message] );
31:
32: //Send e-mail
33: $email->send();
34: }
35: return "Message Sent";
36: }
37: }
SnipprSerivce.as:
1: /**
2: * We take one argument here, and that is a email value object.
3: * We are passing this object to amfphp where our email will be sent
4: *
5: * @param email
6: *
7: */
8: public function sendEmail( email:EmailVO ):void
9: {
10: _service.call( "snippr.MediaService.sendEmail", new Responder( emailResultHandler, snippetSavedHandler ), email );
11: trace( "Sending Email" );
12: }
13:
14:
15: /**
16: * We are taking the data object as the result,
17: * tracing it and we could display an alert to
18: * the user showing him/her whatever message
19: * we want.
20: *
21: * @param data a message showing us the status
22: *
23: */
24: private function emailResultHandler( data:Object ):void
25: {
26: var result:ResultEvent = data as ResultEvent;
27: trace( data );
28: }
EmailVO.as
1: package
2: {
3: [Bindable]
4: public class EmailVO
5: {
6: public var email_to:String;
7: public var email_from:String;
8: public var email_subject:String;
9: public var email_message:String;
10:
11: public function EmailVO()
12: {
13: }
14: }
15: }
EmailForm.mxml:
1: <mx:TitleWindow xmlns:mx="http://www.adobe.com/2006/mxml"
2: width="400"
3: height="350"
4: showCloseButton="true"
5: close="PopUpManager.removePopUp( this )"
6: creationComplete="init()"
7: title="Send a Email">
8:
9: <mx:Script>
10: <![CDATA[
11: import mx.controls.Alert;
12: import mx.validators.Validator;
13: import mx.managers.PopUpManager;
14: import com.jonniespratley.snippr.vo.EmailVO;
15: import com.jonniespratley.snippr.services.SnipprService;
16:
17:
18: /* Our ServiceProxy variable */
19: private var service:SnipprService;
20:
21: /* Our validation array to hold the values of our validators */
22: [Bindable] private var validators:Array = new Array();
23:
24: /* When the component creation completes create a new service */
25: private function init():void
26: {
27: service = new SnipprService();
28:
29: validators = [ toV, fromV, subjectV, messageV ];
30: }
31:
32: /*
33: * -> When the save button is clicked instead of sending the data right away
34: * we first check it to see if it is indeed valid. If our validation array
35: * is empty, then we can go ahead and send our value object to amfphp, other
36: * wise we need to alert the user that there are some errors in the form
37: */
38: private function checkForm():void
39: {
40: var vals:Array = new Array();
41: vals = Validator.validateAll( validators );
42:
43: //If no errors
44: if ( vals.length == 0 )
45: {
46: //Send the Email
47: sendEmail();
48:
49: } else {
50: Alert.show( "Please correct invalid form inputs", "Validation Error" );
51: }
52: }
53:
54: /*
55: * -> When called we take our emailVO and build it up with the
56: * values from the inputs then we call the sendEmail(email) function
57: * in our SnipprService file, passing the packaged emailVO as the
58: * required argument.
59: */
60: private function sendEmail():void
61: {
62: /* Build up the value object for sending */
63: var emailVO:EmailVO = new EmailVO()
64: emailVO.email_to = txt_emailTo.text
65: emailVO.email_from = txt_emailFrom.text
66: emailVO.email_subject = txt_emailSubject.text
67: emailVO.email_message = txt_emailMessage.text
68:
69: /* Call the service passing the emailVO as the argument */
70: service.sendEmail( emailVO );
71: }
72:
73:
74: /* Resets the form and clears any validation */
75: private function resetForm():void
76: {
77: txt_emailTo.text = "";
78: txt_emailFrom.text = "";
79: txt_emailMessage.text = "";
80: txt_emailSubject.text = "";
81: txt_responseMessage.text = "";
82: }
83:
84:
85: ]]>
86: </mx:Script>
87:
88: <mx:Form width="100%" height="100%">
89: <!--Email To-->
90: <mx:FormItem label="To:"
91: width="100%" required="true">
92: <mx:TextInput id="txt_emailTo"
93: width="100%"/>
94: </mx:FormItem>
95:
96: <!--Email From-->
97: <mx:FormItem label="From:"
98: width="100%" required="true">
99: <mx:TextInput id="txt_emailFrom"
100: width="100%"/>
101: </mx:FormItem>
102:
103: <!--Email Subject-->
104: <mx:FormItem label="Subject:"
105: width="100%" required="true">
106: <mx:TextInput id="txt_emailSubject"
107: width="100%"/>
108: </mx:FormItem>
109:
110: <!--Email Message-->
111: <mx:FormItem label="Message:"
112: width="100%"
113: height="100%" required="true">
114: <mx:TextArea id="txt_emailMessage"
115: width="100%"
116: height="100%"/>
117: </mx:FormItem>
118: </mx:Form>
119:
120: <mx:ControlBar horizontalAlign="right" verticalAlign="middle">
121:
122: <!--Cancel Button-->
123: <mx:Button id="btn_cancel"
124: label="Cancel"
125: click="resetForm()"/>
126:
127: <!--Response Message-->
128: <mx:Text id="txt_responseMessage"
129: width="100%"
130: fontSize="14"
131: color="#4F0A59"
132: fontWeight="bold"
133: textAlign="center"/>
134:
135: <!--Send Button-->
136: <mx:Button id="btn_send"
137: label="Send"
138: click="checkForm()"/>
139: </mx:ControlBar>
140:
141: <!-- Validators -->
142: <mx:EmailValidator id="toV"
143: source="{ txt_emailTo }"
144: required="true"
145: property="text"/>
146: <mx:EmailValidator id="fromV"
147: source="{ txt_emailFrom }"
148: required="true"
149: property="text"/>
150: <mx:StringValidator id="subjectV"
151: source="{ txt_emailSubject }"
152: minLength="3"
153: maxLength="50"
154: required="true"
155: property="text"/>
156: <mx:StringValidator id="messageV"
157: source="{ txt_emailMessage }"
158: minLength="5"
159: maxLength="5000"
160: required="true"
161: property="text"/>
162: </mx:TitleWindow>
Recipe 1.9 - Uploading Images with AMFPHP
Problem: You want to be able to take a screenshot of your Air application because ZScreen does not capture it.
Solution: Add another method to your server side PHP script, and add the function for accessing this service in ActionScript then call this method from the view.
Code: You want to add this to your PHP file SnipprService.php:
1: //Specify our output temp directory
2: var $output_dir = "screenshots";
3: //Specify our output url
4: var $server_url = "http://snipper.jonniespratley.com/amfphp/services/snippr/";
5:
6: /** *******************************************************************************
7: * -> Save image from the given bytearray and return the path of the saved image
8: ***********************************************************************************/
9: public function takeScreenshot( $byteArray, $filename )
10: {
11: /** -> Check if our folder exists, and also if it is writeable */
12: if( !file_exists( $this->output_dir ) || !is_writeable( $this->output_dir ) )
13:
14: //If it is not there, throw a error
15: trigger_error ( "Please create a temp directory", E_USER_ERROR );
16:
17: //Set a data variable, and then set it to the value of byteArray (from Flex) the data inside the bytearray
18: $data = $byteArray->data;
19:
20: //Put the File in the Directory, and Rename it, what the User wanted the Name to be.
21: file_put_contents( $this->output_dir . "/$filename", $data );
22:
23: //Return the url to the user, so we can pop up a window with the new image!
24: return $this->server_url . $this->output_dir . "/$filename";
25: }
Now add this to your ActionScript file SnipprService.as:
1: /**
2: * We take two arguments here, one is a byte array and the other is the filename of the file
3: *
4: * @param bytes byte array
5: * @param filename filename of the file
6: *
7: */
8: public function takeScreenshot( bytes:ByteArray, filename:String ):void
9: {
10: _service.call( "snippr.MediaService.takeScreenshot",
11: new Responder( snapshotResultHandler, snipprFaultHandler ), bytes, filename );
12: trace( "Sending Screenshot" );
13: }
14:
15:
16: private function snapshotResultHandler( data:Object ) : void
17: {
18: var result:ResultEvent = data as ResultEvent;
19:
20: /* This is just for some fun */
21: var w:Window = new Window();
22: w.width = 600;
23: w.height = 500;
24: w.layout = "vertical";
25:
26: var i:Image = new Image();
27: i.source = data.result;
28:
29: var t:Text = new Text();
30: t.text = "Nice shot, and nice app,
31: Here is the link for the screenshot.\n "
32: + data.result;
33:
34: w.addChild( i );
35: w.addChild( t );
36: w.open();
37:
38:
39:
40: trace( data.result );
41: }
Now the view and you are sending screenshots Screenshot.mxml:
1: <mx:VBox xmlns:mx="http://www.adobe.com/2006/mxml"
2: width="100%"
3: creationComplete="init()">
4:
5: <mx:Script>
6: <![CDATA[
7: import com.jonniespratley.snippr.services.SnipprService;
8: import com.jonniespratley.snippr.events.SnapScreenshotEvent;
9:
10: import mx.utils.Base64Encoder;
11: import mx.controls.Image;
12: import mx.graphics.ImageSnapshot;
13: import mx.graphics.codec.PNGEncoder;
14: import mx.collections.ArrayCollection;
15: import mx.utils.UIDUtil;
16:
17: //Filename variable
18: private var fileName:String = "";
19: //Our service
20: private var service:SnipprService;
21:
22: //Connection
23: private function init():void
24: {
25: service = new SnipprService();
26: }
27:
28: /*
29: The takeScreenShot function that will create a new bitmap data variable,
30: and take the width and height of our stage (window) and create a image from it,
31: as well as encoding it into a bytearray for easy transfering to amfphp.
32: */
33: private function takeScreenshot():void
34: {
35: //Set the filename param on amfphp to the textinput text, add a .png cause its a png!
36: fileName = txt_filename.text +".png";
37:
38: //New Bitmap data variable set the the height and with of the stage
39: var bitmapData:BitmapData = new BitmapData( stage.width, stage.height );
40: //Draw the stage
41: bitmapData.draw( stage );
42:
43: //Create a byte array variable, then PNGEncoder to encode what, the bitmapData
44: var bytes:ByteArray = new PNGEncoder().encode( bitmapData );
45:
46: //Dispatch the SnapScreenshotEvent passing the bytes(byteArray),
47: //and the filename(string)
48: //(bytes, fileName);
49: //var evt:SnapScreenshotEvent = new SnapScreenshotEvent( bytes, fileName );
50: //evt.dispatch();
51: service.takeScreenshot( bytes, fileName );
52: }
53:
54: ]]>
55: </mx:Script>
56: <mx:HBox width="100%"
57: verticalAlign="middle">
58: <mx:Label
59: text="Screenshot Name"/>
60:
61: <!--The File name for the screenshot-->
62: <mx:TextInput id="txt_filename"
63: width="100%"/>
64: <mx:Button id="btn_capture"
65: label="Capture"
66: width="100%"
67: click="takeScreenshot()"/>
68: </mx:HBox>

