2011年8月13日土曜日

AndroidTestCaseを継承したテストケースでテストプロジェクト内のリソースを利用

今日は朝一から外出して若松通商さんで愛用のT61のCPUFan交換後、第一回Androidテスト・ソースコードリーディングに参加してきました。

ちょっと人数が少なめで寂しかったですがデ部とのダブルブッキングやら時期的な事を考えると致し方ないことだったでしょうか。
少人数でスイーツを頂きながらまったりとActivityInstrumentaionTestCase2あたりを眺めてきました。

その中で先日からハマっていたAndroidテストパッケージの中のassets以下のファイルにアクセス出来ない問題についてmike_neckさんに例を提示して頂いたのでAssetManagerの呼び出しに@hideされているgetTestContextメソッドを利用してアクセス出来るようになりました。

public class AndroidExtendedTestCase extends AndroidTestCase {

    public Context getExTestContext() {
        Context context = null;
        @SuppressWarnings("unchecked")
        Class clz = (Class) this.getClass();
        try {
            Method method = clz.getMethod("getTestContext");
            context = (Context) method.invoke(this);
        } catch (SecurityException e) {
            fail();
        } catch (NoSuchMethodException e) {
            fail();
        } catch (IllegalArgumentException e) {
            fail();
        } catch (IllegalAccessException e) {
            fail();
        } catch (InvocationTargetException e) {
            fail();
        }
        return context;
    }
}

まんまソースを頂いてループしてしまっているようだったのでメソッド名をちょっと変更しましたがこのAndroidExtendedTestCaseを継承したテストケースクラスで

AssetManager assetMgr = getExTestContext().getAssets();
// filenameはassetsファイル以下相対パスを指定
InputStream is = new InputStream(assetMgr.open(filename));

という感じでばっちりと読み込むことが出来ました。@mike_neckさんいつもありがとうございます。

2011年8月8日月曜日

読まねばならぬもの

だいぶ前に知人の方からMacBookをお借りしてiOS開発にも挑戦しようとぼちぼちと準備中。

第一の目的は添付文書検索ツールのiOSへの移植…メインはiPadをターゲットにしたいと思います。
理由としては今後調剤室で添付文書の閲覧が必要になったときには常に最新のものを見てもらいたいということとなかなか業務中にはパソコンの前に座って最新の添付文書を開くというのが難しいということ。

これをクリアするためにiPadなりAndroidタブレットに最適化した添付文書検索ツールのリリースをと
大分前から考えていたりしますがなかなか形にすることが出来ずにいる訳です。

iPad向け開発もAndroidタブレット向け開発も未経験ではありますがなんとか頑張らないといけません。

とりあえずまったくわかってないiOS開発の勉強をしている訳ですがAppleから膨大なドキュメントが日本語で提供されています。ところがこのページがどこにあるのかDeveloperサイトからなかなか辿ることが出来ないので備忘録的に記事にしておきます。

iOS Developer Library

上記リンク先に日本語化されたドキュメントが沢山あります。
全部じっくりと読めばかなりの知識になりそう。

iOS Developer Library(英語)

上記にはさらに沢山のドキュメントが英語ではありますが用意されています。

とりあえずじっくりと腰を据えてドキュメントを読み込みたいと思っています。

Groovy環境を作ってみる。

さてさて半年以上も放置してしまいまいたがぼちぼち書かないといけませんね。
いろんなことに手をだしていてネタは沢山あるので時間を見つけて書くこととしましょう。

昨日はAndroidテスト部の祭りに参加してきた訳ですが(それも記事にしたらいいのにw)その途中で重いから辞めときゃいいのに本を3冊程買ってしまいました。

そのうちの一冊がこちら



テスト部でご一緒させて頂いているmike_neckさんに触発されておもわず購入です。

ということでWin7とOSX上に環境構築してみました。

公式サイトよりWindows用インストーラをDLして起動
インストールパスをC:\JavaLib\Groovy\Groovy-1.8.1に変更したのみ
環境変数まで設定してくれるので楽チン。

一旦ログアウトしてGroovyConsoleを起動してHello, Groovy!を実行。


