Pull to refresh

Создание приложений с помощью PHP и Flex

Reading time9 min
Views1.3K
Original author: Jack Herrington

Создание приложений с помощью PHP и Flex.



Посмотрим правде в глаза: интерактивные графики и панели управления всегда было сложно применять вместе в web-приложениях. Конечно, существуют графические библиотеки для PHP, но чтобы получить что-то действительно хорошо выглядящее и то, с чем пользователь мог бы поэкспериментировать, очень сложно. Или по крайней мере было сложно вчера. Сегодня, я покажу как использовать комбинацию PHP в качестве backend'а и Adobe Flex в качестве frontend'а, которая будет отображать интерактивный 3D-график.

Чтобы начать, мне нужны какие-нибудь данные. Поэтому я создам базу данных traffic, в ней будет одна таблица traffic, в которой будут храниться данные о просмотре страниц и тому подобном за каждый день. MySQL-схема приведена в листинге 1.

Листинг 1. traffic.sql

DROP TABLE IF EXISTS traffic;

CREATE TABLE traffic (
day DATE,
users INT,
views INT,
pages INT,
xmlpages INT
);


Здесь пять полей: дата, количество пользователей, количество просмотров страниц, количество обработанных страниц и количество XML-страниц. (Естественно, вы можете использовать любые поля.) Теперь, чтобы что-то выводить, я заполню таблицу тестовыми данными. Для этого я запущу скрипт loader.php (листинг 2).

Листинг 2. loader.php

<?php
require_once("MDB2.php");

$dsn = 'mysql://root@localhost/traffic';
$mdb2 =& MDB2::factory($dsn);

$dsth =& $mdb2->prepare( "DELETE FROM traffic" );
$dsth->execute( array( ) );

$sth =& $mdb2->prepare( "INSERT INTO traffic VALUE (?,?,?,?,?)" );

$users = 100;
$views = 10000;
$pages = 5000;
$xmlpages = 300;
for( $d = 1; $d <= 30; $d++ ) {
$date = "2008-04-".$d;
$sth->execute( array( $date, $users, $views, $pages, $xmlpages ) );
$users += ( rand( 20, 100 ) - 30 );
$views += ( rand( 200, 1000 ) - 300 );
$pages += ( rand( 100, 500 ) - 150 );
$xmlpages += ( rand( 60, 300 ) - 90 );
}
?>


Этот скрипт просто подключается к базе данных, удаляет все данные из таблицы, а потом заполняет ее случайными данными за месяц. Чтобы графики не выглядели беспорядочно, я использую такой способ, в котором каждое следующее число сдвигается на случайную величину относительно предыдущего. Таким образов график всегда идет вверх.

Теперь, когда данные сгенерированы и хранятся в базе данных, мне нужен способ достать их оттуда. Первый способ — через XML с помощью страницы traffic.php (листинг 3).

Листинг 3. traffic.php

<?php
require_once("MDB2.php");

$dsn = 'mysql://root@localhost/traffic';
$mdb2 =& MDB2::factory($dsn);

$dom = new DomDocument();
$dom->formatOutput = true;

$root = $dom->createElement( "traffic" );
$dom->appendChild( $root );

$sth =& $mdb2->prepare( "SELECT * FROM traffic ORDER BY day" );
$res = $sth->execute( $id );
while ($row = $res->fetchRow(MDB2_FETCHMODE_ASSOC)) {
$dn = $dom->createElement( "day" );
$dn->setAttribute( 'day', $row['day'] );
$dn->setAttribute( 'users', $row['users'] );
$dn->setAttribute( 'views', $row['views'] );
$dn->setAttribute( 'pages', $row['pages'] );
$dn->setAttribute( 'xmlpages', $row['xmlpages'] );
$root->appendChild( $dn );
}

header( "Content-type: text/xml" );
echo $dom->saveXML();
?>


Эта страница соединяется с базой данных, используя PEAR::MDB2, и получает все данные. Затем создается объект DomDocument, и данные добавляются в него.

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

Когда я запускаю скрипт из командной строки, то происходит примерно следующее:

% php Traffic.php
<?xml version="1.0"?>
/>
/>
/>
/>
...




Прекрасно. Теперь у меня есть данные в XML, которые я могу передать во Flex.

Создание интерфейса на Flex, версия 1



Давайте будем честными: у кого есть время или желание создавать библиотеку для трехмерных графиков? Так что давайте использовть уже существующую. Я выбрал для примера библиотеку Elixir от ILOG. Это коммерческий продукт, но у него есть триальная версия, так что можно бесплатно скачать и поэкспериментировать с ней.

Построение приложения на Flex начаинается с создания проекта Flex в Adobe Flex Builder version 3. Доступны как браузерные проекты, так и основанные на Adobe AIR — все зависит от того, где вы будете их использовать — оба будут работать.

