Статьи

Создание базового приложения CRUD с использованием Flex и PHP с Zend AMF

В Flash Builder 4 есть много новых функций, которые значительно упрощают создание приложений Flex, которые работают с данными из серверной части PHP, через стандартную настройку создания, чтения, обновления и удаления (CRUD). Однако иногда полезно начать с основ, чтобы вы могли гораздо больше оценить новые возможности, ориентированные на данные. В этой статье описывается настройка кода PHP на сервере и настройка Zend AMF для работы с приложением Flex. Затем вам понадобится создать код, необходимый на стороне клиента для обоих методов обслуживания вызовов и обработки ответов от сервера.

Примечание. Некоторые рекомендации по использованию класса RemoteObject были изменены с Flex 3 на Flex 4. В этой статье используется Flex 4, поэтому вы можете заметить некоторые отличия, если переходите на фон Flex 3.

Flash Builder 4

Примеры файлов:

Необходимые знания

Вам пригодятся некоторые знания PHP и предыдущий опыт работы с Flex.

 

Быстрый фон

Есть несколько разных способов соединить Flex и PHP вместе. Поскольку Flex поддерживает как службы на основе REST, так и веб-службы SOAP, вы можете использовать данные PHP в приложении Flex, используя любой из этих двух методов. Но третий способ обмена данными между Flex и PHP — через AMF. AMF — это легкий двоичный протокол, родной для Flash Player. Это означает, что Flash Player может анализировать, обрабатывать и отображать данные AMF очень быстро и намного быстрее, чем он может анализировать и манипулировать данными XML. Для использования AMF у вас должен быть сервер, который поддерживает создание объектов AMF. В PHP есть несколько решений AMF, но Adobe тесно сотрудничает с Zend, чтобы добавить поддержку AMF в Zend Framework, поэтому я буду использовать здесь.Классы Zend AMF в Zend Framework будут выполнять работу по созданию объектов AMF на сервере, чтобы вы могли отправлять их непосредственно в приложение Flex.

 

Настройка сервера

Прежде чем перейти к разработке приложений Flex, вам необходимо настроить серверную среду и PHP-код на стороне сервера.

Настройте веб-сервер и базу данных

Чтобы запустить пример приложения для этого руководства, вам понадобится веб-сервер с поддержкой PHP и база данных SQL. Если у вас еще нет доступа к такой среде, вы можете настроить Apache, MySQL и PHP вместе. См. Мою статью Flash Builder 4 и PHP — часть 1: разработка, ориентированная на данные, для указателей на ресурсы по настройке.

Эта статья также содержит инструкции по настройке базы данных MySQL для National Forests из файла примера php_demos.sql.

Установите код на стороне сервера

При создании базового приложения CRUD необходимо обратить внимание на два основных класса PHP. Первым является объект значения (VO), который является просто классом PHP, который представляет и хранит данные из базы данных. В данном случае это объект NationalForest с несколькими свойствами национальных лесов. Объект значения обычно близко соответствует схеме таблицы базы данных. В этом примере он также будет точно соответствовать объекту значения в приложении Flex. Когда данные переносятся с сервера PHP в приложение Flex, все свойства и типы данных сохраняются.

Объект значения включает в себя свойства для каждого столбца в таблице базы данных: идентификатор, состояние, область, установлено, closest_city и имя (см. Рисунок 1).

Рисунок 1. Пример приложения в браузере, отображающий данные из таблицы National Forests

Файл NationalForest.php реализует объект значения PHP для этого примера.

NationalForest.php
<?php
class NationalForest
{
public $id;
public $state;
public $area;
public $established;
public $closest_city;
public $name;

public function __construct()
{
$this->id = 0;
$this->state = "";
$this->area = 0;
$this->established = date("c");
$this->closest_city = "";
$this->name = "";
}
}
?>

Второй основной класс PHP — это сервис или объект доступа к данным (DAO). Поскольку это пример CRUD, будет четыре метода: создание, чтение, обновление и удаление. Каждый из этих методов вызывается приложением Flex для внесения соответствующих изменений в базу данных. Файл ForestService.php реализует класс PHP, который содержит эти четыре метода.

