PHP、jQuery Mobile、Google ToDo リストを活用してモバイル・フレンドリーな ToDo リスト・アプリを作成する

2014年 1月 16日
PDF (828 KB)
 

Create a mobile-friendly to-do list app with PHP, jQuery Mobile, and Google Tasks

05:42  |  Transcript
author photo - Vikram Vaswani

Vikram Vaswani

Founder and CEO of Melonfire

vikram-vaswani.in

私は以前、Windows デスクトップ・アプリケーションを使用して ToDo リストを管理していました。何か重要なことを思い出すたびにそのアプリケーションをさっと開いて、その重要なことに簡潔な名前 (タイトル) を付け、期日を設定して保存していました。そのアプリケーションは、システムにログインすると必ず起動されるため、いつ、何をしなければならないかが即座にわかりました。

この Windows アプリケーションは役に立ったものの、2 つの制約がありました。それは、コンピューターから離れているときには、ToDo リストをチェックしたり、タスクを追加したりできないことです。

そのときに知ったのが、Google ToDo リストです。Google ToDo リストによって、外出先でもタスクをチェックしたり、新しいタスクを追加したりできるようになりました」。

Google ToDo リストは Gmail に統合されていて (このことを知らない人は大勢います)、ユーザーがオンラインで ToDo リストを作成、管理できるようになっています。このように、ブラウザーからタスクのリストにアクセスできると、移動中でも簡単にタスクを追加、表示したり、完了のマークを付けたりできます。

そのときに知ったのが、Google ToDo リストです。Google ToDo リストによって、外出先でもタスクをチェックしたり、新しいタスクを追加したりできるようになりました

ToDo リスト・アプリを作成するために必要なもの

 
  • Apache/PHP 開発環境
  • テスト用の Google アカウント
  • Slim

    この PHP マイクロフレームワークが、PHP コードに構造を追加します。

  • PHP 用の Google API クライアント・ライブラリー

    このライブラリーは、堅牢な OAuth 実装と、あらゆる Google API へのアクセスを単純化するラッパー・オブジェクトを提供します。

  • jQuery Mobile

    このフレームワークは、プラットフォームとブラウザーの互換性問題を最小限に抑え、アプリケーションのユーザー・インターフェースを素早く実装できるようにします。

読む:OAuth 認証

知っておくべきこと

 

他の多くの Google 製品と同じく、Google ToDo リストは ToDo リスト API を公開しています。サード・パーティーのアプリケーションはこの API を使用して、Google ToDo リストに接続し、そのデータを中心としたカスタム・アプリケーションを作成することができます。ToDo リスト API は REST モデルに従っているため、REST 対応のあらゆる開発ツールキットからアクセスすることができます。さらに、私のお気に入りの PHP をはじめ、よく使われている多くのプログラミング言語に対応したクライアント・ライブラリーも用意されています。

ここに記載する PHP サンプル・コードを理解するには、PHP のクラスとオブジェクトについての基礎知識と、REST の扱いに慣れていることが必要です。また、HTML、CSS、jQuery についても十分に理解していなければなりません。

それでは早速、ToDo リスト・アプリの作成に取り掛かりましょう。

依存ライブラリーと依存コンポーネントの構成

 

最初に、Slim をセットアップします。

Slim に馴染みのない方のために説明しておくと、Slim は Web アプリケーションと API の迅速な開発を目的とした PHP マイクロフレームワークです。Slim という名前にだまされないでください。Slim には高度な URL ルーターが組み込まれており、ページ・テンプレート、フラッシュ・メッセージ、暗号化クッキー、ミドルウェアなどのサポートも用意されています。しかも、Slim はごく簡単に理解して使用できる上に、ドキュメントも充実しており、熱心な開発者コミュニティーもあります。

ステップ 1: Slim と Google OAuth ライブラリーを使用してアプリケーションのディレクトリー構造を作成する

 

カレント・ディレクトリーを Web サーバーのドキュメント・ルート・ディレクトリー (通常、Linux では /usr/local/apache/htdocs、Windows では C:\Program Files\Apache\htdocs) に変更し、作成するアプリケーション用の新しいサブディレクトリーを作成します。このディレクトリーには、「tasks」という名前を付けてください。

shell> cd /usr/local/apache/htdocs
shell> mkdir tasks

本記事では、このディレクトリーを $APP_ROOT として参照します。

前のセクションで説明した Slim フレームワークと Google OAuth ライブラリーをすでにダウンロードしてあることを前提として、これらのファイルを $APP_ROOT/vendor に解凍します。また、Slim ダウンロード・アーカイブに含まれている index.php ファイルと .htaccess ファイルを $APP_ROOT ディレクトリーに移動します。index.php ファイルについては、Slim.php ファイルの正しいパスを反映するように編集します。