Затем, я открываю диалог Project Properties и нажимаю на вкладку Flex Build Path. Здесь я выбираю Library Path, а потом нажимаю Add SWC, чтобы добавить библиотеки ILOG Elixir. Их две: главные классы и библиотека локализации для английского и японского языков. В результате должно появится что-то типа такого.



Рисунок 1. Добавление библиотек Elixir в проект.


Теперь библиотека Elixir подключена, и я могу написать код, который будет подключаться к моим XML-данным и выводить их как трехмерный график. Этот код показан в листинге 4.

Листинг 4. traffic.mxml

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="horizontal"
xmlns:ilog="http://www.ilog.com/2007/ilog/flex" creationComplete="trafficReq.send()">
<mx:Script>
<![CDATA[
import mx.rpc.events.ResultEvent;

private var trackPt:Point = null;

private function onTraffic( event:ResultEvent ) : void {
var days:Array = [];
for each( var day:XML in event.result..day ) {
days.push( { day:day.@day.toString(),
users:parseInt(day.@users),
pages:parseInt(day.@pages),
views:parseInt(day.@views),
xmlpages:parseInt(day.@xmlpages) } );
}
chart.dataProvider = days;
}
private function onMouseUp( event:MouseEvent ) : void { trackPt = null; }
private function onMouseMove( event:MouseEvent ) : void {
if ( trackPt == null ) return;
chart.rotationAngle += ( event.localX - trackPt.x );
trackPt = new Point( event.localX, event.localY );
}
private function onMouseDown( event:MouseEvent ) : void {
trackPt = new Point( event.localX, event.localY );
}
]]>
</mx:Script>
<mx:HTTPService id="trafficReq" resultFormat="e4x" url="http://localhost/traffic/traffic.php" result="onTraffic(event)" />
<ilog:LineChart3D rotationAngle="10" width="100%" height="100%" id="chart" mouseDown="onMouseDown(event)"
mouseUp="onMouseUp(event)" mouseMove="onMouseMove(event)" showDataTips="true">
<ilog:horizontalAxis>
<mx:CategoryAxis categoryField="day" displayName="Day" />
</ilog:horizontalAxis>
<ilog:series>

<ilog:LineSeries3D xField="day" yField="users" displayName="Users" />
<ilog:LineSeries3D xField="day" yField="pages" displayName="Pages" />
<ilog:LineSeries3D xField="day" yField="views" displayName="Views" />
<ilog:LineSeries3D xField="day" yField="xmlpages" displayName="XML Pages" />
</ilog:series>
</ilog:LineChart3D>
<mx:Legend dataProvider="{chart}"/>
</mx:Application>


Код начинается с вызова метода send объекта trafficReq компонента HTTPService. Этот сервис указывает на URL страницы PHP, которая возвращает данные в XML. Когда данные получены, вызывается метод onTraffic, который преобразует XML в набор данных, которые готовы для использования в объекте chart. Этот объект определен внизу файла.

Я определил chart как LineChart 3D. Возможны и другие варианты: диаграмма с областями, гистограммы, секторные диаграммы и множество других, как двухмерных, так и трехмерных. Также есть средства для создания карт, древовидных схем, диаграмм Гантта и других способов построения графиков. Примеры, которые поставляются с Elixir, вас поразят.

Вернемся к коду. Я добавил несколько обработчиков действий мыши — нажатий и перемещений, которые меняют угол поворота графика. Это дает ползователям возможность немного повертеть график. Вы можете использовать эти методы для изменения угла просмотра так, чтобы пользователь мог смотреть сверху или сбоку.

Когда я запускаю код во Flex Builder 3, я вижу примерно следующее.


Неплохо, правда? Просто подумайте как это будет выглядеть, когда вы подключите реальные данные с вашего сайта.

Теперь, чтобы узнать немного больше о PHP и Flex, давайте упростим передачу данных.

Получение данных с помощью AMF



Во Flash'е есть бинарный формат передачи данных, который называется Action Message Format (AMF). Он позволяет приложениям на Flex и Adobe Flash передавать и получать целые объекты от сервера с помощью вызовов, которые похожи на обычные вызовы методов. Чтобы соединить приложение с PHP и моим тестовым набором данных, я скачал и установил AMFPHP.

И еще я добавил класс TrafficService в папку services в AMFPHP. Код этого класса вы увидите в листинге 5.

Листинг 5. TrafficService.php

<?php
require_once("MDB2.php");
include_once(AMFPHP_BASE . "shared/util/MethodTable.php");
class TrafficService
{
function getTraffic()
{
$dsn = 'mysql://root@localhost/traffic';
$mdb2 =& MDB2::factory($dsn);
$sth =& $mdb2->prepare( "SELECT * FROM traffic ORDER BY day" );
$res = $sth->execute( $id );
$days = array();
while ($row = $res->fetchRow(MDB2_FETCHMODE_ASSOC)) { $days []= $row; }
return $days;
}
}