ForestService.php
<?php
include 'NationalForest.php';

class ForestService
{
// Usernames and passwords
protected $host = "localhost";
protected $username = "root";
protected $password = "root";
protected $db = "php_demos";

// Connection function for accessing the database
protected function connect()
{
date_default_timezone_set("UTC");

$connection = mysql_connect($this->host,$this->username,$this->password)
or die ("Unable to connect to database.");

$db = mysql_select_db($this->db)
or die ("Unable to select database.");
}

// Create method
public function createForest(NationalForest $forest)
{
$this->connect();
$query = sprintf("insert into national_forests (state, area, established, closest_city, name)
values ('%s','%s','%s','%s','%s')",
mysql_real_escape_string($forest->state),
mysql_real_escape_string($forest->area),
mysql_real_escape_string($forest->established),
mysql_real_escape_string($forest->closest_city),
mysql_real_escape_string($forest->name));
$rs = mysql_query($query)
or die ("Unable to complete query.");

// Use the id that we just created to set the id of the original forest object.
$forest->id = mysql_insert_id();

// And return the forest object
return $forest;
}

// Read method
public function readForests()
{
$this->connect();
$rs = mysql_query("select * from national_forests")
or die ("Unable to complete query.");

$national_forests = array();

while( $row = mysql_fetch_assoc($rs) )
{
$forest = new NationalForest();
$forest->id = $row['id']+0;
$forest->state = $row['state'];
$forest->area = $row['area']+0.0;
$forest->established = new DateTime($row['established']);
$forest->closest_city = $row['closest_city'];
$forest->name = $row['name'];

array_push($national_forests,$forest);
}

return $national_forests;
}

// Update method
public function updateForest(NationalForest $forest)
{
$this->connect();
$query = sprintf("update national_forests set
state = '%s', area = '%s', established = '%s', closest_city = '%s', name = '%s'
where id = '%s' ",
mysql_real_escape_string($forest->state),
mysql_real_escape_string($forest->area),
date("c",mysql_real_escape_string($forest->established)),
mysql_real_escape_string($forest->closest_city),
mysql_real_escape_string($forest->name),
mysql_real_escape_string($forest->id));
$rs = mysql_query($query)
or die ("Unable to complete query.");

return $rs;
}

// Delete method
public function deleteForest($id)
{
$this->connect();
$query = sprintf("delete from national_forests where id = '%s'",
mysql_real_escape_string($id));
$rs = mysql_query($query)
or die ("Unable to complete query.");

return $rs;
}

}

?>

Скопируйте ForestService.php и NationalForest.php на ваш веб-сервер.

 

Настройте шлюз

Теперь, когда ядро ​​PHP-кода готово, следующий шаг — открыть его для вашего приложения Flex. При использовании Flash Remoting Flash Player не взаимодействует напрямую со службой PHP. Вместо этого все проходит через шлюз. Шлюз выполняет преобразование объектов и перевод результатов из методов PHP в собственные объекты ActionScript. Настроить это довольно просто.

Во-первых, вам нужно иметь файл на вашем сервере, который будет действовать как шлюз и иметь доступ к инфраструктуре Zend, поскольку он будет использовать классы Zend_AMF . Вы можете загрузить Zend AMF как часть Zend Framework с веб-сайта Zend. В моей настройке у меня есть Zend Framework, объектный файл значений, службы и этот файл шлюза, все в одной папке на моем локальном сервере, но по соображениям безопасности, это хорошая идея, чтобы Zend Framework был недоступен для веб-доступа. папки.

Содержимое файла шлюза довольно простое. По сути, все, что делает этот файл, — это настроить сервер Zend_AMF, сопоставить ему класс (класс обслуживания выше, ForestService.php) и создать сопоставление между объектом значения ActionScript и объектом значения PHP. ActionScript VO еще не был создан, но он будет иметь то же имя, что и PHP VO. Файл должен находиться в веб-доступном месте; Я назвал мой index.php.

