Построение диаграмм в мобильных браузерах

Использование PHP, XML, jQuery, jQuery mobile и jQuery charting для создания простого интерактивного мобильного приложения для проведения опросов

Разрабатывайте простые и удобные интерактивные мобильные приложения, используя технологии PHP, XML, jQuery, jQuery mobile и jQuery charting. В данной статье рассматривается процесс создания интерфейсной и серверной частей приложения для проведения опросов, использующего функциональность построения диаграмм для отображения результатов опросов

03 октября 2011 г. – в разделе Часто используемые сокращения было исправлено определение HTTP с "Hypertext Markup Language" на "Hypertext Transfer Protocol".

Джек Д Херрингтон, главный инженер-программист, Leverage Software Inc.

Джек Д. Херрингтон (Jack D. Herrington) - главный инженер-программист с более чем двадцатилетним опытом работы. Он автор трех книг: "Генерирование кода в действии", "Podcasting Hacks" и "PHP Hacks". Написал более 30 статей. Вы можете связаться с Джеком по адресу jherr@pobox.com.



14.05.2012

Построение диаграмм на мобильных платформах

Мобильные технологии вызывают огромный интерес, что легко объяснимо. По мощности современные мобильные телефоны конкурируют с настольными компьютерами, выпущенными всего несколько лет назад. У меня в кармане лежит двухъядерный телефон! Фактически "телефонный" аспект мобильных телефонов в настоящее время почти вторичен. На передний план выходят Web-серфинг и приложения, а при помощи таких инструментальных средств, как jQuery Mobile, можно легко сделать приложение удобным для пользователей.

Часто используемые сокращения

  • Ajax: Asynchronous JavaScript + XML (асинхронный JavaScript и XML)
  • DOM: Document Object Model (объектная модель документа)
  • HTTP: Hypertext Transfer Protocol (протокол передачи гипертекста)
  • SQL: Structured Query Language (язык структурированных запросов).
  • XML: Extensible Markup Language (расширяемый язык разметки)

В данной статье я с помощью технологий PHP и XML на стороне сервера и jQuery, jQuery Mobile и jqPlot на стороне клиента создаю интерактивного приложения для проведения опросов. В готовом примере приложения администратор сайта сможет создавать новые вопросы и соответствующие ответы. На клиентской стороне пользователи смогут просматривать список опросов, выбирать подходящий ответ и просматривать результаты в графическом виде. Вас удивят мощность современных мобильных устройств и простота написания приложений для них с использованием имеющихся инструментов.

Сначала напишем серверную часть, а затем перейдем к клиентской.


Создание опросов

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

Листинг 1. db.sql
DROP TABLE IF EXISTS polls;
CREATE TABLE polls(
  id INT NOT NULL AUTO_INCREMENT,
  question TEXT NOT NULL,
  primary key ( id ) );

DROP TABLE IF EXISTS answers;
CREATE TABLE answers(
  id INT NOT NULL AUTO_INCREMENT,
  poll INT NOT NULL,
  answer TEXT NOT NULL,
  count INT,
  primary key ( id ) );

Первая таблица, polls, хранит различные опросы, каждый из которых содержит вопрос. Вторая таблица, answers, хранит ответы на каждый вопрос и имеет поля poll для связи с первой таблицей и count для хранения текущего числа голосов для данного ответа.

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

Для создания базы данных прежде всего запустите программу mysqladmin и с помощью команды mysql выполните сценарий db.sql, как показано ниже:

% mysqladmin --user=root --password=foo create polls
% mysql --user=root --password=foo polls < db.sql

После создания базы данных можно приступить к разработке PHP-страницы, на которой можно добавлять новые вопросы. В листинге 2 приведен код страницы create.php.

Листинг 2. create.php
<?php
function add_answer( $db, $qid, $answer ) {
  $sql = 'INSERT INTO answers VALUES ( 0, ?, ?, 0 )';
  $sth = $db->prepare($sql);
  $sth->execute( array( $qid, $answer ) );
}

