2010年12月27日月曜日

CodeIgniterでGoogleCalendarAPIのテスト(1)

さてGoogleContactsAPIに続いて今度はCalendarAPIのテストをCodeIgniter上で行なてみたいと思います。

Contactsに比べZendFramework自身が直接対応していますし他の情報もいくつか存在しているので苦労の度合いは少ないように予想してます。今考えたら先にCalendarをテストしておいた方が楽だったのではないかと思わなくもありませんが後の祭りというやつですね。

はてさてGoogleカレンダーは私も利用していますがAjaxがたっぷり使われていてとても使いやすいUIが提供されています。それを今さらならがwebアプリから使えるようにしたところで何になるんだ?という気もしなくもありませんがまあそれはそれとして今回はCodeIgniter上で動作するwebアプリからカレンダーの作成、修正やカレンダーを指定しての予定の追加、修正そして削除を実装してみる予定です。またContactsではClientLoginを使用しましたが実際のwebアプリではwebアプリ製作者である私にもユーザの認証情報の伝わらないAuthSubで実装してみたいと思います。まずはClientLogin認証で機能を実装してから最後にAuthSub認証に変更してみる予定です。

それぞれの詳しい情報については以下のドキュメントをご参照ください。

Zend Framework GDataプログラマーズリファレンスガイド Google Calendar の使用法
Google Calendar APIs and Tools Data API Developer's Guide: Protocol
Google Calendar APIs and Tools API Reference Guide
Authentication and Authorization for Google APIs AuthSub for Web Applications

なおGoogleCalendarAPIのドキュメントには「これは古いバージョンである」旨記載がありますがVer.2のPHPGuideはまだありませんし。ZendFrameworkがまだ対応してないような雰囲気なので古い方のドキュメントを参照してますが詳細はまだわかっていません。
コーディングを進めるうちになにかわかりましたら加筆予定です。

