モバイル・ブラウザーにチャート機能を追加する

PHP、XML、jQuery、jQuery Mobile、そして jQuery のチャート機能を使用して、洗練された対話型モバイル・アンケート・アプリケーションを作成する

PHP、XML、jQuery、jQuery Mobile、そして jQuery のチャート機能を使用して、指で操作しやすくユーザー・フレンドリーな、モバイル環境用のオンライン・アプリケーションを作成しましょう。この記事ではチャート機能を使用してそれぞれのアンケートの結果を表示する、アンケート・アプリケーションのバックエンドとフロントエンドを作成します。

Jack D. Herrington, Senior Software Engineer, Fortify Software, Inc.

Photo of Jack HerringtonJack Herrington はサンフランシスコ湾岸地域に住む技術者、著作者、講演者です。彼の最新の活動や記事を http://jackherrington.com で知ることができます。



2011年 10月 21日

モバイル・プラットフォームでチャートを表示する

モバイル技術は大きな関心を呼んでおり、その理由は容易に理解することができます。ほんの数年の間に、最近の携帯電話の強力さはデスクトップ・コンピューターに匹敵するようになりました。私がポケットに入れている携帯電話は、なんとデュアル・コアです。実際、携帯電話の「電話」としての側面は、今やほとんど二次的なものになっています。携帯電話で最も重要な機能は Web の閲覧や Web アプリケーションであり、jQuery Mobile などのツールを使用することで、皆さんが作成したアプリケーションを簡単にモバイル向けのアプリケーションにすることができます。

よく使われる頭文字語

  • Ajax: Asynchronous JavaScript + XML
  • DOM: Document Object Model
  • HTTP: HyperText Markup Language
  • SQL: Structured Query Language
  • XML: Extensible Markup Language

この記事では、バックエンドで PHP と XML を組み合わせ、フロントエンドで jQuery、jQuery Mobile、jqPlot を組み合わせることで、対話機能に優れたアンケート・アプリケーションを作成します。このサンプル・アプリケーションでは、サイト管理者が新しいアンケートの質問とそれに対する回答を作成することができます。フロントエンドでは、ユーザーはアンケートを一覧から選択し、最も気に入った回答に投票し、結果のグラフを見ることができます。皆さんは今日のモバイル機器の強力さに驚くはずです。また、既製のツールを使用することで、皆さんも容易にモバイル機器用のアプリケーションを作成することができます。

まずはアプリケーションのバックエンドを作成し、次にフロントエンドを作成します。


アンケートを作成する

バックエンドを作成するために、まず MySQL データベースのデータ・モデルを定義します。リスト 1 は、このアプリケーションの基になる 2 つのテーブルを作成するコードを示しています。

リスト 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 は、さまざまなアンケートを保持するテーブルで、各アンケートには 1 つの質問が含まれています。2 番目のテーブル answers は、各アンケートの質問に対する回答と、その回答の現在の票数のカウント値を保持するテーブルで、質問と回答との関係を管理するために poll フィールドを使用しています。

このスキーマは、実際には本番のアンケート・アプリケーションのスキーマに使えるようなものではありません。本番でも使えるようなスキーマ・モデルとするためには、ユーザーの投票を追跡するための投票テーブルを含めることで、1 度しか投票できないようにする必要があります。この記事では、アンケートの正確さは関心の中心ではないので、こうしたスキーマの変更は本格的にアンケート・アプリケーションを作成したい読者の方にお任せします。

データベースを作成するためには、以下のようにして、まず 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>

スクリプトの先頭にある PHP コードにより、フォームが投稿された場合には、質問とその回答がデータベースに追加されます。この PHP コードは最初にデータベースに接続し、接続に成功したら、質問が投稿されているかどうかをチェックします。投稿されている場合には、まず新しいレコードを polls テーブルに挿入し、そのレコードの一意の ID を取得します。その後で、スクリプトの一番先頭で定義されている add_answer 関数を使用して各回答を追加します。この add_answer 関数は単純に新しいレコードを answers テーブルに追加し、新しいレコードには回答と、その回答が関係するアンケートの一意の ID が含まれています。