Groovyのインストール@OSX
http://groovy.codehaus.org/DownloadよりバイナリzipをDLして
Matsumoto.JSさんのMac OS XでのGroovy等をインストールする場所の記事を参考にさせて頂き/Library/Frameworks/Groovy.frameworks/Versions/1.8.1に展開して/Library/Frameworks/Groovy.frameworks/Versions/Currentにシンボリックリンクを作成

.bash_profileを以下にように変更
export JAVA_HOME=/Library/Java/Home
export ANT_HOME=/usr/share/java/ant-1.8.2
export GROOVY_HOME=/Library/Frameworks/Groovy.framework/Versions/Current
export PATH=$PATH:/Developer/android-sdk-mac_x86/platform-tools
export PATH=$PATH:$GROOVY_HOME/bin
そして確認。
MacBook:~ kaepapa$ source .bash_profile
MacBook:~ kaepapa$ groovy -version
Groovy Version: 1.8.1 JVM: 1.6.0_26

同じくgroovyConsoleを起動してHello, Groovy!を実行


ということで環境構築は完了しました。
徐々にいろいろといじってみたいと思っております。

2010年12月30日木曜日

CodeIgniterでGoogleCalendarAPIのテスト(2)

さてちょっと時間が空いてしまいましたがGoogleCalendarAPIをCI上から利用してカレンダー機能にアクセスはほぼ実装が終わりました。そのあとjsなどをいじっていたので更新が遅くなってしまった訳ですが各機能についてご紹介したいと思います。

前回IBMのサンプルをそのまま用いてカレンダーの予定からタイトルやサマリを取得した訳ですがサマリだとちょっと見づらいので取得する情報をbasicからfullへと変更し各情報の詳細を取得出来るように変更しました。

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('full');
  $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;

   $when = $event->when;
   $obj->when = date('Y/n/j',strtotime($when[0]->getStartTime()));
   $obj->startTime = date('G:i',strtotime($when[0]->getStartTime()));
   $obj->endTime = date('G:i',strtotime($when[0]->getEndTime()));

   $reminders = $when[0]->reminders;
   if (count($reminders)!=0) {
    foreach ($reminders as $reminder) {
     if ($reminder->getMethod()=='email') {
      $obj->reminder_email = $reminder->getMinutes();
     } else {
      $obj->reminder_alert = $reminder->getMinutes();
     }
    }
   }
   $obj->content = $event->content;
   $obj->edit_key = $event->id;

   $where = $event->where;
   $obj->where = $where[0]->getValueString();

   $events[] = $obj;
  }

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

 return $data;
}

ソースをご覧頂くとEventQuery生成部分でbasicがfullに変更されているのがおわかり頂けるかと思います。またforeach文の中で各種詳細データを取得しているのが前回との変更点です。

詳細データの取得に関して注意が必要なところは「when」「reminders」そして「where」等の情報はイベントデータ内に配列として保持されている点です。基本的にwhenやwhereは1つだけ保持していると思われるので$when[0]等で決め打ちでアクセスしてしまっています。またリマインダについてはGoogleカレンダー上では複数登録することが出来ますが今回はメールとアラートもしくはメール+アラートという3つの選択肢から選択し設定時間は1つだけ保持できる仕様としております。

続いては削除メソッドです。

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

  $edit_key = $this->input->post('edit_key', TRUE);
  if (isset($edit_key)) {
   $event = $gcal->getCalendarEventEntry('http://www.google.com/calendar/feeds/default/private/full/'.$edit_key);
   $event->delete();
  }
 }catch(Exception $e){
  echo "エラー:".$e->getMessage();
 }
}

イベントエントリに含まれているidを使って個別EventEntryを取得してdelete()メソッドを実行するだけで削除が可能です。Contactsの時はeditキーを送っていましたがZFの実装がこのようになっています。これはなんなく実装できました。