Этот код похож на тот, который формировал XML, за исключением того, что здесь я никак не форматирую данные. Я просто возвращаю массив.

Чтобы протестировать код, я использую браузер, который включен в AMFPHP.

Рисунок 3. Просмотр сервиса AMF traffic


Как вы видите, вы можете вызвать метод getTraffic() и вернуть все записи из базы данных как массив объектов ActionScript. Очень просто, очень быстро.

Подключение к AMFPHP



Подключение к сервису AMFPHP traffic требует небольших изменений в предыдущих примерах. Они показаны в листинге 6.

Листинг 6. Traffic_ro.php

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="horizontal"
xmlns:ilog="http://www.ilog.com/2007/ilog/flex" creationComplete="trafficRO.getTraffic.send()">
<mx:Script>
<![CDATA[
import mx.rpc.events.ResultEvent;

private var trackPt:Point = null;

private function onTraffic() : void {
chart.dataProvider = trafficRO.getTraffic.lastResult;
}
...
]]>
</mx:Script>
<mx:RemoteObject id="trafficRO"
endpoint="http://localhost/amfphp/gateway.php"
source="traffic.TrafficService" destination="traffic.TrafficService"
showBusyCursor="true">
<mx:method name="getTraffic" result="onTraffic()" />
</mx:RemoteObject>
<ilog:LineChart3D ...>
...
</ilog:LineChart3D>
<mx:Legend dataProvider="{chart}"/>
</mx:Application>


HTTPService заменен на RemoteObject, который ссылается на сервер AMFPHP и определяет метод, к которому я хочу обратиться. А метод onTraffic теперь просто помещает в свойство dataProvider объекта chart данные, которые получены от сервера. Когда я запускаю этот код во Flex Builder, результат остается таким же, как и в первом примере. Отличие в том, что код теперь понятнее, а передача данных быстрее и меньше, чем с помощью XML. Чтобы немного улучшить приложение, я добавил слайдер, который позволяет пользователю просматривать только определенный набор данных и делать это динамически. Новый код показан в листинге 7.

Листинг 7. traffic_ro2.php

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="horizontal"
xmlns:ilog="http://www.ilog.com/2007/ilog/flex" creationComplete="trafficRO.getTraffic.send()"
xmlns:flexlib="flexlib.controls.*">
<mx:Script>
<![CDATA[
import mx.rpc.events.ResultEvent;

private var trackPt:Point = null;
private var days:Array = [];

private function onTraffic() : void {
days = trafficRO.getTraffic.lastResult as Array;
dateRange.minimum = 0;
dateRange.maximum = days.length;
dateRange.values[0] = 0;
dateRange.values[1] = days.length;
chart.dataProvider = days;
}
private function onDateRangeChange() : void {
chart.dataProvider = days.slice( dateRange.values[0], dateRange.values[1] );
}
...
]]>
</mx:Script>
<mx:RemoteObject id="trafficRO"
endpoint="http://localhost/amfphp/gateway.php"
source="traffic.TrafficService" destination="traffic.TrafficService"
showBusyCursor="true">
<mx:method name="getTraffic" result="onTraffic()" />
</mx:RemoteObject>
<mx:VBox width="100%" height="100%">
<mx:HBox>
<mx:Label text="Date Range" />
<flexlib:HSlider id="dateRange" thumbCount="2" width="300" liveDragging="true" change="onDateRangeChange()"
snapInterval="1" />
</mx:HBox>
<ilog:LineChart3D ...>
...
</ilog:LineChart3D>

</mx:VBox>
<mx:Legend dataProvider="{chart}"/>
</mx:Application>


Я добавил объект HSlider с двумя ползунками наверх страницы. Когда ползунки перемещаются, происходит вызов метода onDateRangeChange, который обновляет график и показывает только данные между двумя датами.

Класс HSlider есть в билиотеке FlexLib. FlexLib — это набор Flex-классов, которые улучшают и расширяют оригинальные средства Flex 3. В данном случае, это позволяет выделять диапазон с помощью перемещения ползунков мышкой.

То, как работает этот код, можно увидеть на рисунке 4.

Рисунок 4. График с возможностью выбора диапазона дат


На этом рисунке показываются только данные за несколько последних дней. Но я могу добавить еще данных перемещением ползунков влево или вправо.

Куда двигаться дальше


Комбинация технологий, которые здесь показал — PHP, Flex, ILOG Elixir, AMFPHP и FlexLib — очень мощная. ILOG Elixir, в частности, имеет изумительный набор визуализаций, которые будут дополнять практически любые структурированные данные и делать их представление просто великолепным. Можно без преувеличения сказать, что заставить Elixir выглядеть плохо, это очень сложно. Если вы не хотите платить за Elixir, обратите внимание на встроенные во Flex средства построения графиков или поищите в Google любой open source'ный проекты, который подойдет вам.
Tags:
Hubs:
Total votes 10: ↑6 and ↓4+2
Comments9

Articles