この PHP ファイルの最後にはフォームの HTML コードがあります。ユーザーが「Add Question (質問を追加)」ボタンをクリックすると、form タグによってフォーム内の値がスクリプトに送信されます。

図 1 は、これがブラウザーでどのように表示されるかを示しています。

図 1. アンケートの質問を追加する
Web フォームのスクリーン・キャプチャーが表示されており、フォームには 1 つの「Question (質問)」フィールドと 5 つの「Answer (回答)」フィールド、そして「Add Question (質問を追加)」ボタンがあります。

図 1 は 6 つの入力フィールドがある非常に単純な Web フォームを示しています。最初に「Question (質問)」フィールドがあり、その後に 5 つの「Answer (回答)」入力があります。一番下にはデータベースに質問を追加するための「Add Question (質問を追加)」ボタンがあります。

Add Question (質問を追加)」ボタンをクリックすると、プログラムはデータベースにレコードを追加し、別の質問を追加できるように値を空白にしたフォームを再び表示します。どんなアンケートがあるかを実際に表示するためには、新しいスクリプトが必要です。リスト 3 の polls.php スクリプトは、現在公開されているアンケートが含まれる 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();
?>

このスクリプトでは、まずデータベースに接続し、polls テーブルに対して単純な SELECT クエリーを実行します。続いて、新しい XML DOM 文書を作成してルートに polls タグを追加し、それぞれのアンケートに対する各 poll タグを polls タグに追加します。各 poll タグには、そのアンケートの一意の ID と、そのアンケートの質問のテキストが含まれています。

このページをテストするには、単純にブラウザーでこのページにナビゲートし、「View Source (ソースを表示)」を選択して表示される内容を確認します。もう 1 つの方法としては、以下のように curl を使用し、返される XML を取得します。

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

フロントエンドでは Ajax リクエストを使用して、このスクリプトにアクセスします。

以下に記載する 3 つのスクリプトはアンケートの回答部分を処理します。処理を容易にするために、まず build_answers というヘルパー関数を作成します。この関数は、データベース・ハンドルとアンケートの ID を引数として受け取り、そのアンケートに対するすべての回答を含む 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 出力を作成します。この XML 出力にはルートとして answers タグがあり、アンケートに対する各回答の answer タグがあります。各 answer には、その回答の一意の ID、回答のテキスト、その回答に対する投票数が含まれています。

この XML をページに組み込むためには、リスト 5 の answers.php スクリプトが必要です。

リスト 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']  );
?>

この単純なスクリプトによってデータベースに接続し、リクエストから取得した ID を 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 ページとの違いは、このスクリプトでは指定された回答のカウントをデータベースから取得し、インクリメントしてから、そのカウントでデータベースの answers テーブルのレコードを更新する点です。また、このスクリプトは回答をデータベースから取得すると、その回答が関係するアンケートの一意の ID を $poll 変数に格納します。そしてそのアンケートの ID が build_answers に渡されることで、現在の一連の回答が出力されます。

vote スクリプトで現在の回答の状態をエクスポートすることは重要です。それは、アンケートに対する投票の実行と、現時点でのアンケートの結果の取得が、すべて 1 つのステップで行えるようになるからです。vote スクリプトが現在の回答の状態をエクスポートしない場合には、最初に投票を行ってから answers テーブルに対して現在の投票数を取得するリクエストを実行する必要があります。このため、vote スクリプトに現在の回答のカウントの一覧を取得する処理を行わせることで、すべてを 1 つのステップで行った方が明らかに最適です。

バックエンド・スクリプトが完成したので、今度はフロントエンドのスクリプトを作成します。


フロントエンドを作成する