次は登録メソッドを見てみたいと思います。

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

  $dates = explode('/', $this->input->post('when', TRUE));
  $stimes = explode(':', $this->input->post('start_time', TRUE));
  $etimes = explode(':', $this->input->post('end_time', TRUE));
  date_default_timezone_set('Asia/Tokyo');
  $stime = date(DATE_RFC3339, mktime($stimes[0],$stimes[1],0,$dates[1],$dates[2],$dates[0]));
  $etime = date(DATE_RFC3339, mktime($etimes[0],$etimes[1],0,$dates[1],$dates[2],$dates[0]));

  $event = $gcal->newEventEntry();
  $event->title = $gcal->newTitle($this->input->post('title', TRUE));
  $when = $gcal->newWhen();
  $when->startTime = $stime;
  $when->endTime = $etime;
  if (isset($_POST['reminder_cbox'])) {
   $time = $this->input->post('reminder_time', TRUE);
   $method = $this->input->post('reminder', TRUE);
   $reminders = array();
   switch ($method) {
    case 'm':
     $reminder = $gcal->newReminder();
     $reminder->method = "email";
     $reminder->minutes = $time;
     $reminders[] = $reminder;
     break;
    case 'p':
     $reminder = $gcal->newReminder();
     $reminder->method = "alert";
     $reminder->minutes = $time;
     $reminders[] = $reminder;
     break;
    case 'mp':
     $reminder = $gcal->newReminder();
     $reminder->method = "email";
     $reminder->minutes = $time;
     $reminders[] = $reminder;
     $reminder = $gcal->newReminder();
     $reminder->method = "alert";
     $reminder->minutes = $time;
     $reminders[] = $reminder;
     break;
   }
   $when->reminders = $reminders;
  }
  $event->when = array($when);
  $event->where = array($gcal->newWhere($this->input->post('where', TRUE)));
  $event->content = $gcal->newContent($this->input->post('content', TRUE));

  $newEvent = $gcal->insertEvent($event);
 }catch(Exception $e){
  echo "エラー:".$e->getMessage();
 }
}

この部分はわりと苦労しました。日時をRFC3339形式にして送らないといけませんがこれはphpのdate()メソッドが対応してくれていますので$_POSTから取得した日時データをexplode()メソッドでバラして生成しています。冒頭で書いたようにリマインダについては「メールのみ」「ポップアップのみ」そして「メールとポップアップ併用」という3種類を選択可能とし時間は1つだけ指定できる仕様のためこのような実装になっています。「when」「reminders」そして「where」についてはしっかりと配列で渡してあげることをお忘れなく。これを忘れるとエラーとなります。

また各種データについてZF側でインスタンス生成メソッドが用意されているのでそのメソッドを利用してデータを登録しています。xmlを生成していたContactsに比べてとても楽チンになっているので助かりました。いや…逆にメソッドを調べるのが大変だったかも?(笑

最後にデータ修正です。

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

  $dates = explode('/', $this->input->post('when', TRUE));
  $stimes = explode(':', $this->input->post('start_time', TRUE));
  $etimes = explode(':', $this->input->post('end_time', TRUE));
  date_default_timezone_set('Asia/Tokyo');
  $stime = date(DATE_RFC3339, mktime($stimes[0],$stimes[1],0,$dates[1],$dates[2],$dates[0]));
  $etime = date(DATE_RFC3339, mktime($etimes[0],$etimes[1],0,$dates[1],$dates[2],$dates[0]));

  $event = $gcal->getCalendarEventEntry($this->input->post('edit_key', TRUE));
  $event->title = $gcal->newTitle($this->input->post('title', TRUE));
  $when = $gcal->newWhen();
  $when->startTime = $stime;
  $when->endTime = $etime;
  if (isset($_POST['reminder_cbox'])) {
   $time = $this->input->post('reminder_time', TRUE);
   $method = $this->input->post('reminder', TRUE);
   $reminders = array();
   switch ($method) {
    case 'm':
     $reminder = $gcal->newReminder();
     $reminder->method = "email";
     $reminder->minutes = $time;
     $reminders[] = $reminder;
     break;
    case 'p':
     $reminder = $gcal->newReminder();
     $reminder->method = "alert";
     $reminder->minutes = $time;
     $reminders[] = $reminder;
     break;
    case 'mp':
     $reminder = $gcal->newReminder();
     $reminder->method = "email";
     $reminder->minutes = $time;
     $reminders[] = $reminder;
     $reminder = $gcal->newReminder();
     $reminder->method = "alert";
     $reminder->minutes = $time;
     $reminders[] = $reminder;
     break;
   }
   $when->reminders = $reminders;
  }
  $event->when = array($when);
  $event->where = array($gcal->newWhere($this->input->post('where', TRUE)));
  $event->content = $gcal->newContent($this->input->post('content', TRUE));

  $event->save();
 }catch(Exception $e){
  echo "エラー:".$e->getMessage();
 }
}