以上の作業を終えた時点で、ディレクトリー構造は以下のようになっているはずです。

プロジェクトのディレクトリー構造

ステップ 2: 仮想ホストを定義する

 

アプリケーションに簡単にアクセスできるようにするには、新しい仮想ホストを定義し、その仮想ホストを作業ディレクトリーに設定するのが賢明な策です。それには、Apache 構成ファイル (httpd.conf または httpd-vhosts.conf) を編集して以下の行を追加します。

NameVirtualHost 127.0.0.1
<VirtualHost 127.0.0.1>
    DocumentRoot "/usr/local/apache/htdocs/tasks"
    ServerName tasks.melonfire.com
</VirtualHost>

これらの行は、$APP_ROOT に対応するドキュメント・ルートを持つ新しい仮想ホストを定義するものです。上記のコード・リストでは、仮想ホストの名前が「tasks.melonfire.com」となっていますが、この名前を「localhost」または自分が制御できる別のドメインに必ず変更してください。

Web サーバーを再起動して、新しい設定を有効にします。注意する点として、この新規ホストを認識させるために、ネットワークのローカル DNS サーバーを更新しなければならない場合もあります。

以上の作業が完了したら、ブラウザーで、新しく定義したホストにアクセスします。すると、Slim フレームワークのウェルカム・ページが表示されるはずです。

Slim のデフォルト・ウェルカム・ページ

ステップ 3. 作成するアプリケーションを Google Apps プラットフォームに登録する

 

Google ToDo リスト API を使用するには、その前に、作成する Web アプリケーションを Google に登録する必要があります。それには、Google アカウントの資格情報を使用して Google にログインし、Google Cloud Console にアクセスします。新規プロジェクトを作成して名前を設定した後、Google ToDo リスト API へのアクセスをオンに設定します。Google Cloud Console には、作成したプロジェクトが以下のように表示されているはずです。

Google Cloud Console に表示された API アクセス

クリックして大きなイメージを見る

次に、Web アプリケーションを登録して、OAuth 2.0 クライアント ID とシークレットを入手します。これらの値を書き留めておいてください。Google PHP OAuth クライアントに必要となります。

Google Cloud Console に表示された OAuth 資格情報

この時点でアプリケーションのリダイレクト URL を設定することも忘れないでください。OAuth 認証プロセスが完了すると、Google は、ここに設定された URL にクライアント・ブラウザーをリダイレクトします。この例では、URL が http://tasks.melonfire.com/login に設定されています。

Google Cloud Console でのリダイレクト URL の設定

ログインしている間に、Gmail にもアクセスします。Gmail に統合された Google ToDo リスト・インターフェースを使って、いくつかのサンプル・タスク・リストとタスクを追加してください。この作業は、PHP アプリケーションが正常に動作していることを確認するのに役立ちます。Gmail の Google ToDo リスト・インターフェースは、以下のように表示されます。

Gmail に表示された Google ToDo リスト・インターフェース

以上の作業はかなり面倒に思えるかもしれませんが、ありがたいことに、この作業を行う必要があるのは一度だけです。

ToDo リスト API の概要

 

Google ToDo リスト API は、リソースに対するアクションの REST リクエストを受け入れ、リクエストされた情報を含めたレスポンスを返すという方法で動作します。ここで「リソース」とは、アクションを実行する対象のオブジェクトまたはエンティティーを参照する URL に過ぎません (例えば、/lists/users など)。また、「アクション」とは、4 つの HTTP 動詞の 1 つです。つまり GET (取得)、POST (作成)、PUT (更新)、DELETE (削除) のいずれかとなります。

Google ToDo リスト API には、タスクとタスク・リストという 2 つの主要なリソースがあります。ユーザーは複数のタスク・リストを作成することができ、各タスク・リストには複数のタスクを含めることができます。タスクが存在する場所は、タスク・リスト内に限られます。Google ToDo リスト API は、ユーザーが作成した最初のタスク・リストを、そのユーザーの「デフォルト」タスク・リストと見なします。

Google ToDo リスト API はレスポンスを JSON フォーマットにエンコードします。以下に API レスポンスの一例を示します。これは、https://www.googleapis.com/tasks/v1/lists/@default/tasks (ユーザーのデフォルト・タスク・リストを取得するための API エンドポイント) に対して認証済み GET リクエストを発行した場合の例です。

リスト 1. Google ToDo リスト API レスポンスの例
{
 "kind": "tasks#tasks",
 "etag": "\"zhaMOBt\"",
 "items": [
  {
   "kind": "tasks#task",
   "id": "MTc3Mz",
   "etag": "\"zhaMOBt\"",
   "title": "Milk",
   "updated": "2013-11-11T07:46:09.000Z",
   "selfLink": "https://www.googleapis.com/tasks/v1/lists/MTc3Mz/tasks/MTc3MzQ1",
   "position": "00000000000637427684",
   "status": "needsAction"
  },
  {
   "kind": "tasks#task",
   "id": "MTc3Mz",
   "etag": "\"zhaMOBt\"",
   "title": "Bread",
   "updated": "2013-11-11T07:46:11.000Z",
   "selfLink": "https://www.googleapis.com/tasks/v1/lists/MTc3Mz/tasks/MTc3MzQ6",
   "position": "00000000000717532232",
   "status": "needsAction"
  },
  {
       ...
  }
 ]
}