После установки Zend Framework скопируйте файл примера index.php на свой веб-сервер. Если все настроено правильно, при просмотре index.php вы должны увидеть «Конечная точка Zend Amf». Если вы этого не видите, значит, что-то не так, и вам необходимо перепроверить свою конфигурацию.

index.php
<?php
include 'Zend/Amf/Server.php';
include 'ForestService.php';

// Create a new instance of the Zend_Amf server
$server = new Zend_Amf_Server();

// This exposes the methods from ForestService to the Flex application
$server->setClass("ForestService");

// This maps the ActionScript type to the PHP type
// and allows us to transfer complex types between PHP and Flex
$server->setClassMap("NationalForest","NationalForest");

// The handle() statement sets it all in motion
echo($server->handle());
?>

 

Настройте services-config.xml

Последний необходимый фрагмент — это файл XML с именем services-config.xml. Этот файл сообщает приложению Flex, где искать методы, которые должен предоставить сервер PHP. Ниже приведен базовый файл services-config.xml, который вы можете использовать. Единственное, что необходимо изменить, — это URI конечной точки определения канала. Это должно указывать на местоположение, где вы выставили файл шлюза (в этом примере, index.php). Flash Builder будет использовать этот файл для создания правильных сопоставлений при компиляции приложения Flex. В результате файл services-config.xml не обязательно должен находиться в веб-доступном месте.

услуги-config.xml
<?xml version="1.0" encoding="UTF-8"?>
<services-config>
<services>
<service id="amfphp-flashremoting-service" class="flex.messaging.services.RemotingService" messageTypes="flex.messaging.messages.RemotingMessage">
<destination id="zendamf">
<channels>
<channel ref="zend-amf-channel"/>
</channels>
<properties>
<source>*</source>
</properties>
</destination>
</service>
</services>
<channels>
<channel-definition id="zend-amf-channel" class="mx.messaging.channels.AMFChannel">
<endpoint uri="http://localhost:8888/zend_mamp/php_crud/" class="flex.messaging.endpoints.AMFEndpoint"/>
</channel-definition>
</channels>
</services-config>

Это все, что требуется на стороне сервера для подключения PHP-сервера и приложения Flex.

 

Создание приложения Flex

Примечание. Чтобы создать приложение, следуйте инструкциям в этом разделе, скопировав и вставив код в созданный вами проект Flex. Однако, если вы предпочитаете, вы можете вместо этого импортировать весь проект, выбрав «Файл»> «Импортировать проект Flex» (FXP) в Flash Builder, а затем выбрав образец файла PHPCrud.FXP.

Выполните следующие шаги, чтобы создать приложение Flex:

  1. Выберите «Файл»> «Создать»> «Проект Flex».
  2. Введите MyPHPCrud в качестве имени проекта, а в качестве Типа сервера приложений выберите Нет / Другой.
  3. Нажмите Готово.

    Чтобы установить соединение с PHP с помощью Flash Remoting, Flex должен быть связан с файлом services-config.xml, который вы создали ранее.

  4. После создания проекта щелкните правой кнопкой мыши проект в проводнике пакетов и выберите «Свойства».
  5. Выберите «Компилятор Flex» и добавьте следующее в параметры «Дополнительные аргументы компилятора»:

    -services <absolute path to your services-config.xml file>

    Например, мой выглядит так:

    -services /Applications/MAMP/htdocs/zend_mamp/php_crud/services-config.xml
  6. Нажмите ОК.

    Если вы неправильно наберете абсолютный путь, Flash Builder выдаст ошибку, чтобы вы знали, как ее исправить, прежде чем запускать приложение.

Добавить объявления

Чтобы использовать данные AMF в приложении Flex, используйте тег RemoteObject. Во Flex 3 вы можете поместить теги RemoteObject в любое место вашего приложения, но во Flex 4 правила немного изменились. В новой версии Flex есть набор тегов <fx: Declarations>, куда и направляется весь невизуальный код. Кроме того, вместо использования тега <mx: operations> вы устанавливаете CallResponders, который позволяет вам прикреплять события к определенным функциям. Поэтому в этом примере я установил тег RemoteObject с тегом CallResponder для каждой операции.