$dd = new PDO('mysql:host=localhost;dbname=polls', 'root', '');
if ( isset( $_POST['question'] ) ) {
  $sql = 'INSERT INTO polls VALUES ( 0, ? )';
  $sth = $dd->prepare($sql);
  $sth->execute( array( $_POST['question'] ) );
  $qid = $dd->lastInsertId();
  if ( isset( $_POST['a1'] ) && strlen( $_POST['a1'] ) > 0 )
    add_answer( $dd, $qid, $_POST['a1'] );
  if ( isset( $_POST['a2'] ) && strlen( $_POST['a2'] ) > 0 )
    add_answer( $dd, $qid, $_POST['a2'] );
  if ( isset( $_POST['a3'] ) && strlen( $_POST['a3'] ) > 0 )
    add_answer( $dd, $qid, $_POST['a3'] );
  if ( isset( $_POST['a4'] ) && strlen( $_POST['a4'] ) > 0 )
    add_answer( $dd, $qid, $_POST['a4'] );
  if ( isset( $_POST['a5'] ) && strlen( $_POST['a5'] ) > 0 )
    add_answer( $dd, $qid, $_POST['a5'] );
}
?>
<html>
<body>
<form method="post">
<table>
<tr><th>Question</td><th><input type="text" name="question" /></td></tr>
<tr><th>Answer 1</th><td><input type="text" name="a1" /></td></tr>
<tr><th>Answer 2</th><td><input type="text" name="a2" /></td></tr>
<tr><th>Answer 3</th><td><input type="text" name="a3" /></td></tr>
<tr><th>Answer 4</th><td><input type="text" name="a4" /></td></tr>
<tr><th>Answer 5</th><td><input type="text" name="a5" /></td></tr>
</table>
<input type="submit" value="Add Question" />
</form>
</body>
</html>

В начале сценария расположен код для добавления в базу данных вопроса и ответов на него при отправке формы. Прежде всего осуществляется подключение к базе данных. Затем проверяется отправка вопроса. Если вопрос отправлен, в таблицу polls вставляется новая запись и возвращается ее уникальный идентификатор. После этого при помощи функции add_answer, определенной в самом начале сценария, добавляются ответы. Эта функция просто добавляет в таблицу answer новую запись с ответом и уникальный идентификатор соответствующего вопроса.

В конце PHP-файла находится код формы. Тег form отправляет значения, введенные в поля формы, обратно в сценарий при нажатии пользователем кнопки submit.

На рисунке 1 показана форма в браузере.

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

На рисунке 1 показана очень простая форма с шестью полями для ввода данных. Первое поле служит для ввода вопроса, а последующие пять – для ввода ответов. Внизу находится кнопка Add Question для добавления вопроса в базу данных.

После нажатия кнопки Add Question программа добавляет запись в базу данных и возвращает форму с пустыми полями, чтобы можно было добавить другой вопрос. Для просмотра списка доступных опросов необходим новый сценарий. Сценарий polls.php, приведенный в листинге 3, возвращает XML-блок с перечнем доступных опросов.

Листинг 3. polls.php
<?php
header( 'Content-Type:text/xml' );

$dbh = new PDO('mysql:host=localhost;dbname=polls', 'root', '');

$sql = 'SELECT * FROM polls';

$q = $dbh->prepare( $sql );
$q->execute( array() );

$doc = new DOMDocument();
$r = $doc->createElement( "polls" );
$doc->appendChild( $r );

foreach ( $q->fetchAll() as $row) {
  $e = $doc->createElement( "poll" );
  $e->setAttribute( 'id', $row['id'] );
  $e->setAttribute( 'question', $row['question'] );
  $r->appendChild( $e );
}

print $doc->saveXML();
?>

Сначала в сценарии выполняется подключение к базе данных, а затем простой запрос SELECT к таблице polls. После этого сценарий создает новый XML DOM-документ, добавляет корневой тег polls и отдельные теги poll для каждого опроса. Каждый тег poll имеет уникальный идентификатор и текст вопроса.

Чтобы протестировать страницу, просто перейдите на нее в браузере и выберите View Source для просмотра возвращаемых данных. Еще одним вариантом проверки является использование программы curl для получения XML-результата:

$ curl "http://localhost/poll/polls.php"
<?xml version="1.0"?>
<polls><poll id="1" question="Is jQuery great?"/></polls>
$