リスト 1 に示されているように、ToDo リスト API は、JSON にエンコードしたレスポンスを生成します。レスポンスにはタスクのリストが含まれており、それぞれのタスク・エントリーに、タスクのタイトル、期日、タスク自体の URL、ステータスなどの有用なメタデータが含まれています。この状態であれば、極めて簡単に JSON をデコードして、Web ブラウザーでの表示に適した HTML 表現に変換することができます。その一方、通常は、ToDo リスト API に対して GET リクエストや POST リクエストをそのまま送信することはありません。Google PHP OAuth クライアントとそのサービス・オブジェクトがこれらのリクエストを包含する便利なラッパーを提供するためです。ラッパーによって、すべての関連する機能が PHP のオブジェクトおよびメソッドにカプセル化されます。

タスクの一覧表示

 

リスト 2 では、タスク・リストとタスクの要約に対する接続、認証、および表示を処理するために、Slim フレームワークと Google OAuth ライブラリーを使用しています。

リスト 2. OAuth 認証フローおよびタスク・リストの取得
<?php
session_start();
require_once 'vendor/Slim/Slim.php';
require_once 'vendor/google-api-php-client/src/Google_Client.php';
require_once 'vendor/google-api-php-client/src/contrib/Google_TasksService.php';

\Slim\Slim::registerAutoloader();
$app = new \Slim\Slim();
$app->config(array(
  'debug' => true,
  'templates.path' => './templates'
));
$client = new Google_Client();
$client->setApplicationName('Project X');
$client->setClientId('YOUR-CLIENT-ID');
$client->setClientSecret('YOUR-CLIENT-SECRET');
$client->setRedirectUri('http://tasks.melonfire.com/login');
$client->setScopes(array(
  'https://www.googleapis.com/auth/tasks'
));
$app->client = $client;
$app->tasksService = new Google_TasksService($app->client);


$app->get('/login', function () use ($app) {
  
    if (isset($_GET['code'])) {
      $app->client->authenticate();
      $_SESSION['access_token'] = $app->client->getAccessToken();
      $app->redirect('/index');
      exit;
    }  

    // if token available in session, set token in client
    if (isset($_SESSION['access_token'])) {
      $app->client->setAccessToken($_SESSION['access_token']);
    }

    if ($app->client->getAccessToken()) {
      if (isset($_SESSION['target'])) {
        $app->redirect($_SESSION['target']);
      } else {
        $app->redirect('/index');
      }
    } else {
      $authUrl = $app->client->createAuthUrl();
      $app->redirect($authUrl);
    }
  
});

$app->get('/index', 'authenticate', function () use ($app) {
  $lists = $app->tasksService->tasklists->listTasklists();
  foreach ($lists['items'] as $list) {
    $id = $list['id'];
    $tasks[$id] = $app->tasksService->tasks->listTasks($id);
  }
  $app->render('index.php', array('lists' => $lists, 'tasks' => $tasks));
});


$app->get('/logout', function () use ($app) {
  unset($_SESSION['access_token']);    
  $app->client->revokeToken();
});

$app->run();

function authenticate () {
  $app = \Slim\Slim::getInstance();
  $_SESSION['target'] = $app->request()->getPathInfo();
  if (isset($_SESSION['access_token'])) {
    $app->client->setAccessToken($_SESSION['access_token']);
  }
  if (!$app->client->getAccessToken()) {
    $app->redirect('/login');
  }
}

リスト 2 ($APP_ROOT/index.php として保存されているはずです) は、Google ToDo リスト・サービス・オブジェクトと併せて Slim と Google OAuth クライアント・ライブラリーをロードするところから始まっています。これに続き、新規 Slim アプリケーション・オブジェクトと新規 Google_Client オブジェクトを初期化します。言うまでもなく、Google_Client オブジェクトは、前の手順で Google Cloud Console で定義したクライアント ID、クライアント・シークレット、およびリダイレクト URL を使用して構成する必要があります。さらに、Google_TasksService サービス・オブジェクトも初期化します。PHP で Google ToDo リスト API とやりとりする際には、このサービス・オブジェクトが主要な制御ポイントとしての役割を果たします。