Замените тег <fx: Declarations> в файле MyPHPCrud.mxml следующим текстом:

<fx:Declarations>
<!-- Place non-visual elements (e.g., services, value objects) here -->
<s:RemoteObject id="ro" destination="zendamf" source="ForestService"/>
<s:CallResponder id="createResponder" result="createResponder_resultHandler(event)" fault="ro_faultHandler(event)" />
<s:CallResponder id="readResponder" result="readResponder_resultHandler(event)" fault="ro_faultHandler(event)" />
<s:CallResponder id="updateResponder" result="updateResponder_resultHandler(event)" fault="ro_faultHandler(event)" />
<s:CallResponder id="deleteResponder" result="deleteResponder_resultHandler(event)" fault="ro_faultHandler(event)" />
</fx:Declarations>

Наиболее важной частью тега RemoteObject является свойство назначения. Это должно соответствовать идентификатору места назначения, которое было создано в файле services-config.xml. RemoteObject использует этот целевой идентификатор и файл services-config.xml, чтобы найти правильный PHP-файл для общения.

Я также добавил обработчики событий для каждого из объектов CallResponder, которые будут вызываться, когда приложение получит результат из класса PHP или обнаружит ошибку.

 

Добавьте элементы интерфейса

Это приложение собирается показать список NationalForests в компоненте DataGrid и позволить пользователю обновлять или удалять их с помощью формы. С помощью Flash Builder вы можете создавать DataGrid и другие элементы формы, перетаскивая их с панели «Компоненты» в представлении «Дизайн» или вручную кодируя их.

Добавьте следующий код для DataGrid и форму непосредственно под тегом <fx: Declarations>:

<mx:DataGrid id="dg" dataProvider="{arrNationalForests}" x="10" y="10" width="600" />
<mx:Form x="5" y="207">
<mx:FormItem label="Id">
<s:TextInput id="tiId"/>
</mx:FormItem>
<mx:FormItem label="Area">
<s:NumericStepper id="nsArea"/>
</mx:FormItem>
<mx:FormItem label="Name">
<s:TextInput id="tiName"/>
</mx:FormItem>
<mx:FormItem label="Closest City">
<s:TextInput id="tiClosestCity"/>
</mx:FormItem>
<mx:FormItem label="State">
<s:TextInput id="tiState"/>
</mx:FormItem>
<mx:FormItem label="Established">
<mx:DateField id="dfEstablished"/>
</mx:FormItem>
</mx:Form>
<s:Button id="btnCreate" x="258" y="210" label="Edit Local Data"/>
<s:Button id="btnUpdate" x="258" y="239" label="Update"/>
<s:Button id="btnDelete" x="258" y="268" label="Delete"/>

 

Создайте объект значения ActionScript

Следующим шагом является создание объекта значения в проекте Flex. В предыдущем разделе вы создали класс объекта значений PHP со свойствами объекта NationalForest. Одним из преимуществ использования AMF для связи между Flex и PHP является то, что вы можете отображать пользовательские типы на двух языках. Это означает, что PHP-класс NationalForest может быть сопоставлен непосредственно с классом ActionScript NationalForest, чтобы вы могли поддерживать типы в своем коде.

Чтобы это работало, вам нужно создать класс NationalForest в ActionScript:

  1. Щелкните правой кнопкой мыши папку src в Flash Builder и выберите «Создать»> «Пакет».
  2. Введите vo для имени и нажмите Finish.
  3. Щелкните правой кнопкой мыши по пакету vo и выберите «Создать»> «Класс ActionScript».
  4. Введите NationalForest для имени и нажмите Готово.

Это сгенерирует скелетный код для вас, а затем вы можете заполнить свойства. Вам также необходимо добавить метаданные в класс ActionScript, который связывает этот класс с классом из PHP. Сделайте это с помощью метаданных [RemoteClass] и присвойте свойству alias имя вашего класса PHP. Вы также захотите использовать атрибут метаданных [Bindable], чтобы вы могли использовать этот класс в качестве привязываемого значения в вашем приложении. Ваш класс должен выглядеть так:

