Server-side typing for Flash Builder data access tools (PHP)

Flash Builder 4 introduces a rich set of tools for configuring access to data services. The Flash Builder documentation and tutorials can help you get started with writing PHP and ColdFusion services as well as configuring your applications to access these services.

However, the Flash Builder documentation examples and tutorials use client-side typing. You can also write PHP and ColdFusion services that implement server-side typing. Server-side typing simplifies the workflow in Flash Builder, and also provides server code that is easier to understand and maintain.

Client-side typing

Flash Builder uses client-side typing for services that do not specify data types for arguments or return values. To implement access to these services, Flash Builder needs to know the data types for the arguments and return values. Flash Builder tools introspects the services, prompting you to configure the necessary custom data types.

Here are links to the Flash Builder tutorials that use client-side typing:

This article provides documentation and an example for server-side typing in PHP. Later, I’ll provide a separate example for server-side typing in ColdFusion.

Server-side typing

Both PHP and ColdFusion allow you to define custom data types in server code. Flash Builder recognizes the custom data type during introspection of services. This simplifies the access to the service – you do not have to walk through wizard screens to configure custom data types.

This blog post contains an example for PHP that shows how to implement server-side typing. This basic application lists employees from a database. It also includes an input form to add new employees. This blog post contains the full source listing for both the client and server, plus a mini-tutorial.

StrongTypePHPApp.jpg

Database for the example
This example use the same database used in the Flex tutorials that illustrate Flash Builder access to data services.

PHP server-side typing example
For the current release, Flash Builder uses PHP DocBlock comments to recognize server-side data types. The sample service in this example shows how to use DocBlock comments to define a custom data type and also to specify a data type for function arguments and return values.

Using DocBlocks is a workaround for FB-23123, which prevents Flash Builder from recognizing typed parameters.

Once FB-23123 is fixed, you can uncomment the section of server code that shows the preferred way to use typed parameters.

To run the example:

  1. Create EmployeeServiceServerType.php, which is listed below. Place the file in the following folder in your web root:
    <webroot>PHPserverType/services/

  2. In Flash Builder, create a new project for the PHP application server type.

  3. From the Data/Services panel, connect to the data service. Using the Flash Builder Service wizard, navigate to EmployeeServiceServerType.php, which you created in step 1.

  4. In Design mode, add a DataGrid component. From the Data/Services view, drag the getAllEmployees() operation on to the DataGrid. Click OK.

    Note: Because you are using server-side typing, there is no need to configure the return type. For client-side typing, you have to configure the return type for getAllEmployees().

  5. (Optional) Using the Configure Columns option for the DataGrid, rename and reorder the columns.

  6. Generate an input form for the DataGrid:

    1. In Data/Services view, from the context menu for createEmployee(), select Generate Form.
    2. Deselect Form for Return Type. Click Next.
    3. Expand item. Deselect emp_id. Click Finish.
    4. Drag the newly added form beneath the DataGrid.
  7. (Optional) Rearrange the FormItems and edit the labels to meaningful names. This is easiest to do in Source mode of the editor.

  8. Provide initial values for the Input Form. In Source mode, add a creationComplete event to the Application tag. Generate an event handler. Add the following code to the event handler.

    protected functionapplication1_creationCompleteHandler(event:FlexEvent):void{lnameTextInput.text = "Last Name";fnameTextInput.text = "First Name";genderTextInput.text = "M";hdateTextInput.text = "2001-01-01";bdateTextInput.text = "2001-01-01";}
  9. Configure the DataGrid so it scrolls to newly added items. Add an updateComplete event handler to the DataGrid. Add the following code to the event handler:

    protected functiondataGrid_updateCompleteHandler(event:FlexEvent):void{dataGrid.verticalScrollPosition =dataGrid.maxVerticalScrollPosition;}
  10. Add the following line to the Button click handler to scroll the DataGrid after the new employee is added. (The Button click handler was created when you created the Input Form).

    protected function button_clickHandler(event:MouseEvent):void{employee.bdate = bdateTextInput.text;employee.fname = fnameTextInput.text;employee.lname = lnameTextInput.text;employee.gender = genderTextInput.text;employee.hdate = hdateTextInput.text;createEmployeeResult.token =employeeServiceServerType.createEmployee(employee);getAllEmployeesResult.token =employeeServiceServerType.getAllEmployees();}

    Note: In a real application, you would not put these two asynchronous operations in the same event handler. You are not guaranteed which operation completes first. However, for this trivial example, this seems to work.

  11. Save and run the application.
    Below is a full listing of the application, ServerSideTypePHP.mxml.