Slim は、HTTP メソッドとエンドポイントに対し、ルーター・コールバックを定義することで動作します。そのために必要なことは、これに対応するメソッド (例えば、GET リクエストの場合は get()、POST リクエストの場合は post()、等々) を呼び出し、そのメソッドの 1 番目の引数として、突き合わせの対象となる URL ルートを渡すことのみです。メソッドの 2 番目の引数は関数です。この関数は、受信されるリクエストと URL ルートが一致したときに実行するアクションを指定します。リスト 2 でそのようなルーター・コールバックとしてセットアップしているのは、/index/login/logout の 3 つです。それでは、これらのコールバックのそれぞれについて順に検討していきましょう。

  • /login コールバックは、OAuth 認証フローを処理します。このフローの完全な解説は、この記事の目的とするところではありませんが、Google API のドキュメントでその詳細を余すところなく調べられます。簡単に言うと、このコールバックは Google_Client オブジェクトの createAuthUrl() メソッドを使用して Google 認証ページ (次の図を参照) の URL を生成し、その URL にクライアント・ブラウザーをリダイレクトします。ユーザーがアプリケーションを認証して、そのアプリケーションにアクセスを許可するデータを承認すると、Google はクライアントを再び /login URL にリダイレクトします。クライアントはそこでアクセス・トークンを取得してセッションに保管します。このアクセス・トークンを使用することで、クライアントは Google ToDo リスト API にアクセスできるようになります。
  • OAuth 認証が成功すると、クライアントは /index に位置するアプリケーションの index ページにリダイレクトされます。/index コールバックでは、構成済みの Google_TasksService オブジェクトとその listTasklists() メソッドを使用して、認証済みユーザーのタスク・リストに含まれるリストを取得します。上記のコードは、このタスク・リストのコレクションを繰り返し処理し、各リストに対してサービス・オブジェクトの listTasks() メソッドを呼び出して、リストに含まれる個々のタスクを取得します。この情報がビューに転送され、ビューによってユーザーに情報を表示する処理が行われます。このビュー・スクリプトは、後ほど記載します。
  • /logout メソッドは、セッションを破棄することによって、セッションに保管されたアクセス・トークンを無効にします。さらに、セキュリティーを強化するために、クライアント・オブジェクトの revokeToken() メソッドを呼び出して、Google のサーバー上でもトークンを無効にします。
Google API での認可

以上のように、/index コールバックによってユーザーのタスク・リストと各リストのタスクが取得されると、その情報が PHP の変数に格納されてビューに転送され、このビューにより、人間が理解可能なリストにフォーマット設定されます。$APP_ROOT/templates/index.php に配置されているこのビュー・スクリプトは、以下のような内容になっています。

リスト 3. index ページ
<!DOCTYPE html> 
<html> 
<head> 
  <meta name="viewport" content="width=device-width, initial-scale=1"> 
  <link rel="stylesheet" href="http://code.jquery.com/
    mobile/1.3.2/jquery.mobile-1.3.2.min.css" />
  <script src="http://code.jquery.com/jquery-1.9.1.min.js"></script>
  <script src="http://code.jquery.com/mobile/1.3.2/
    jquery.mobile-1.3.2.min.js"></script>
</head> 
<body> 
    <div data-role="page">
      <div data-role="header">
      Tasks
      </div>
      <div data-role="content">
      <div data-role="collapsible-set" data-inset="false">
        <?php foreach ($lists['items'] as $list): ?>
          <?php $id = $list['id']; ?>
          <div data-role="collapsible">
            <h2><?php echo $list['title']; ?></h2>
            <ul data-role="listview">
              <?php if (isset($tasks[$id]['items'])): ?>
                <?php foreach ($tasks[$id]['items'] as $task): ?>
                <li>                
                  <h3><?php echo $task['title']; ?></h3> 
                </li>
                <?php endforeach; ?>
              <?php endif; ?>
            </ul> 
          </div>
        <?php endforeach; ?>
        </div>
      </div>
    </div>        
</body>
</html>

リスト 3 は、jQuery Mobile の標準規則に従ってフォーマット設定された listview ページをセットアップします。主要なページ要素は、data-role="page" 属性が設定された <div> 要素です。この要素の中に、ページのヘッダー、フッター、コンテンツのそれぞれに対応する <div> 要素があります。ページのコンテンツは、ユーザーのタスク・リストのいずれかを表す一連の折り畳み可能な <div> 要素で構成されます。タスク・リストのタイトルをクリックすると、そのリストが展開されて、リストに含まれるタスクが表示されます。

実際の動作を確認するには、ブラウザーで http://tasks.melonfire.com/index (この URL は、自分の仮想ホストの URL に置き換えてください) にアクセスしてください。以下のようにタスクが一覧表示されるはずです。

タスクの一覧表示

タスク・リストの作成と削除

 

当然のことながら、タスクの表示は最初のステップに過ぎません。次は、ユーザーがタスクを表示するだけでなく、新しいタスクとタスク・リストを追加することもできるようにします。それにはまず、$APP_ROOT/index.php に以下の新しいルートを定義します。