package vo
{
[RemoteClass(alias="NationalForest")]
[Bindable]
public class NationalForest
{
public var id:int;
public var state:String;
public var area:Number;
public var established:Date;
public var closest_city:String;
public var name:String;
}
}

 

Позвоните в сервис

Теперь, когда VO создан, вы можете начать подготовку приложения к вызову сервисов из вашего класса PHP. Вернувшись в приложение Flex, создайте обработчик события для события creationComplete приложения.

Например:

<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"
creationComplete="application1_creationCompleteHandler(event)"
xmlns:vo="vo.*"
minWidth="955" minHeight="600">

Примечание: XMLNS: VO = «* VO » выше строка объявляет пространство имен для объекта значения.

Затем добавьте указанный ниже обработчик событий в блок fx: Script. Обработчик события вызывает метод чтения CRUD. Чтобы вызвать его, необходимо установить для свойства токена объекта readResponder фактический вызов службы, в данном случае метод readForests () компонента RemoteObject.

protected function application1_creationCompleteHandler(event:FlexEvent):void
{
readResponder.token = ro.readForests();
}

Теперь обработчики результатов должны быть созданы для readResponder. Код CallResponder в теге <fx: Declarations> создал заполнители для обработчиков результатов и ошибок, но их необходимо добавить в блок скрипта. Когда вызывается метод readForests (), результат должен быть установлен в массив, который в настоящее время связан как поставщик данных для сетки данных. Блок <fx: Script> нуждается в этом определении массива, а также обработчиках результата и ошибок для readResponder. Добавьте следующее в блок <fx: Script> над тегом <fx: Declarations>:

<fx:Script>
<![CDATA[
import flash.utils.describeType;
import flash.utils.getQualifiedClassName;

import mx.collections.ArrayCollection;
import mx.controls.Alert;
import mx.events.FlexEvent;
import mx.rpc.events.FaultEvent;
import mx.rpc.events.ResultEvent;

import vo.NationalForest;

[Bindable]
public var arrNationalForests:ArrayCollection = new ArrayCollection();


protected function application1_creationCompleteHandler(event:FlexEvent):void
{
readResponder.token = ro.readForests();
}

protected function readResponder_resultHandler(event:ResultEvent):void
{
arrNationalForests.source = event.result as Array;
}

]]>
</fx:Script>

 

Добавить поддержку для манипулирования данными

Теперь приложение может вводить данные, но также должно иметь возможность манипулировать данными. Теги формы уже созданы, но они требуют небольшой модификации. Всякий раз, когда элемент выбирается в DataGrid, форма должна обновляться выбранной информацией, чтобы ее можно было изменить. Сначала создайте объект NationalForest, который будет источником для формы.

В теге fx: Declarations добавьте следующий тег:

<vo:NationalForest id="nationalForest" />

Это создает объект NationalForest с идентификатором nationalForest. В Flex 4 добавлена ​​возможность двухстороннего связывания данных, чтобы при объединении двух элементов изменение одного из них приводило к обновлению другого. Двустороннее связывание данных позволяет пользователям легко вносить изменения в форму, а те изменяют объект NationalForest, чтобы его можно было использовать для вызова классов PHP. Вам просто нужно изменить некоторые свойства формы, привязав их к свойствам объекта NationalForest.

Обновите <mx: Form> следующим образом:

<mx:Form x="5" y="207">
<mx:FormItem label="Id">
<s:TextInput id="tiId" text="{nationalForest.id}" enabled="false"/>
</mx:FormItem>
<mx:FormItem label="Area">
<s:NumericStepper id="nsArea" value="@{nationalForest.area}" maximum="10000000" minimum="0" stepSize="10" />
</mx:FormItem>
<mx:FormItem label="Name">
<s:TextInput id="tiName" text="@{nationalForest.name}"/>
</mx:FormItem>
<mx:FormItem label="Closest City">
<s:TextInput id="tiClosestCity" text="@{nationalForest.closest_city}"/>
</mx:FormItem>
<mx:FormItem label="State">
<s:TextInput id="tiState" text="@{nationalForest.state}"/>
</mx:FormItem>
<mx:FormItem label="Established">
<mx:DateField id="dateEstablished" selectedDate="@{nationalForest.established}"/>
</mx:FormItem>
</mx:Form>

