This page has been designed specifically for the printed screen. It may look different than the page you were viewing on the web.
Please recycle it when you're done reading.

The URI for this page is { http://jonniespratley.com }

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".

  1. AMFPHP Recipes
  2. Web Service Recipes
  3. Air Recipes
  4. Cairngorm 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 - 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:

amfphp_folderstructure 

Back to top

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.

Back to top

 

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:  }

Back to top

 

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.

Back to top

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:  }

 

 Back to top

 

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:

Back to top

 

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>

 

 Back to top

 

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>

 

 Back to top

 

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>