<?php

// ... other routes 

$app->get('/add-list', 'authenticate', function () use ($app) {
  $app->render('add-list.php');    
});

上記の定義では、/add-list にリクエストを送信すると、$APP_ROOT/templates/add-list.php テンプレートがユーザーに表示されることになります。このテンプレートの内容は、以下のコード・リストに記載されているとおりです。このルート・コールバックが実行される前に、カスタム関数 authenticate() が実行されます。リスト 2 を見るとわかるように、この関数はアクセス・トークンの有無をチェックし、トークンが見つからない場合には、再ログインを促すためにクライアントをログイン・ページにリダイレクトします。

リスト 4. タスク・リストの作成フォーム
<!DOCTYPE html> 
<html> 
<head> 
  <meta name="viewport" content="width=device-width, initial-scale=1"> 
  <link rel="stylesheet" href="http://code.jquery.com/mobile/1.3.2/
    jquery.mobile-1.3.2.min.css" />
  <script src="http://code.jquery.com/jquery-1.9.1.min.js"></script>
  <script src="http://code.jquery.com/mobile/1.3.2/
    jquery.mobile-1.3.2.min.js"></script>
</head> 
<body> 
    <div data-role="page">
      <div data-role="header">
      Add List
      </div>
      <div data-role="content">
        <div data-role="collapsible-set">
          <form method="post" action="/add-list">
            <label for="title">Title:</label>
            <input name="title" id="title" data-clear-btn="true" type="text"/>
            <input name="submit" value="Save" type="submit" 
              data-icon="check" data-inline="true" data-theme="a" />
            <a href="/index" data-role="button" data-inline="true" 
              data-icon="back" data-theme="a">Back</a>
          </form>
      </div>
    </div>
</body>
</html>

リスト 4 を構成しているのは、新規タスク・リストのタイトルを入力する単一のフィールドを持つフォームです。このフォームを送信すると、POST アクションによってフォームのデータが /add-list ルートに返されます。したがって、フォーム入力に対処するように /add-list ルートを拡張する必要があります。そのために追加するコードは、以下のとおりです。

リスト 5. タスク・リストの作成
<?php

// ... other routes 

$app->post('/add-list', 'authenticate', function () use ($app) {
  if (isset($_POST['submit'])) {
    $title = trim(htmlentities($_POST['title']));
    if (empty($title)) {
      $title = 'Untitled List';
    }
    $tasklist = new Google_TaskList();
    $tasklist->setTitle($title);
    $result = $app->tasksService->tasklists->insert($tasklist);
    $app->redirect('/index');
  } 
});

リスト 5 では、フォームで送信されたタイトルをサニタイズしてから、新規 Google_TaskList オブジェクトを作成しています。このオブジェクトは、Google ToDo リスト API のタスク・リスト・リソースを表します。このオブジェクトの setTitle() メソッドを使用してタイトルを設定した後、サービス・オブジェクトの insert() メソッドを使用して新規タスク・リストを Google ToDo リストに保存します。

以下に、フォームとフォームを送信した結果を示します。

タスク・リストの作成

この時点で Gmail 内の Google ToDo リスト・インターフェースを確認すると、そこには新しく追加されたタスク・リストも表示されているはずです。ご自分で確認してみてください。

ユーザーがリストを追加できるようになっている場合、ユーザーにはリストを削除する手段も提供しなければなりません。サービス・オブジェクトの insert() メソッドの逆バージョンは delete() メソッドです。このメソッドは、タスク・リスト ID を受け取り、それに対応するリストを Google ToDo リストから削除します。このルートの定義は以下のとおりです。

リスト 6. タスク・リストの削除
<?php

// ... other routes 

$app->get('/delete-list/:lid', 'authenticate', function ($lid) use ($app) {
  $app->tasksService->tasklists->delete($lid);
  $app->redirect('/index');
});

リスト 6 では、/delete-list をセットアップしています。この新規ルートは、リクエスト URL の一部としてリスト ID を受け取ります。このリクエスト URL が Slim のルーティング・フレームワークによって構文解析されて、リスト ID が抽出されます。抽出された リスト ID に対応するリストは、delete() メソッドを使用して Google ToDo リストから削除されます。

この他に残された作業は、リストを追加するボタンと削除するためのボタンを表示するように index ページのコードを更新することです。更新後の index ページのコードを以下に示します。

リスト 7. index ページ
<!DOCTYPE html> 
<html> 
<head> 
  <meta name="viewport" content="width=device-width, initial-scale=1"> 
  <link rel="stylesheet" href="http://code.jquery.com/mobile/1.3.2/
    jquery.mobile-1.3.2.min.css" />
  <script src="http://code.jquery.com/jquery-1.9.1.min.js"></script>
  <script src="http://code.jquery.com/mobile/1.3.2/
    jquery.mobile-1.3.2.min.js"></script>