また今回もIBM developerWorksの記事[http://www.ibm.com/developerworks/jp/xml/library/x-googleclndr/index.html]を参考に進めていきたいと思います。

ではdWの記事を熟読した後今回もこのソースをCIに移植していく訳ですがその前にそのままCI上でviewを表示してみたいと思います。

DL可能となっているソースのview.php(idとpassを書き換え)をCIのコントローラからloadするだけです。
CodeIgniterのsystemフォルダやindex.phpファイルを今回もeclipseを利用してコーディングするので作成したPHPプロジェクトフォルダ内にコピーしてconfig.phpやroutes.phpについては適切に設定を行なってください。

まずはコントローラの./system/application/controllers/calendar.phpファイルから見ていきましょう。

<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');

class Calendar extends Controller {

 public function __construct() {

  parent::Controller();

  $this->output->set_header('Content-Type: text/html; charset=UTF-8');
 }

 /*
  * デフォルトメソッド
  */
 public function index() {
  $this->load->view('view');
 }
}

/* End of file calendar.php */
/* Location: ./system/application/controllers/calendar.php */

Controllerクラスを継承したCalendarクラスを作成してコンストラクタで親クラスのコンストラクタを呼び文字化け対策にUTF-8を指定しています。デフォルトメソッドのindex()ではIBMで配布されているview.phpファイルを一部手直ししたものをロードしているだけです。

続いてはview.phpファイルを見てみましょう。

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="ja" lang="ja">
<head>
 <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
 <title>Googleカレンダー予定一覧</title>
 <style type="text/css">
 body {
  font-family: Verdana;
 }
 li {
  border-bottom: solid black 1px;
  margin: 10px;
  padding: 2px;
  width: auto;
  padding-bottom: 20px;
 }
 h2 {
  color: red;
  text-decoration: none;
 }
 span.attr {
  font-weight: bolder;
 }
 </style>
</head>
<body>
 <?php
    require_once 'Zend/Loader.php';
    Zend_Loader::loadClass('Zend_Gdata');
    Zend_Loader::loadClass('Zend_Gdata_ClientLogin');
    Zend_Loader::loadClass('Zend_Gdata_Calendar');
    Zend_Loader::loadClass('Zend_Http_Client');
    
    $gcal = Zend_Gdata_Calendar::AUTH_SERVICE_NAME;
    $user = "username@gmail.com";
    $pass = "password";
    $client = Zend_Gdata_ClientLogin::getHttpClient($user, $pass, $gcal);
    $gcal = new Zend_Gdata_Calendar($client);
    
    $query = $gcal->newEventQuery();
    $query->setUser('default');
    $query->setVisibility('private');
    $query->setProjection('basic');
    $query->setOrderby('starttime');
    if(isset($_GET['q'])) {
      $query->setQuery($_GET['q']);      
    }
    
    try {
      $feed = $gcal->getCalendarEventFeed($query);
    } catch (Zend_Gdata_App_Exception $e) {
      echo "Error: " . $e->getResponse();
    }
    ?>
 <h1><?php echo $title ?></h1>
 <?php echo $totalResults ?> 件の予定があります。
 <p/>
 <ol>
 <?php
  foreach ($feed as $event) {
   echo "<li>\n";
   echo "<h2>" . stripslashes($event->title) . "</h2>\n";
   echo stripslashes($event->summary) . " <br/>\n";
   $id = substr($event->id, strrpos($event->id, '/')+1);
   echo "<a href=\"edit.php?id=$id\">編集</a> | ";
   echo "<a href=\"delete.php?id=$id\">削除</a> <br/>\n";
   echo "</li>\n";
  }
  echo "</ul>";
 ?>
 </ol>
 <p/>
 <a href="add.php">新しいイベントを追加</a><p/>
 <form action="<?php echo $_SERVER['PHP_SELF']; ?>" method="get">
  予定の検索:<br/>
  <input type="text" name="q" size="10"/>
  <input type="submit" name="submit" value="検索"/>
 </form>
</body>
</html>


ヘッダ部分を日本語対応とし表示を日本語化した以外はそのままのコードになっています。

では簡単にphpスクリプト部分を読んでいってみましょう。

最初に必要となるZendFrameworkのクラスをロード後、ClientLogin認証で$gcalインスタンスを生成しZend_Gdata_Calendarクラスを生成しています。さらにdefaultカレンダーのprivate領域のデータをbasicなデータ量で取得するというクエリを作成していますね。$_GET['q']を使っている部分が予定の検索時に利用する条件です。そしてtry~catch文の中でこの作成したクエリを発行してデータを取得しています。得られたfeedをhtml内に埋め込んでいき表示を行なうという実装ですね。ソースコード内の$userと$passについてはご自身のアカウント情報を入力してください。


上記がこのview.phpを表示した状態です。表示されているEventは私が適当に作成したものです。今回もあっさりと表示されて安心しました。

問題なく表示されましたのでCI化していきたいと思います。

まずはモデルの./system/application/models/calendar_model.phpファイルから作成しました。

<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');

class Calendar_model extends Model {

 private $user;
 private $pass;

 public function __construct() {
  parent::Model();

  require_once 'Zend/Loader.php';
  Zend_Loader::loadClass('Zend_Gdata');
  Zend_Loader::loadClass('Zend_Gdata_ClientLogin');
  Zend_Loader::loadClass('Zend_Gdata_Calendar');
  Zend_Loader::loadClass('Zend_Http_Client');

  $this->config->load('calendar_config', TRUE);
  $this->user = $this->config->item('username','calendar_config');
  $this->pass = $this->config->item('password','calendar_config');
 }

 public function getCalendarEntryList() {

  try{
   $gcal = Zend_Gdata_Calendar::AUTH_SERVICE_NAME;
   $client =Zend_Gdata_ClientLogin::getHttpClient($this->user, $this->pass, $gcal);
   $gcal = new Zend_Gdata_Calendar($client);

   $query = $gcal->newEventQuery();
   $query->setUser('default');
   $query->setVisibility('private');
   $query->setProjection('basic');
   $query->setOrderby('starttime');

   $feed=$gcal->getCalendarEventFeed($query);

   $data['title'] = (string)$feed->title;
   $data['totalResults'] = (String)$feed->totalResults;

   $events = array();
   foreach ($feed as $event) {
    $obj = new stdClass;

    $obj->title = $event->title;
    $obj->summary = $event->summary;
    $obj->id = $event->id;

    $events[] = $obj;
   }

   $data['events'] = $events;
  }catch(Zend_Gdata_App_Exception $e){
   echo "エラー:".$e->getResponse();
  }

  return $data;
 }
}

/* End of file calendar_model.php */
/* Location: ./system/application/model/calendar_model.php */

Modelクラスを継承したCalendar_modelクラスを作成してコンストラクタの中でZFの必要なクラスをロード、Contactsの時と同様にユーザ名とパスワードはcalender_config.php内に記述してあり、それをload後取得しています。

getCalendarEntryList()メソッドでGoogleCalendarにアクセスしてfeedを取得し$dataに必要なデータを入れて返すというContactsのときとまったく同じ手法です。

続いては変更を加えたコントローラクラスのcalender.phpを見てみます。

<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');

class Calendar extends Controller {

 public function __construct() {

  parent::Controller();

  $this->load->model('calendar_model');

  $this->output->set_header('Content-Type: text/html; charset=UTF-8');
 }

 /*
  * デフォルトメソッド
  */
 public function index() {
  $data = $this->calendar_model->getCalendarEntryList();
  $this->load->view('calendar_index',$data);
 }
}

/* End of file calendar.php */
/* Location: ./system/application/controllers/calendar.php */

コンストラクタメソッド内でCalendar_modelをロードして先程作成したgetCalendarEntryList()メソッドでデータを取得後view.phpから名称を変更したcalendar_index.phpファイルにデータを渡してロードするよう変更しました。

最後にビューのcalendear_index.phpを見てみましょう。

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="ja" lang="ja">
<head>
 <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
 <title>Googleカレンダー予定一覧</title>
 <style type="text/css">
 body {
  font-family: Verdana;
 }
 li {
  border-bottom: solid black 1px;
  margin: 10px;
  padding: 2px;
  width: auto;
  padding-bottom: 20px;
 }
 h2 {
  color: red;
  text-decoration: none;
 }
 span.attr {
  font-weight: bolder;
 }
 </style>
</head>
<body>
 <h1><?php echo $title ?></h1>
 <?php echo $totalResults ?> 件の予定があります。
 <p/>
 <ol>
 <?php
  foreach ($events as $e) {
   echo "<li>\n";
   echo "<h2>" . stripslashes($e->title) . "</h2>\n";
   echo stripslashes($e->summary) . " <br/>\n";
   $id = substr($e->id, strrpos($e->id, '/')+1);
   echo "<a href=\"edit.php?id=$id\">編集</a> | ";
   echo "<a href=\"delete.php?id=$id\">削除</a> <br/>\n";
   echo "</li>\n";
  }
  echo "</ul>";
 ?>
 </ol>
 <p/>
 <a href="add.php">新しいイベントを追加</a><p/>
 <form action="<?php echo $_SERVER['PHP_SELF']; ?>" method="get">
  予定の検索:<br/>
  <input type="text" name="q" size="10"/>
  <input type="submit" name="submit" value="検索"/>
 </form>
</body>
</html>

実装部分がなくなりかなりすっきりした感がありますね。これぞMVCモデルの利点でしょうか。データ表示部を数カ所実装にある変数名に変更しただけですがこれでview.phpと同じ表示が得られるようになりました。

3ヶ所ほどある他機能へのリンクはまだ実装していないのでそのままになっています。そもそもCIはデフォルトではGETを受け取らないのでこのままでは利用できません。

さてここまででGoogleカレンダーにAPIを使ってアクセスしEventデータを取得する機能が実装できました。実装といってもそのままコピーしたような状態ではありますがまあ良しとしましょう。このあとEventの追加、削除そして修正を実装したりカレンダーの追加や修正等もチャレンジする予定です。

0 件のコメント:

コメントを投稿