PHP service: EmployeeServiceServerType.php

<?php/*** This sample service contains functions that* illustrate typical service operations.** This code is for prototyping only.**  Authenticate users before allowing them to call these methods.*/class Employee {/*** @var int*/var $emp_id;/*** @var string*/var $bdate;/*** @var string*/var $fname;/*** @var string*/var $lname;/*** @var string*/var $gender;/*** @var string*/var $hdate;}class EmployeeServiceServerType {var $username = "root";var $password = "root";var $server = "localhost";var $port = "3306";var $databasename = "fb_tutorial_db";var $tablename = "employees";var $connection;/*** The constructor initializes the connection to database.* Everytime a request is received by Zend AMF, an instance of the* service class is created and then the requested method is called.*/public function __construct() {$this->connection = mysqli_connect($this->server,$this->username,$this->password,$this->databasename,$this->port);$this->throwExceptionOnError($this->connection);}/*** Returns all the rows from the table.** Add authorization or any logical checks* for secure access to your data** @return Employee[]*/public function getAllEmployees() {$stmt = mysqli_prepare($this->connection, "SELECTemployees.emp_no,employees.birth_date,employees.first_name,employees.last_name,employees.gender,employees.hire_dateFROM $this->tablename");$this->throwExceptionOnError();mysqli_stmt_execute($stmt);$this->throwExceptionOnError();$rows = array();mysqli_stmt_bind_result($stmt, $row->emp_id, $row->bdate,$row->fname, $row->lname, $row->gender, $row->hdate);while (mysqli_stmt_fetch($stmt)) {$rows[] = $row;$row = new stdClass();mysqli_stmt_bind_result($stmt, $row->emp_id, $row->bdate,$row->fname, $row->lname, $row->gender, $row->hdate);}mysqli_stmt_free_result($stmt);mysqli_close($this->connection);return $rows;}/*** @param Employee $item* @return int*/public function createEmployee($item) {$stmt = mysqli_prepare($this->connection,"INSERT INTO $this->tablename (birth_date,first_name, last_name, gender, hire_date)VALUES (?, ?, ?, ?, ?)");$this->throwExceptionOnError();mysqli_bind_param($stmt, 'sssss', $item->bdate,$item->fname, $item->lname, $item->gender, $item->hdate);$this->throwExceptionOnError();mysqli_stmt_execute($stmt);$this->throwExceptionOnError();$autoid = mysqli_stmt_insert_id($stmt);mysqli_stmt_free_result($stmt);mysqli_close($this->connection);return $autoid;}/** Typed params not supported in this Flash Builder Releasepublic function createEmployee(Employee $item) {$stmt = mysqli_prepare($this->connection,"INSERT INTO $this->tablename (birth_date,first_name, last_name, gender, hire_date)VALUES (?, ?, ?, ?, ?)");$this->throwExceptionOnError();mysqli_bind_param($stmt, 'sssss', $item->birth_date,$item->first_name, $item->last_name,$item->gender, $item->hire_date);$this->throwExceptionOnError();mysqli_stmt_execute($stmt);$this->throwExceptionOnError();$autoid = mysqli_stmt_insert_id($stmt);mysqli_stmt_free_result($stmt);mysqli_close($this->connection);return $autoid;}*//*** Utitity function to throw an exception if an error occurs* while running a mysql command.*/private function throwExceptionOnError($link = null) {if($link == null) {$link = $this->connection;}if(mysqli_error($link)) {$msg = mysqli_errno($link) . ": " . mysqli_error($link);throw new Exception('MySQL Error - '. $msg);}}}?>