</head> 
<body> 
    <div data-role="page">
      <div data-role="header">
      Tasks
      </div>
      <div data-role="content">
      <div data-role="collapsible-set" data-inset="false">
        <?php foreach ($lists['items'] as $list): ?>
          <?php $id = $list['id']; ?>
          <div data-role="collapsible">
            <h2><?php echo $list['title']; ?></h2>
            <ul data-role="listview">
              <?php if (isset($tasks[$id]['items'])): ?>
                <?php foreach ($tasks[$id]['items'] as $task): ?>
                <li>                
                  <?php if ($task['status'] == 'needsAction'): ?>
                  <h3><?php echo $task['title']; ?></h3> 
                  <?php else: ?>
                  <h3 style="text-decoration:line-through"
                    ><?php echo $task['title']; ?></h3> 
                  <?php endif; ?>
                  <?php if (isset($task['due']) && 
                    ($task['status'] == 'needsAction')): ?>
                  <p>Due on <?php echo date('d M Y', 
                    strtotime($task['due'])); ?></p> 
                  <?php endif; ?>                    
                  <?php if (isset($task['completed']) 
                    && ($task['status'] == 'completed')): ?>
                  <p>Completed on <?php echo 
                    date('d M Y', strtotime($task['completed'])); ?></p> 
                  <?php endif; ?>                    
                </li>
                <?php endforeach; ?>
              <?php endif; ?>
            </ul> 
            <a href="/delete-list/<?php echo $id; ?>" 
              data-inline="true" data-role="button" data-icon="delete" 
              data-theme="a">Remove list</a>
          </div>
        <?php endforeach; ?>
        </div>
      </div>
      <a href="/add-list" data-inline="true" data-role="button" 
        data-icon="plus" data-theme="b">Add new list</a> 
    </div>        
</body>
</html>

リストを追加または削除するための新しいボタンの他にも、このバージョンのビュー・スクリプトには拡張が加えられています。それは、タスクの期日が表示されるようになっていること、そして完了したタスクには取り消し線が引かれ、完了していないタスク (status="needsAction") には取り消し線が引かれないようになっていることです。以下に、一例を示します。

タスクの一覧表示

タスクの作成と削除

 

タスク・リストを追加、削除できるのと同じように、リストにタスクを追加したり、リストからタスクを削除したりすることも可能です。以下に、それぞれの操作を目的とした新規 /add-item ルートと /delete-item ルートを示します。

リスト 8. タスクの追加と削除
<?php

// ... other routes 

$app->get('/add-task/:tid', 'authenticate', function ($tid) use ($app) {
  $app->render('add-task.php', array('id' => $tid));    
});

$app->post('/add-task', 'authenticate', function () use ($app) {
  if (isset($_POST['submit'])) {
    $title = trim(htmlentities($_POST['title']));
    $due = trim(htmlentities($_POST['due']));
    $id = trim(htmlentities($_POST['id']));
    if (empty($title)) {
      $title = 'Untitled Task';
    }
    if (empty($due)) {
      $due = 'tomorrow';
    }
    $task = new Google_Task();
    $task->setTitle($title);
    $task->setDue(date(DATE_RFC3339, strtotime($due)));
    $result = $app->tasksService->tasks->insert($id, $task);
    $app->redirect('/index');
  } 
});

$app->get('/delete-task/:lid/:tid', 'authenticate', function ($lid, $tid) use ($app) {
  $app->tasksService->tasks->delete($lid, $tid);
  $app->redirect('/index');
});

すべてのタスクはタスク・リストに関連付ける必要があります。そのため、タスク・リスト ID を GET リクエスト・パラメーターとして受け取るように /add-task ルート・コールバックをセットアップします。タスク・リスト ID を受け取った /add-task ルート・コールバックは、$APP_ROOT/templates/add-task.php テンプレートを表示します。このテンプレートには、以下のリストに示すように、新規タスクを追加するためのフォームが含まれています。

リスト 9. タスクの作成フォーム
<!DOCTYPE html> 
<html> 
<head> 
  <meta name="viewport" content="width=device-width, initial-scale=1"> 
  <link rel="stylesheet" href="http://code.jquery.com/mobile/1.3.2/
    jquery.mobile-1.3.2.min.css" />
  <link rel="stylesheet" type="text/css" href="http://dev.jtsage.com/cdn/
    datebox/latest/jqm-datebox.min.css" /> 
  <script src="http://code.jquery.com/jquery-1.9.1.min.js"></script>
  <script src="http://code.jquery.com/mobile/1.3.2/
    jquery.mobile-1.3.2.min.js"></script>
  <script src="http://dev.jtsage.com/cdn/datebox/latest/
    jqm-datebox.core.min.js"></script>
  <script src="http://dev.jtsage.com/cdn/datebox/latest/
    jqm-datebox.mode.calbox.min.js"></script>
  <script src="http://dev.jtsage.com/cdn/datebox/i18n
    /jquery.mobile.datebox.i18n.en_US.utf8.js"></script>