モバイル・インターフェースは従来の Web ページとは大きく異なります。選択肢の数は少なく、また親指で操作しやすいように、選択肢やボタンは大きく作られています。ここで、長い時間をかけて、こうしたインターフェースを自分で作成することもできますが、jQuery Mobile があるのにそんなことをする必要はありません。jQuery Mobile は、非常によく使われている jQuery JavaScript ライブラリーをベースに構築されたユーザー・インターフェース・ツールキットです。

インターフェースは 1 つの Web ページ内にある 3 つのページに組み込まれています。最初のページには一連の質問があります。ユーザーが 1 つの質問を選択すると、ページが左にスライドして 2 番目のページが表われ、選択された質問に対する一連の回答が表示されます。次にユーザーが 1 つの回答を選択すると、そのページが左にスライドして 3 番目のページが表われます。この 3 番目のページには、選択されたアンケートの結果を示すチャートが表示されます。このチャートを作成するために、同じく jQuery 上に構築された非常に優れた jqPlot ライブラリーを使用します。

リスト 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>

ここには大量のコードがありますが、3 つの異なるページがすべて 1 つのページに集約されていると考えると、少しコードを追いやすくなります。このコードはまず、body タグの先頭で定義されるホーム・ページから始まります。ホーム・ページの ul リスト・タグには、文書が用意されると実行される Ajax リクエストによって内容が追加されます。この Ajax リクエストは polls.php スクリプトを呼び出し、その polls.php スクリプトによって XML が返されます。その XML は構文解析されて li タグになり、それらの li タグが ul タグに追加されます。

ユーザーがホーム・ページにあるアンケートの一覧から 1 つの項目をクリックすると、回答を並べたページが表示されます。続いて openPoll 関数が呼び出され、ホーム・ページの場合と同じ手法を使用して回答ページの li タグに内容を追加します。

1 つの回答が選択されると、vote メソッドの中で vote.php スクリプトが呼び出されます。Ajax メソッドがリターンすると plotData 関数が呼び出され、jqPlot ライブラリーとアンケートの結果を使用して「ドーナツ・チャート」が作成されます。

これが実際にどのように表示されるかを見てください。図 2 はホーム・ページを示しています。

図 2. ホーム画面
1 つの質問を表示したホーム画面のスクリーン・キャプチャー

図 2 はモバイル対応のページを示しており、用意された各アンケートに、回答を一覧表示するためのボタンが 1 つあります。しかしこの場合にはアンケートが 1 つしかないため、ボタンは 1 つしかありません。このボタンをクリックすると、図 3 のような回答ページが表示されます。

図 3. 回答ページ
アンケートに対する 4 つの回答を表示したホーム画面のスクリーン・キャプチャー

図 3 は、このアンケートに対する一連の回答を示しています。またこのページには、ユーザーがホーム・ページに戻るための「Back (戻る)」ボタンもあります。ここまでのすべてを jQuery Mobile が処理してくれるため、皆さんはアプリケーションに集中することができます。

1 つの回答をクリックして投票を行うと、図 4 に示すような結果のグラフが表示されます。

図 4. 最初の結果
1 つの投票に基づく結果を表示したドーナツ型グラフのスクリーン・キャプチャー

図 4 は、ドーナツに極めてよく似たグラフになっています。1 票しか投票がないため、データ系列の 1 つの要素のみが表示されています。さらに何票か追加で投票するには、「Back (戻る)」ボタンをクリックし、何回か投票を行います。すると図 5 の結果が表示されます。

図 5. 何票か投票を追加した後の結果
複数の投票に基づく結果を表示したドーナツ型グラフのスクリーン・キャプチャー

こちらの方が、はるかに見栄えが良いと思いませんか。それぞれの回答が占める割合は、すべての投票に対する相対的な比率となっています。

まとめ