内容については登録時とほぼ同じですがeditキーを使ってEventEntryを取得しその内容を書き換えてsave()メソッドで登録しております。ここで注意が必要なのが削除時や個別イベントデータ取得時に利用するidとデータ修正を行うeditキーは別のデータということです。

IBMのサンプルはbasicでデータを取得しているためこの違いには言及していませんでしたが以下のxmlデータをご覧ください。

<atom:id>http://www.google.com/calendar/feeds/default/private/full/57a84t1b243radd31209erdd9k≷/atom:id>
<atom:link href="http://www.google.com/calendar/event?eid=NTdhODR0MWIyNDNyYWRkMzEyMDllcmRkOWtfMjAxMDEyMjdUMDIzMDAwWiB0ZXN0LmthZXBhcGFAbQ" rel="alternate" type="text/html" title="alternate"/>
<atom:link href="http://www.google.com/calendar/feeds/default/private/full/57a84t1b243radd31209erdd9k" rel="self" type="application/atom+xml"/>
<atom:link href="http://www.google.com/calendar/feeds/default/private/full/57a84t1b243radd31209erdd9k/63429101549" rel="edit" type="application/atom+xml"/>

最後のeditだけ数字のデータが付加されているのがおわかり頂けるかと思います。このデータを渡してあげないとデータ修正は上手くいかずエントリの先頭のデータが書き換えられてしまったりしましたのでご注意ください。

さて実際のブラウザでの表示も掲載しておきたいと思います。


こんな具合にイベントのタイトルと時間、説明と場所が表示されリマインダについてはアイコン表示にしてあります。下の削除と編集リンクからそれぞれの機能が利用できて一番下の「新しいイベントを追加」で新規イベント登録が出来ます。最下段の検索機能についてはまだ実装しておりません。この後実装予定です。


上記はイベントの登録と修正に利用するフォームです。日時と時間の入力はjqueryを利用して入力補助をしています。またリマインダについてはチェックを入れるとセレクトフォームが表示されデータ登録が可能となります。いや…jsで遊んでみたかったんです。jqueryを使うととっても楽にいろいろと出来るようになりますねぇ。もっと勉強しないと!

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の追加、削除そして修正を実装したりカレンダーの追加や修正等もチャレンジする予定です。

2010年12月26日日曜日

CodeIgniterでGoogleContactsAPIのテスト(8)

さてGoogleContactAPIを叩いての連絡先データをいじいじするテストですが今回で最終回です。連絡先の新規登録、修正及び削除、グループの新規登録、修正及び削除が出来るようになっています。未実装なのは連絡先のデータにグループデータを含める部分ということになりますね。今回の記事では連絡先データの中でのグループデータについて一覧取得、登録そして修正時のxmlデータ扱い方についてご紹介します。

これまでのデータの扱い方が理解できていれば特に問題ない範疇だと思います。

まずは一覧取得時のデータ取得方法です。

$group_data = $this->gdata_groups_model->getGroupsElement();
foreach ($xml->groupMembershipInfo as $g) {
 foreach ($group_data as $key => $val) {
  if ($key == $g['href']) {
   $obj->groups[] = $val;
  }
 }
}

feedデータにはhrefとしてグループのidしか含まれていないのでgroupデータを扱うモデルからElementデータを配列で取得してグループ名を代入していますがやり方としてはおわかり頂けるかと思います。

続いては登録時のxml要素追加方法です。

// add group elements
$groups = $this->input->post('groups', TRUE);
foreach ($groups as $g) {
 $group = $doc->createElement('gContact:groupMembershipInfo');
 $group->setAttribute('deleted','false');
 $group->setAttribute('href',$g);

 $entry->appendChild($group);
}