</head> 
<body> 
    <div data-role="page">
      <div data-role="header">
      Add Task
      </div>
      <div data-role="content">
        <div data-role="collapsible-set">
          <form method="post" action="/add-task">
            <input name="id" type="hidden" value="<?php echo $id; ?>" />
            <label for="title">Title:</label>
            <input name="title" id="title" data-clear-btn="true" type="text"/>
            <label for="due">Due:</label>
            <input name="due" id="due" type="date" data-role="datebox" 
              data-options='{"mode": "calbox", "useFocus": true, 
              "themeDateToday": "e"}' />
            <input name="submit" value="Save" type="submit" 
              data-icon="check" data-inline="true" data-theme="a" />
            <a href="/index" data-role="button" data-inline="true" 
              data-icon="back" data-theme="a">Back</a>
          </form>
      </div>
    </div>
</body>
</html>

リスト 9 に含まれているフォームには、2 つの可視フィールドがあります。1 つはタスクのタイトルを入力するフィールド、もう 1 つはタスクの期日を入力するフィールドです。日付エントリーを単純にするために、日付の入力フィールドは jQuery Mobile DateBox プラグインを使用するように構成されています。このプラグインが表示するグラフィカルな日付ピッカーでは、ポイント・アンド・クリックで日付を入力できます。タスクはタスク・リストと関連付けなければならないことから、このフォームには、GET パラメーターとして渡されたタスク・リスト ID も隠しフィールドとして指定されています。

フォームが送信されると、そこに入力されたデータがサニタイズされてから、Google_Task オブジェクトを初期化するために使用されます。このオブジェクトは、隠しタスク・リスト ID と一緒にサービス・オブジェクトの insert() メソッドに渡されます。すると、REST 呼び出しによって、オブジェクトが Google ToDo リストのシステムに追加されます。/delete-task ルート・コールバックは、/add-task コールバックと同じ要領で、タスク・リスト ID とタスク ID の両方を受け取るように構成されています。このルート・コールバックは、サービス・オブジェクトの delete() メソッドを使用して、指定されたタスクを指定されたタスク・リストから削除します。

ルートとビジネス・ログインは用意されたので、残る作業は index ページを更新してタスクの追加ボタンと削除ボタンを組み込むことのみです。この後すぐに、タスクのステータスを更新する方法を説明するので、ついでにそのためのボタンも追加しておきます。更新後のテンプレートは以下のとおりです。

リスト 10. index ページ
<!DOCTYPE html> 
<html> 
<head> 
  <meta name="viewport" content="width=device-width, initial-scale=1"> 
  <link rel="stylesheet" href="http://code.jquery.com/mobile/1.3.2/
    jquery.mobile-1.3.2.min.css" />
  <link rel="stylesheet" type="text/css" 
    href="http://dev.jtsage.com/cdn/datebox/latest/jqm-datebox.min.css" /> 
  <script src="http://code.jquery.com/jquery-1.9.1.min.js"></script>
  <script src="http://code.jquery.com/mobile/1.3.2/
    jquery.mobile-1.3.2.min.js"></script>
  <script src="http://dev.jtsage.com/cdn/datebox/latest/
    jqm-datebox.core.min.js"></script>
  <script src="http://dev.jtsage.com/cdn/datebox/latest/
    jqm-datebox.mode.calbox.min.js"></script>
  <script src="http://dev.jtsage.com/cdn/datebox/i18n/
    jquery.mobile.datebox.i18n.en_US.utf8.js"></script>