将来、デスクトップ機器は完全にモバイル機器に置き換わるのでしょうか?もしそうでないとしても、現在すでにモバイル機器は確実に大きな部分を占めています。iPhone、iPad、そして Android フォンや Android タブレットが飛ぶように売れています。人々は、指で操作しやすいと同時にユーザー・フレンドリーなオンライン・アプリケーションにアクセスできる、便利な機器を求めています。開発の観点から見ると、これらの機器に応じて複数のバージョンのユーザー・インターフェースを開発しなければならない点が問題です。しかし、jQuery や jQuery Mobile などのライブラリーを使用することで、モバイル環境で動作するユーザー・インターフェースを非常に容易に作成できるようになります。

参考文献

学ぶために

  • jQuery ライブラリー: この素晴らしい Web サイトを訪れてください。jQuery の機能と利用可能な拡張機能のすべてがドキュメント化されています。
  • jqPlot: ドーナツ・チャートについて調べてみてください。jqPlot には驚くほど多様な視覚化オプションがあり、ドーナツ・チャートはその単なる 1 つにすぎません。
  • jQuery Mobile: 美しく機能的なモバイル Web アプリケーションをいかに容易に作成できるかを学んでください。
  • PHP のサイト: PHP に関する最も優れた参照先サイトを調べてみてください。
  • W3C のサイト: さまざまな標準のための素晴らしいサイトにアクセスしてください。特にこの記事に関連しているのは XML 標準です。
  • developerWorks に公開された他の jQuery Mobile 関連コンテンツ: jQuery Mobile 技術について学んでください。
  • 著者の Jack Herrington が developerWorks に寄稿した他の記事 (2005年3月から現在まで): Ajax、JSON、PHP、XML、その他の技術に関する記事を読んでください。
  • New to XML には、XML を学ぶために必要なリソースが豊富に用意されています。
  • developerWorks の XML ゾーン: DTD、スキーマ、XSLT など、XML の領域でのスキルを磨くためのリソースが豊富に用意されています。XML の技術文書一覧には、広範な話題を網羅した技術記事やヒント、チュートリアル、技術標準、IBM Redbooks が豊富に用意されています。
  • IBM XML certification: XML および関連技術において IBM 認定技術者になる方法を参照してください。
  • developerWorks の Technical events and webcasts: これらのセッションで最新情報を入手してください。
  • developerWorks on Twitter: 今すぐ Twitter に参加して developerWorks のツイートをフォローしてください。
  • developerWorks podcasts: ソフトウェア開発者のための興味深いインタビューや議論を聞いてください。
  • developerWorks on-demand demos: 初心者のための製品インストール方法やセットアップのデモから、上級開発者のための高度な機能に至るまで、多様な話題が解説されています。

製品や技術を入手するために

議論するために

コメント

developerWorks: サイン・イン

必須フィールドは(*)で示されます。


IBM ID が必要ですか?
IBM IDをお忘れですか?


パスワードをお忘れですか?
パスワードの変更

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む

 


お客様が developerWorks に初めてサインインすると、お客様のプロフィールが作成されます。会社名を非表示とする選択を行わない限り、プロフィール内の情報(名前、国/地域や会社名)は公開され、投稿するコンテンツと一緒に表示されますが、いつでもこれらの情報を更新できます。

送信されたすべての情報は安全です。

ディスプレイ・ネームを選択してください



developerWorks に初めてサインインするとプロフィールが作成されますので、その際にディスプレイ・ネームを選択する必要があります。ディスプレイ・ネームは、お客様が developerWorks に投稿するコンテンツと一緒に表示されます。

ディスプレイ・ネームは、3文字から31文字の範囲で指定し、かつ developerWorks コミュニティーでユニークである必要があります。また、プライバシー上の理由でお客様の電子メール・アドレスは使用しないでください。

必須フィールドは(*)で示されます。

3文字から31文字の範囲で指定し

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む

 


送信されたすべての情報は安全です。


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=60
Zone=XML, Open source, Web development
ArticleID=765713
ArticleTitle=モバイル・ブラウザーにチャート機能を追加する
publish-date=10212011