Клиентская часть обращается к этому сценарию, используя Ajax-запрос.

Три следующих сценария работают с ответами опросов. Для облегчения работы сначала создается вспомогательная функция build_answers, которая при передаче в нее дескриптора базы данных и идентификатора опроса выдает XML-код для всех ответов опроса (см. листинг 4).

Листинг 4. build_answers.php
<?php
function build_answers( $dbh, $poll ) {
  $sql = 'SELECT * FROM answers WHERE poll=?';

  $q = $dbh->prepare( $sql );
  $q->execute( array( $poll) );

  $doc = new DOMDocument();
  $r = $doc->createElement( "answers" );
  $doc->appendChild( $r );

  foreach ( $q->fetchAll() as $row) {
    $e = $doc->createElement( "answer" );
    $e->setAttribute( 'id', $row['id'] );
    $e->setAttribute( 'answer', $row['answer'] );
    $e->setAttribute( 'count', $row['count'] );
    $r->appendChild( $e );
  }

  print $doc->saveXML();
}
?>

Функция build_answers сначала через подключение к базе данных выполняет SQL-запрос и получает все ответы для указанного опроса. После этого с помощью XML DOM-кода создается новый XML-код с корневым тегом answers и тегами answer для каждого ответа данного опроса. Каждый ответ имеет уникальный идентификатор, текст ответа и количество пользователей, выбравших этот ответ.

Для подключения этих действий к странице необходим сценарий answers.php, приведенный в листинге 5.

Листинг 5. answers.php
<?php
require_once( 'build_answers.php' );

header( 'Content-Type:text/xml' );

$dbh = new PDO('mysql:host=localhost;dbname=polls', 'root', '');
build_answers( $dbh, $_REQUEST['id']  );
?>

Этот простой сценарий подключается к базе данных и отправляет идентификатор из поля quest в функцию build_answers.

Ниже приведен пример запроса для этого сценария:

$ curl "http://localhost/poll/answers.php?id=1"
<?xml version="1.0"?>
<answers>
       <answer id="1" answer="Yep, awesome!" count="7"/>
       <answer id="2" answer="It's pretty good." count="2"/>
       <answer id="3" answer="It's ok." count="1"/>
       <answer id="4" answer="Nah, it's not so hot." count="1"/>
</answers>
$

Последним шагом в разработке сервиса является создание Ajax-страницы, позволяющей выбирать ответ. В листинге 6 приведен этот сценарий.

Листинг 6. vote.php
<?php
require_once( 'build_answers.php' );

header('Content-Type: text/xml');

$poll = 0;

$dd = new PDO('mysql:host=localhost;dbname=polls', 'root', '');
if ( isset( $_REQUEST['id'] ) ) {
  $sth = $dd->prepare("SELECT count, poll FROM answers WHERE id=?");
  $sth->execute( array( $_REQUEST['id'] ) );
  $count = 0;
  foreach ( $sth->fetchAll() as $row) {
    $count = $row['count'];
	$poll = $row['poll'];
  }
  $count++;
  $sql = 'UPDATE answers SET count=? WHERE id=?';
  $sth = $dd->prepare($sql);
  $sth->execute( array( $count, $_REQUEST['id'] ) );
}

build_answers( $dd, $poll );
?>

Отличие этого сценария от страницы answers.php состоит в том, что сначала подсчитывается число голосов за данный ответ, затем это число увеличивается на единицу и обновляется соответствующая запись в базе данных. Кроме того, при поиске ответа сценарий сохраняет уникальный идентификатор соответствующего вопроса в переменной $poll. Затем этот идентификатор передается в функцию build_answers для вывода текущего набора ответов.

Важность экспорта текущего состояния ответов состоит в том, что он позволяет выбирать ответы и получать текущие результаты опроса за один шаг. Поскольку в альтернативном варианте нужно сначала выполнить голосование, а затем запрос к answers для получения количества голосов, объединение всех этих действий в один шаг безусловно представляет собой лучшее решение.

После завершения серверных сценариев можно приступить к написанию клиентской части.


Создание клиентской части