</head> 
<body> 
    <div data-role="page">
      <div data-role="header">
      Tasks
      </div>
      <div data-role="content">
      <div data-role="collapsible-set" data-inset="false">
        <?php foreach ($lists['items'] as $list): ?>
          <?php $id = $list['id']; ?>
          <div data-role="collapsible">
            <h2><?php echo $list['title']; ?></h2>
            <ul data-role="listview">
              <?php if (isset($tasks[$id]['items'])): ?>
                <?php foreach ($tasks[$id]['items'] as $task): ?>
                <li>                
                  <div class="ui-grid-b">
                    <div class="ui-block-a">
                      <?php if ($task['status'] == 'needsAction'): ?>
                      <h3><?php echo $task['title']; ?></h3> 
                      <?php else: ?>
                      <h3 style="text-decoration:line-through">
                        <?php echo $task['title']; ?></h3> 
                      <?php endif; ?>
                      <?php if (isset($task['due']) && 
                        ($task['status'] == 'needsAction')): ?>
                      <p>Due on <?php echo date('d M Y', 
                        strtotime($task['due'])); ?></p> 
                      <?php endif; ?>                    
                      <?php if (isset($task['completed']) &&
                        ($task['status'] == 'completed')): ?>
                      <p>Completed on <?php echo 
                        date('d M Y', strtotime($task['completed'])); ?></p> 
                      <?php endif; ?>                    
                    </div>                    
                    <div class="ui-block-b"></div>                    
                    <div class="ui-block-c">
                      <?php if ($task['status'] == 'needsAction'): ?>
                      <a href="/update-task/<?php echo $id; ?>/
                        <?php echo $task['id']; ?>" data-inline="true" 
                        data-role="button" data-icon="check" 
                        data-theme="a">Done!</a>
                      <?php endif; ?>
                      <a href="/delete-task/
                        <?php echo $id; ?>/<?php echo $task['id']; ?>" 
                        data-inline="true" data-role="button" data-icon="delete" 
                        data-theme="a">Remove task</a>
                    </div>
                  </div>
                </li>
                <?php endforeach; ?>
              <?php endif; ?>
            </ul> <br/>
            <a href="/add-task/<?php echo $id; ?>" 
              data-inline="true" data-role="button" data-icon="plus" 
              data-theme="a">Add new task</a>
            <a href="/delete-list/<?php echo $id; ?>" 
              data-inline="true" data-role="button" data-icon="delete" 
              data-theme="a">Remove list</a>
          </div>
        <?php endforeach; ?>
        </div>
        <a href="/add-list" data-inline="true" data-role="button" 
          data-icon="plus" data-theme="b">Add new list</a> 
      </div>
    </div>
</body>
</html>

リスト 10 に示されているように、各タスク・リストのマークアップが更新されて、それぞれのリストが 2 列からなるグリッドに変わっています。左側の列にはタスクのタイトルと期日が表示され、右側の列には、そのタスクに適用できる操作 (タスクのステータスの更新、またはタスクの削除など) が表示されます。さらに、リストの一番下にある新しいボタンによって、新しいタスクを追加してリストを更新することができます。

以下に、タスク・リストに新規タスクを追加するプロセスを示します。

タスクの作成

タスクのステータスの更新

 

これで、このアプリケーションはタスクおよびタスク・リストの追加操作、削除操作をサポートするようになりました。最後に必要な機能は、タスクに完了のマークを付けることです。リスト 10 では、この機能のためのボタンをすでに組み込んであり、/update-task ルートにリンクさせています。リスト 11 では、リンク先ルートのビジネス・ロジックを指定して、タスクのステータスの更新プロセスを完成させます。

リスト 11. タスクの更新
<?php

// ... other routes 

$app->get('/update-task/:lid/:tid', 'authenticate', 
  function ($lid, $tid) use ($app) {
    $task = new Google_Task($app->tasksService->tasks->get($lid, $tid));
    $task->setStatus('completed');
    $result = $app->tasksService->tasks->update($lid, $task->getId(), $task);
    $app->redirect('/index');
});

/update-task ルート・コールバックは、リスト ID とタスク ID の両方を受け取り、その情報を使用して Google ToDo リスト API からタスク情報を取得します。このタスク情報を新規 Google_Tasks オブジェクトに取り込んで、オブジェクトの setStatus() メソッドによってタスクのステータスを「completed (完了)」に変更します。最後に、サービス・オブジェクトの update() メソッドを使用して、この新しいタスク・エントリーを Google のサーバーに送信します。

以下に、タスクに完了のマークを付けるプロセスを示します。

タスクのステータスの更新

まとめ

 

これで完了です!この短期集中コースでは、jQuery Mobile、Google PHP OAuth ライブラリー、そして Slim PHP マイクロフレームワークを組み合わせて使用して、Google ToDo リスト API からのデータを PHP アプリケーションに統合する方法を説明しました。この記事には、Google ToDo リストの JSON フォーマットを紹介する例の他に、タスク・リストを取得する方法、タスクを追加、変更、削除する方法、ユーザーの Google アカウントにタスク・リストのカスタム・インターフェースを組み込む方法を説明する例も記載しました。

これらの例から明らかなように、タスクの管理に関連する新しい独創的なアプリケーションを作成したい開発者にとって、Google ToDo リスト API は強力かつ柔軟なツールになります。ぜひ、この API を実際に使って確かめてみてください!


関連トピック:PHPjQuery MobileGoogle Tasks

コメントの追加

注意: HTML コードは、コメント内ではサポートされません。


残り 1000 文字

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=Mobile development, Web development, Open source, Cloud computing
ArticleID=960370
ArticleTitle=PHP、jQuery Mobile、Google ToDo リストを活用してモバイル・フレンドリーな ToDo リスト・アプリを作成する
publish-date=01162014