グループデータはwebサイトデータと同様にgContactに定義されているのでそれで指定してあげます。deleted要素は必要ないような気もしますがデベロッパーガイドに従い加えてあります。

最後に修正時のxml要素追加方法ですが登録時とちょっとだけ違います。

// add group elements
$groups = $this->input->post('groups', TRUE);
foreach ($groups as $g) {
 $group = $doc->createElement('groupMembershipInfo');
 $group->setAttribute('xmlns','http://schemas.google.com/contact/2008');
 $group->setAttribute('deleted','false');
 $group->setAttribute('href',$g);

 $entry->appendChild($group);
}

違いはxmlns要素が追加になっているだけですのでお約束みたいなものでしょうか。
この要素がないとエラーが帰ってしまいますのでご注意ください。

さてCodeIgniterでGoogleContactsAPIを叩きながらwebアプリから連絡先データを操作することが出来るようになりました。まだ連絡先データには様々なデータが扱えるようになっていますがリファレンスガイド等を見ればおそらく操作できるのではないかと思います。

ZendFrameworkを利用したので本当に簡単にデータ操作が出来ました。フレームワークの中でフレームワークを使うような愚はどうかとも思いますがまあ軽量フレームワークのCodeIgniterの利点を活かしつつこのテストを活用したwebアプリをこれから製作する予定です。

しかし連絡先だけでなくカレンダーのデータ操作も実や予定していまして次のシリーズは「CodeIgniterでGoogleCalenderAPIをテスト」シリーズが始まります。(笑

CodeIgniterでGoogleContactsAPIのテスト(7)

さていろいろとハマりまくっているCodeIgniterですがバリデーションにも一癖二癖ある感じです。既に使われている方々にはあたりまえの事かもしれませんがこれから始めて触ってみようという方のために記事にしておきたいと思います。

まずはバリデーションを通らなかったときの対応について。

一般的にはform画面に戻って入力結果をinput等に戻して表示という流れになるかと思います。今回複数のメールアドレスや電話番号登録が可能なように実装しているのでformのnameを配列としてCIに渡しております。

このように配列を利用するとバリデーションに渡した結果を受けるときのset_valueメソッドが上手く動作していないようで値を得ることが出来ませんでした。

仕方がないのでMy_Form_validation.phpを作成してForm_vaidationクラスを継承した独自バリデーションクラスを作成してちょこっとset_validationをいじりました。

function set_value($field = '', $default = '') {
 if ( ! isset($this->_field_data[$field]))
 {
  return $default;
 }

 if ( ! isset($this->_field_data[$field]['postdata'])) {
  return $default;
 } else {
  return $this->_field_data[$field]['postdata'];
 }
}

バリデーション後にviewを呼びだす前に$_POSTに必要データを突っ込んでおいて

set_value('edit', $this->input->post('edit'));

のような感じでデフォルト値として$_POSTからのデータを入れておいてあげると上手く行きました。

また複数formのバリデーションを行なう場合にユーザガイドでは http://codeigniter.jp/user_guide_ja/libraries/form_validation.html#savingtoconfig の「検証ルールのセットを作る」に従ってセット名を付けて呼びだすようになっています。

例えば…

if ($this->form_validation->run('contacts') == TRUE) {
 $this->gdata_model->post();
 $data = $this->gdata_model->getContactsList();
 $this->load->view('contacts_index', $data);
} else {
 $this->load->view('form_contacts_view', $this->gdata_model->getFormData());
}

こんな感じで呼びだすことになる訳です。

このときに利用するルールをApplication/config/form_validation.phpに配列として記載する訳ですがユーザドキュメントでは2010/12/25現在「=>」とすべきところが「=」となってしまっています。
こんな間違いに30分もハマるのは私くらいのものかと思いますが念のため書いておきます。(笑

軽快なCodeIgniterなだけあって痒いところに手が届くという機能は実装されてない感じもありますが個人的にはわりと気に入ったのでもうちょっと深く勉強してみたいと思っています。