Мобильные интерфейсы сильно отличаются от традиционных Web-страниц. Для удобства навигации при помощи пальцев количество опций уменьшают, а размер доступных опций и кнопок увеличивают. На самостоятельное создание этого интерфейса можно потратить много времени, но это и не нужно, поскольку существует jQuery Mobile. jQuery Mobile – это инструментальный пакет для создания интерфейса пользователя, построенный на базе чрезвычайно популярной JavaScript-библиотеки jQuery.

Интерфейс состоит из трех "страниц", представляющих собой составные части одной Web-страницы. Первая страница содержит список вопросов. После выбора вопроса пользователем страница сдвигается влево и отображается вторая страница с ответами на выбранный вопрос. После выбора ответа страница сдвигается влево и появляется третья страница. На ней отображается диаграмма результатов по данному опросу. Для создания диаграммы используется замечательная библиотека jqPlot, которая тоже основана на jQuery.

В листинге 7 представлен код для всего вышеописанного.

Листинг 7. index.html
<html><head>
<link rel="stylesheet" href="css/jquery.mobile-1.0a4.1.css" />
<link rel="stylesheet" type="text/css" href="css/jquery.jqplot.css" />
<script src="js/jquery-1.6.1.min.js"></script>
<script src="js/jquery.mobile-1.0a4.1.js"></script>
<script language="javascript" type="text/javascript" 
        src="js/jquery.jqplot.js"></script>
<script language="javascript" type="text/javascript" 
        src="js/plugins/jqplot.donutRenderer.js"></script>

<script type="text/javascript">
function plotData( data ) {
  ds = [];
  $(data).find('answer').each( function() {
    ds.push( [ $(this).attr('answer'), parseInt( $(this).attr('count') ) ] );
  } );
  $.jqplot('chart1', [ds], {
    seriesDefaults:{
       renderer:$.jqplot.DonutRenderer
    },
    legend: {show:true}
  });
}
function vote( poll, answer ) {
  $.ajax( { url: 'vote.php',
    data:{id:answer},
    success:function( data ) {
     plotData( data );
    }
  });
}
function openPoll( poll ) {
  $.ajax( { url: 'answers.php',
    data:{id:poll},
    success:function( data ) {
      $(data).find('answer').each( function() {
        var name = $(this).attr('answer');
        var id = $(this).attr('id');
        $('#answer-list').append(
          '<li><a href="#results" poll="'+poll+'" answer="'+id+'" 
                  class="answer">'+name+'</a></li>'
        );
      } );
      $('.answer').click(function(e) {
      vote( $(this).attr('poll'),
            $(this).attr('answer') );
      })
      $('ul').listview('refresh');
    }
  });
}
$(document).ready(function(){
   $.jqplot.config.enablePlugins = true;
   $.ajax( { url: 'polls.php',
     success:function( data ) {
       $(data).find('poll').each( function() {
         var name = $(this).attr('question');
         var id = $(this).attr('id');
         $('#poll-list').append(
           '<li><a href="#answers" pollid="'+id+'" class="poll">'+name+'</a></li>'
         );
       });
       $('.poll').click(function(e) {
         openPoll( $(this).attr('pollid') );
       })
       $('ul').listview('refresh');
     }
   });
});
</script>

</head><body>
  <div data-role="page" id="home"> 
    <div  data-role="header"><h1>Poll Central</h1></div> 
    <div  data-role="content">
  <ul data-role="listview" data-inset="true" data-theme="c" data-dividertheme="b" 
       id="poll-list">
  </ul>
      <div id="chart0" class="plot" style="width:400px;height:280px;"></div>
      </div>
  </div> 

  <div data-role="page" id="answers">
    <div data-role="header">
      <h1 id="poll-question">Question</h1>
    </div>
    <div data-role="content">  
    <ul data-role="listview" data-inset="true" data-theme="c" data-dividertheme="b" 
        id="answer-list">
    </ul>
    </div>
  </div>

  <div data-role="page" id="results">
    <div data-role="header">
      <h1>Results</h1>
    </div>
    <div data-role="content"> 
    <div data-role="collapsible">
    <h3>Results Graph</h3>
    <p>
      <div class="jqPlot" id="chart1" style="height:320px; width:480px;"></div>
    </p>
    </div>
    
    <a href="#home" data-role="button">Start Again</a>
    </div>
  </div>