Знак @ создает двустороннюю привязку данных. Теперь любое изменение этого объекта NationalForest заполнит форму с правильными данными. Чтобы упростить изменение данных, вы можете добавить обработчик событий в компонент DataGrid, чтобы любой выбранный элемент затем заполнялся в форме.

Просто измените тег DataGrid, чтобы изменить объект NationalForest на selectedItem, когда что-то изменится в DataGrid.

<mx:DataGrid id="dg" x="10" y="10" width="600" dataProvider="{arrNationalForests}" 
change="nationalForest = dg.selectedItem as NationalForest;"/>

 

Добавьте оставшиеся операции CRUD

Осталось только добавить код для остальных операций CRUD. Три кнопки, которые являются частью формы, соответствуют оставшимся операциям: создание, обновление и удаление. Используя тот же формат, что и выше (со свойством token для callResponder), довольно просто вызывать функции PHP при нажатии этих кнопок.

Замените кнопки следующим кодом:

<s:Button id="btnCreate" x="258" y="210" label="Create" click="createResponder.token = ro.createForest(nationalForest);"/>
<s:Button id="btnUpdate" x="258" y="239" label="Update" click="updateResponder.token = ro.updateForest(nationalForest);"/>
<s:Button id="btnDelete" x="258" y="268" label="Delete" click="deleteResponder.token = ro.deleteForest(nationalForest.id);"/>

Оглядываясь назад на код PHP, методы create и update принимают объект типа NationalForest. Поскольку AMF допускает сопоставление типов, объект NationalForest, отправленный из Flex, будет соответствовать классу NationalForest, что ожидают методы PHP. Метод delete принимает идентификатор, поэтому метод deleteForest просто передает идентификатор текущего выбранного элемента.

Последний фрагмент головоломки — обработка результатов для этих трех методов. Основная проблема заключается в том, что сетка данных должна обновляться при каждом изменении записи. К счастью, двусторонняя привязка данных заботится о большей части этого. Это действительно подпадает под широкую категорию управления данными. Flash Builder 4 имеет несколько отличных встроенных инструментов для управления данными, но для простоты в этом руководстве я использовал очень простую реализацию управления данными.

Ответчик для метода createForest добавляет новый лес в массив, обновляет объект NationalForest и прокручивает сетку данных до новой записи. Добавьте следующее в блок <fx: Script>:

protected function createResponder_resultHandler(event:ResultEvent):void
{
arrNationalForests.addItem(event.result);
nationalForest = event.result as NationalForest;
dg.scrollToIndex(arrNationalForests.length);
}

Два респондента для метода updateForest и метода deleteForest просты. На самом деле респонденту updateForest не требуется никакой код, поэтому я просто создал оператор трассировки. Но для удалений сетка данных не будет обновляться автоматически, поэтому респондент может быть использован для обновления сетки данных путем удаления выбранного элемента.

Добавьте следующие два метода:

protectedfunction updateResponder_resultHandler(event:ResultEvent):void
{
trace('updated successfully');
}


protectedfunction deleteResponder_resultHandler(event:ResultEvent):void
{
arrNationalForests.removeItemAt(dg.selectedIndex);
}

И, наконец, вам нужно добавить обработчик ошибок:

protected function ro_faultHandler(event:FaultEvent):void
{
Alert.show(event.fault.message);
}

Вот и все. Теперь вы сможете запускать приложение, просматривать данные, создавать новые записи, обновлять записи и удалять записи.

 

Куда пойти отсюда

Теперь, когда вы узнали, как создать базовое приложение CRUD, работающее поверх AMF между Flex и PHP с нуля, вы можете узнать больше о том, как функции ориентированной на данные разработки в Flash Builder 4 упрощают процесс. Вы можете узнать больше в моей статье Flash Builder 4 и PHP — Часть 1. Разработка, ориентированная на данные .