PHP client: ServerSideTypePHP.mxml

<?xml version="1.0" encoding="utf-8"?><s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"xmlns:s="library://ns.adobe.com/flex/spark"xmlns:mx="library://ns.adobe.com/flex/mx"minWidth="955" minHeight="600"xmlns:employeeserviceservertype="services.employeeserviceservertype.*"xmlns:valueObjects="valueObjects.*"creationComplete="application1_creationCompleteHandler(event)"><fx:Script><![CDATA[import mx.controls.Alert;import mx.events.FlexEvent;protected functiondataGrid_creationCompleteHandler(event:FlexEvent):void{getAllEmployeesResult.token =employeeServiceServerType.getAllEmployees();}protected function button_clickHandler(event:MouseEvent):void{employee.bdate = bdateTextInput.text;employee.fname = fnameTextInput.text;employee.lname = lnameTextInput.text;employee.gender = genderTextInput.text;employee.hdate = hdateTextInput.text;createEmployeeResult.token =employeeServiceServerType.createEmployee(employee);getAllEmployeesResult.token =employeeServiceServerType.getAllEmployees();}protected functionapplication1_creationCompleteHandler(event:FlexEvent):void{lnameTextInput.text = "Last Name";fnameTextInput.text = "First Name";genderTextInput.text = "M";hdateTextInput.text = "2001-01-01";bdateTextInput.text = "2001-01-01";}protected functiondataGrid_updateCompleteHandler(event:FlexEvent):void{dataGrid.verticalScrollPosition =dataGrid.maxVerticalScrollPosition;}]]></fx:Script><fx:Declarations><s:CallResponder id="getAllEmployeesResult"/><employeeserviceservertype:EmployeeServiceServerTypeid="employeeServiceServerType"fault="Alert.show(event.fault.faultString + '\n' +event.fault.faultDetail)" showBusyCursor="true"/><valueObjects:Employee id="employee"/><s:CallResponder id="createEmployeeResult"/></fx:Declarations><mx:DataGrid x="19" y="21" id="dataGrid"creationComplete="dataGrid_creationCompleteHandler(event)"dataProvider="{getAllEmployeesResult.lastResult}"updateComplete="dataGrid_updateCompleteHandler(event)"><mx:columns><mx:DataGridColumn headerText="ID" dataField="emp_id"/><mx:DataGridColumn headerText="First Name" dataField="fname"/><mx:DataGridColumn headerText="Last Name" dataField="lname"/><mx:DataGridColumn headerText="Gender" dataField="gender"/><mx:DataGridColumn headerText="Hire Date" dataField="hdate"/><mx:DataGridColumn headerText="Birth Date" dataField="bdate"/></mx:columns></mx:DataGrid><mx:Form defaultButton="{button}" x="23" y="210"><mx:FormItem label="First Name"><s:TextInput id="fnameTextInput" text="{employee.fname}"/></mx:FormItem><mx:FormItem label="Last Name"><s:TextInput id="lnameTextInput" text="{employee.lname}"/></mx:FormItem><mx:FormItem label="Gender"><s:TextInput id="genderTextInput" text="{employee.gender}"/></mx:FormItem><mx:FormItem label="Hire Date"><s:TextInput id="hdateTextInput" text="{employee.hdate}"/></mx:FormItem><mx:FormItem label="Birth Date"><s:TextInput id="bdateTextInput" text="{employee.bdate}"/></mx:FormItem><s:Button label="Create Employee" id="button"click="button_clickHandler(event)"/></mx:Form></s:Application>