</body></html>

Это довольно большой код, но если представить его в виде трех разных страниц, расположенных на одной странице, разобраться в нем будет легче. Он начинается с домашней страницы, определяемой в начале тега body. Тег списка ul на домашней странице заполняется посредством Ajax-запроса, выполняющегося после формирования документа. Ajax-запрос вызывает сценарий polls.php, возвращающий XML-код, который затем анализируется и преобразуется в теги li, добавляемые в тег ul.

После выбора пользователем элемента списка опросов на домашней странице отображается страница ответов. Затем вызывается функция openPoll, которая заполняет тег li на странице ответов, используя аналогичную методику.

После выбора ответа активизируется метод vote сценария vote.php. После возврата Ajax-метода вызывается функция plotData, которая использует библиотеку jqPlot для создания кольцевой диаграммы с результатами.

На рисунке 2 показана домашняя страница рабочего приложения.

Рисунок 2. Домашняя страница
Рисунок 2. Домашняя страница

На рисунке 2 показана удобная для отображения в мобильных устройствах страница со списком кнопок для каждого вопроса. В нашем случае имеется только один вопрос и, следовательно, только одна кнопка. После нажатия кнопки отобразится страница с ответами, показанная на рисунке 3.

Рисунок 3. Страница ответов
Рисунок 3. Страница ответов

На рисунке 3 показан список ответов на данный вопрос. Также на странице есть кнопка Back для возврата на предыдущую страницу. jQuery Mobile берет все заботы на себя, поэтому вы можете сконцентрироваться на приложении.

Выберите ответ и просмотрите диаграмму результатов, показанную на рисунке 4.

Рисунок 4. Первые результаты
Рисунок 4. Первые результаты

На рисунке 4 показана диаграмма, похожая на бублик. Для одного ответа в ряду данных будет только один элемент. Чтобы добавить несколько ответов, нажмите кнопку Back и проголосуйте несколько раз. Результаты представлены на рисунке 5.

Рисунок 5. После добавления нескольких ответов
Screen capture of donut-shaped graph of results based on multiple votes

Так значительно лучше! Каждый ответ занимает область, пропорциональную количеству выбравших его пользователей.

Заключение

Являются ли мобильные устройства технологиями будущего? Если даже нет, без них определенно не обойдется. iPhone, iPad, Android-телефоны и планшеты активно продаются. Людям нужно удобное устройство, предоставляющее доступ к простым и дружественным интерактивным приложениям. С точки зрения разработчика проблема заключается в необходимости разработки нескольких версий пользовательского интерфейса для этих устройств. Библиотеки, подобные jQuery и jQuery Mobile, значительно облегчают создание пользовательских интерфейсов для мобильных устройств.

Ресурсы

Комментарии

developerWorks: Войти

Обязательные поля отмечены звездочкой (*).


Нужен IBM ID?
Забыли Ваш IBM ID?


Забыли Ваш пароль?
Изменить пароль

Нажимая Отправить, Вы принимаете Условия использования developerWorks.

 


Профиль создается, когда вы первый раз заходите в developerWorks. Информация в вашем профиле (имя, страна / регион, название компании) отображается для всех пользователей и будет сопровождать любой опубликованный вами контент пока вы специально не укажите скрыть название вашей компании. Вы можете обновить ваш IBM аккаунт в любое время.

Вся введенная информация защищена.

Выберите имя, которое будет отображаться на экране



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

Отображаемое имя должно иметь длину от 3 символов до 31 символа. Ваше Имя в системе должно быть уникальным. В качестве имени по соображениям приватности нельзя использовать контактный e-mail.

Обязательные поля отмечены звездочкой (*).

(Отображаемое имя должно иметь длину от 3 символов до 31 символа.)

Нажимая Отправить, Вы принимаете Условия использования developerWorks.

 


Вся введенная информация защищена.


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=40
Zone=Мобильные приложения, Open source
ArticleID=815564
ArticleTitle=Построение диаграмм в мобильных браузерах
publish-date=05142012