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

2010年12月25日土曜日

CodeIgniterでGoogleContactsAPIのテスト(6)

時間がかかると云ってた割にはすぐに更新がきました。もっと苦労するかと思っていましたがすんなりと実装することが出来ました。ん?すんなり?嘘です。思いきりどハマりしたところが何箇所かありましたねぇ。

ということで今回はGoogleContactsのグループデータの取得、登録、更新そして削除についてまとめてご紹介します。

まずは一覧の取得方法です。

try {
 // perform login and set protocol version to 3.0
 $client = Zend_Gdata_ClientLogin::getHttpClient($this->user, $this->pass, 'cp');
 $gdata = new Zend_Gdata($client);
 $gdata->setMajorProtocolVersion(3);

 // perform query and get feed of all results
 $query = new Zend_Gdata_Query('http://www.google.com/m8/feeds/groups/default/full');
 $query->maxResults = 0;
 $query->setParam('orderby', 'lastmodified');
 $query->setParam('sortorder', 'descending');
 $feed = $gdata->getFeed($query);

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

 // parse feed and extract contact information
 // into simpler objects
 $groups = array();
 foreach($feed as $entry){
  $obj = new stdClass;
  $xml = simplexml_load_string($entry->getXML());
  if (! isset($xml->systemGroup)) {
   $obj->edit = (string)$entry->getEditLink()->href;
   $obj->group_name = (string) $entry->title;
  } else {
   $obj->edit = 'system_group';
   $val = $this->config->item('system_groups','config_contacts');
   $obj->group_name = $val[(string)$xml->systemGroup['id']];
  }
  $groups[] = $obj;
 }

} catch (Exception $e) {
 die('ERROR:' . $e->getMessage());
}

$data['groups'] = $groups;

return $data;

contactの時と何が違うかというと宛先URLが変っただけですね。グループデータの取得についてはhttp://www.google.com/m8/feeds/groups/default/fullこちらからの取得になります。それ以降はcontactsの時と同じ処理ですのでおわかり頂けるかと思います。

続いては登録部分です。

try {
 // perform login and set protocol version to 3.0
 $client = Zend_Gdata_ClientLogin::getHttpClient($this->user, $this->pass, 'cp');
 $gdata = new Zend_Gdata($client);
 $gdata->setMajorProtocolVersion(3);

 // create new entry
 $doc = new DOMDocument();
 $doc->formatOutput = true;
 $entry = $doc->createElement('atom:entry');
 $entry->setAttributeNS('http://www.w3.org/2000/xmlns/' ,'xmlns:atom', 'http://www.w3.org/2005/Atom');
 $entry->setAttributeNS('http://www.w3.org/2000/xmlns/' ,'xmlns:gd', 'http://schemas.google.com/g/2005');
 $doc->appendChild($entry);

 // add category element
 $category = $doc->createElement('atom:category');
 $category->setAttribute('scheme', 'http://schemas.google.com/g/2005#kind');
 $category->setAttribute('term','http://schemas.google.com/contact/2008#group');
 $entry->appendChild($category);

 // add title element
 $title = $doc->createElement('atom:title', $this->input->post('group_name', TRUE));
 $title->setAttribute('type', 'text');
 $entry->appendChild($title);

 $entryResult = $gdata->insertEntry( $doc->saveXML(), 'https://www.google.com/m8/feeds/groups/default/full');

} catch (Exception $e) {
 die('ERROR:' . $e->getMessage());
}

return $entryResult->id;

登録もputするURLがgroups用になっているだけでそれ以外には項目がグループの方がかなり少ないといったところでしょうか。本来「gd:extendedProperty」という項目にグループの情報を登録することが出来るのですが登録自体は成功するもののデータ更新時にこのinfoデータをxmlから取得するのが上手くいかず登録から断念することになりました。取得した$feedの中に改行が入ってしまっていたりしたのでなにかバグがあるのかも?
作成するxmlはデベロッパーガイドを参考にして作成しています。

続いては削除です。

try {
 // perform login and set protocol version to 3.0
 $client = Zend_Gdata_ClientLogin::getHttpClient($this->user, $this->pass, 'cp');
 // set GData delete headers
 $client->setHeaders('If-Match: *');
 $gdata = new Zend_Gdata($client);
 $gdata->setMajorProtocolVersion(3);

 // delete entry
 $gdata->delete($this->input->post('edit',TRUE));
} catch (Exception $e) {
 die('ERROR:' . $e->getMessage());
}

削除もcontactsと同様editタグに指定されているキーを送ってあげることで簡単に削除が出来ます。今回もIf-Match:*を指定して簡単に削除を行なっています。

最後に更新です。

try {
 // perform login and set protocol version to 3.0
 $client = Zend_Gdata_ClientLogin::getHttpClient($this->user, $this->pass, 'cp');
 $client->setHeaders('If-Match: *');
 $gdata = new Zend_Gdata($client);
 $gdata->setMajorProtocolVersion(3);

 $query = new Zend_Gdata_Query($this->input->post('edit'));
 $entry = $gdata->getEntry($query);
 $xml = simplexml_load_string($entry->getXML());

 $xml->title = $this->input->post('group_name');

 $entryResult = $gdata->updateEntry($xml->saveXML(), $entry->getEditLink()->href);

} catch (Exception $e) {
 die('ERROR:' . $e->getMessage());
}

return $entryResult->id;

更新も項目が少ないのでとても短かなソースになっています。editデータを送りqueryを実行、取得したxmlデータの中でtitleタグに新しいグループ名を代入してZendFrameworkのupdateEntryコマンドで送信して完了です。

contactsの方で苦労したせいかgroupデータについてはわりと簡単に実装することが出来ました。えっ?ハマったところ?データをjavascriptでいじる部分でハマっただけです。(笑

2010年12月24日金曜日

CodeIgniterでGoogleContactsAPIのテスト(5)

さてちょっと時間が空いてしまいましたが今回はContactsの更新です。
これまで一覧取得、登録そして削除ときましたので更新が出来たら一通りデータをいじれるようになる訳です。更新も削除と同じく一覧取得で得ているidをキーに行ないます。

$obj->edit = (string)$entry->getEditLink()->href;

こんな感じで取得してましたね。
ZendFramework様々といったところでしょうか。

今回時間が空いてしまったのはjavascript等をいじっていたせいなんですがこれはまあ他のサイトを参考にしてアレンジしていただけなので省略してContactsデータの更新に焦点をあててご紹介します。

// perform login and set protocol version to 3.0
$client = Zend_Gdata_ClientLogin::getHttpClient($this->user, $this->pass, 'cp');
$client->setHeaders('If-Match: *');
$gdata = new Zend_Gdata($client);
$gdata->setMajorProtocolVersion(3);

$query = new Zend_Gdata_Query($this->input->post('edit'));
$org_entry = $gdata->getEntry($query);
$xml = simplexml_load_string($org_entry->getXML());

上記のような形でアクセスを行ないqueryのデータとしてeditキーを渡しています。
通常はheaderにETagの値を指定して更新を行うらしいですが自分しかデータをいじらないアカウントですのでアスタリスクを指定しETagはデータに含めていません。

このqueryで得られたデータを上書きしていってputすることでデータが更新されるのですがデータ削除を行なうことを考えて一旦メールアドレス等のデータを全削除してからデータ登録を行なったのですがエレメントの順番が大切なようでエラーが出てしまい今回は全てのデータを新たなDOMDocumentに追加してくという手法を取りました。

// create new entry
$doc = new DOMDocument();
$doc->formatOutput = true;
$entry = $doc->createElement('atom:entry');
$entry->setAttributeNS('http://www.w3.org/2000/xmlns/' ,'xmlns:atom', 'http://www.w3.org/2005/Atom');
$doc->appendChild($entry);

// add name element
$name = $doc->createElement('name');
$name->setAttribute('xmlns', 'http://schemas.google.com/g/2005');
$entry->appendChild($name);
//input->post()メソッドを利用してXSS対策
$fullName = $doc->createElement('fullName', $this->input->post('name', TRUE));
$name->appendChild($fullName);

// add org name element
$org = $doc->createElement('organization');
$org->setAttribute('xmlns', 'http://schemas.google.com/g/2005');
$org->setAttribute('rel' ,'http://schemas.google.com/g/2005#work');
$entry->appendChild($org);
$orgName = $doc->createElement('orgName', $this->input->post('org', TRUE));
$org->appendChild($orgName);

// add email elements
$emails = $this->input->post('email', TRUE);
$epleces = $this->input->post('email_place', TRUE);
foreach ($emails as $key => $e) {
 if ($e != '') {
  $email = $doc->createElement('email');
  $email->setAttribute('xmlns','http://schemas.google.com/g/2005');
  if ($epleces[$key] == 'custom') {
   $email->setAttribute('label', $this->input->post('email_custom', TRUE));
  } else {
   $email->setAttribute('rel', 'http://schemas.google.com/g/2005#' . $epleces[$key]);
  }
  $email->setAttribute('address', $e);
  $entry->appendChild($email);
 }
}

// add phone elements
$phones = $this->input->post('phone', TRUE);
$pplaces = $this->input->post('phone_place', TRUE);
foreach ($phones as $key => $p) {
 if ($p != '') {
  $phone = $doc->createElement('phoneNumber', $p);
  $phone->setAttribute('xmlns','http://schemas.google.com/g/2005');
  if ($pplaces[$key] == 'custom') {
   $phone->setAttribute('label', $this->input->post('phone_custom', TRUE));
  } else {
   $phone->setAttribute('rel', 'http://schemas.google.com/g/2005#' . $pplaces[$key]);
  }
  $entry->appendChild($phone);
 }
}

// add web elements
$webs =$this->input->post('web', TRUE);
$wplaces = $this->input->post('web_place', TRUE);
foreach ($webs as $key => $w) {
 if ($w != '') {
  $web = $doc->createElement('website');
  $web->setAttribute('xmlns','http://schemas.google.com/contact/2008');
  $web->setAttribute('href',$w);
  if ($wplaces[$key] == 'custom') {
   $web->setAttribute('label', $this->input->post('web_custom', TRUE));
  } else {
   $web->setAttribute('rel', $wplaces[$key]);
  }
  $entry->appendChild($web);
 }
}

foreach ($org_entry->category as $category) {
 $categorys = $doc->createElement('atom:category');
 $categorys->setAttribute('term', $category->getTerm());
 $categorys->setAttribute('scheme', $category->getScheme());
 $entry->appendChild($categorys);
}


$id = $doc->createElement('atom:id', $org_entry->id);
$entry->appendChild($id);

foreach ($org_entry->link as $link) {
 $links = $doc->createElement('atom:link');
 $links->setAttribute('href', $link->getHref());
 $links->setAttribute('rel', $link->getRel());
 $links->setAttribute('type', $link->getType());
 $entry->appendChild($links);
}

$title = $doc->createElement('atom:title',$this->input->post('name', TRUE));
$entry->appendChild($title);

$updated = $doc->createElement('atom:updated', $org_entry->updated);
$entry->appendChild($updated);

$edited = $doc->createElement('atom:edited', $org_entry->edited);
$entry->appendChild($edited);

更新データ以外のデータについては先程行なったqueryの結果をそのまま入れるようにしてあります。じっくりコードを見るとわかると思いますがGmailではメール等の付随データとして仕事や自宅等が選べるようになっていますがその機能にも対応しています。コード中にcustomの文字が見えるかと思います。カスタムデータの場合にはrel要素ではなくlabel要素としてデータをタグに含めて送ることでデータが登録されます。

$entryResult = $gdata->updateEntry($entry_data, $org_entry->getEditLink()>href);

最後にZendFrameworkGDataAPIのupdateEntryメソッドでデータを送って更新が完了です。一緒に送っているのは冒頭で述べたlinkタグに含まれるeditキーです。

これでContactsについては一通りデータのやりとりが出来るようになりました。次の予定ではこれら連絡先を管理するには大切なグループについて登録や更新、削除を出来るようにしてみたいと思います。この部分についてはIBMのサンプルがないので英語ドキュメントを読みながらの作業になります。さらに時間がかかってしまうかも?

2010年12月18日土曜日

CodeIgniterでGoogleContactsAPIのテスト(4)

IBMのGoogleContactsサンプルのCI化第三弾はデータ削除です。

削除機能実装についてはわりとスムーズに行きました。

formタグで削除する連絡先のデータをhiddenで隠し持ちsubmitボタンで確認ダイアログを出しOKボタンで削除実行という流れです。

確認ダイアログのjavascriptは以下のようになっています。

<script language="JavaScript">
<!--
function check(){
    if(window.confirm('削除して宜しいですか?')){
        return true;
    } else {
        window.alert('削除を中止しました。');
        return false;
    }
}
//-->

一般的なスクリプトですから説明はいらないと思いますがOKボタンを押したときにtrueを返し呼び出し側でそれを検知してsubmitを実行するか判断するというものです。




表示した画像は上記のようになっています。リンククリックで即座に削除はちょっと怖いかな?とこんな機構を取り入れてみました。

<div class="name">
    <?php echo $r->name; ?>
    <span class="links">
        <?php echo form_open('contacts/delete','onSubmit="return check()"'); ?>
        <?php echo form_hidden('id',$r->edit)?>
        <?php echo form_submit('submit', '削除'); ?>
        <?php echo form_close(); ?>
    </span>
</div>

上記がその判断を行なうformタグ部分です。CodeIgniterのformヘルパーを利用しているのでちょっとわかりにくいかもしれませんがsubmit先がcontacts.phpのdeleteメソッドで連絡先のidを送っていることがおわかり頂けますでしょうか。

続いてはコントローラのdelete()メソッドを見てみましょう。

public function delete() {
    $this-<gdata_model-<delete();

    $data = $this-<gdata_model-<getContactsList();
    $this-<load-<view('contacts-index', $data);
}

gdata_modelのdeleteメソッドを呼びだしそちらでデータを削除後一覧表示画面を呼びだしている形になっています。削除すべき連絡先のIDについては$_POSTから受けとるため引数として渡しておりません。このあたりのお作法がよくわかっていませんがmodelでも$_POSTが受けとれるようになっているので問題ないかな?

続いてはmodelのdelete()メソッドです。メソッド名がかぶってしまっていますがまあ気にしない方向で…。(笑

public function delete() {
    try {
        // perform login and set protocol version to 3.0
        $client = Zend_Gdata_ClientLogin::getHttpClient($this-<user, $this-<pass, 'cp');
        // set GData delete headers
        $client-<setHeaders('If-Match: *');
        $gdata = new Zend_Gdata($client);
        $gdata-<setMajorProtocolVersion(3);

        // delete entry
        $gdata-<delete($this-<input-<post('id',TRUE));
    } catch (Exception $e) {
        die('ERROR:' . $e-<getMessage());
    }
}

こちらも登録に比べると非常にシンプルに完了しています。ログインしてプロトコルバージョンの指定およびdelete()メソッドの実行です。たったこれだけで削除については実装できてしまいました。

残すはデータ修正ですがこれは再び苦労しそうな予感がします。CIの使い方の問題とGoogleDataAPIともにまだはっきりと指針を詰められていません。じっくりドキュメントを読んで実装方法を考えてみたいと思います。IBMのサンプルプログラムに修正はないんですよねぇ。(笑

2010年12月17日金曜日

CodeIgniterでGoogleContactsAPIのテスト(3)

IBMのGoogleContactsサンプルのCI化第三弾はデータ登録です。

さて続いてはデータ登録部分を実装することにしたいと思います。

IBMのサンプルでは前記事でも書いたようにcontacts-save.php1ファイルで完結しています。これをmodelとviewに分けてCodeIgniter上で動くように変更します。サンプルでは登録出来る項目と表示している項目に違いがあるのでとりあえず表示している項目については登録を出来るようにしました。

contacts-save.phpでは$_POSTで受けとったデータをhtmlentities()を使ってサニタイズしていますがこれが原因で日本語が通らなくなっていたのでCodeIgniterのバリデーション機能に置き換えて実装しています。また入力フォームの生成にCodeIgniterのformヘルパーを利用するよう変更しています。

いやぁ…やたら苦労しましたこの部分。ハマりどころ満載というか英語が読めないというか。一応希望するデータ項目については登録が出来るようになったので記事化します。

まずは登録フォームから見ていきましょう。

<!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>新規連絡先登録</title>
    <style type="text/css">
        body {
            font-family: "MS Pゴシック", Osaka, "ヒラギノ角ゴ Pro W3";
        }
        div.red {
            color: red;
            text-decoration: none;
            font-weight: bold;
        }
    </style>
</head>
<body>
    <h2>連絡先新規登録</h2>
    <?php echo validation_errors(); ?>
    <?php echo form_open('contacts/post'); ?>
        <p>
            名前(必須): <br/>
            <?php echo form_input('name', set_value('name'), 'size="20"'); ?><br/>
        </p>
        <p>
            会社名: <br/>
            <?php echo form_input('org', set_value('org'), 'size="35"'); ?><br/>
        </p>
        <p>メールアドレスもしくは電話番号は必ず入力してください。
        </p>
        <p>
            メールアドレス:<br/>
            <?php echo form_dropdown('mail1_place', $mail_options); ?>
            <?php echo form_input('mail1', set_value('mail1'), 'size="35"'); ?><br/>
        </p>
        <p>
            電話番号:<br/>
            <?php echo form_dropdown('phone1_place', $phone_options); ?>
            <?php echo form_input('phone1', set_value('phone1'), 'size="35"'); ?><br/>
            <br/>ハイフンは含めず数字のみで入力してください。
        </p>
        <p>
            webサイト:<br/>
            <?php echo form_dropdown('web1_place', $web_options); ?>
            <?php echo form_input('web1', set_value('web1'), 'size="35"'); ?><br/>
        </p>
        <?php echo form_submit('submit', '登録'); ?>
        <?php echo form_reset('reset', 'リセット'); ?>
    <?php echo form_close(); ?>
</body>
</html>

説明が必要そうなところとしては<?php echo validation_errors(); ?>と云う部分でしょうか。validatitonの時は個々の項目についてエラー時にメッセージが指定できたのですがform_validationになってまだその機能が実装されていないようで一箇所にエラーが集まって表示されます。またformヘルパーを利用してformタグを生成しています。
selectタグのoptionについてはController上で配列を用意してviewに渡しviewでformヘルパーを利用して展開している感じです。set_value()メソッドを使ってエラー時に入力済みデータを保持するようにしています。


続いてはこのviewを呼びだすcontrollerに追加した関数です。

public function add() {
    $this->load->view('add_contacts_view', $this->_get_option_data());
}
    
public function _get_option_data() {
    $mail_options = array(
                            'work' => '仕事',
                            'home' => '自宅'
                        );
    $data['mail_options'] = $mail_options;

    $phone_options = array(
                            'mobile'=> '携帯電話',
                            'work' => '仕事',
                            'home' => '自宅',
                            'main' => 'メイン',
                            'work_fax' => 'FAX(勤務先)',
                            'home_fax' => 'FAX(自宅)',
                            'pager' => 'ポケベル'
                        );
    $data['phone_options'] = $phone_options;

    $web_options = array(
                            'profile' => 'プロフィール',
                            'blog' => 'ブログ',
                            'home-page' => 'ホームページ',
                            'work' => '仕事'
                        );
    $data['web_options'] = $web_options;

    return $data;
}

add()メソッドはブラウザからアクセス可能なメソッドとして新規連絡先登録画面として呼びだされます。このメソッド内で先程作成した登録フォームのhtmlを呼びだし_get_option_data()メソッドでselectタグで利用する配列を生成しています。メソッド名の頭にアンダースコアがついているのはCodeIgniterの機能を利用してこのメソッドにブラウザからアクセスするのを防ぐためです。

続いてはブラウザにて登録フォームにデータを入力しそのデータを受けとるpost()メソッドを見ていきたいと思います。

public function post() {
 //バリデーション導入
 //XSSサニタイズはmodelにて行う
 $this->form_validation->set_rules('name','名前','trim|required|max_length[40]');
 $this->form_validation->set_rules('org','会社名','trim|max_length[40]');
 $this->form_validation->set_rules('email1','メールアドレス','trim|callback__mail_require_check|valid_email');
 $this->form_validation->set_rules('email1_place','メールアドレスの場所','trim|alpha_dash');
 $this->form_validation->set_rules('phone1','電話番号','trim|numeric|callback__phone_require_check|max_length[11]');
 $this->form_validation->set_rules('phone1_place','電話番号の場所','trim|alpha_dash');
 $this->form_validation->set_rules('web1','webサイト','trim|max_length[50]');
 $this->form_validation->set_rules('web1_place','webサイトの場所','trim|alpha_dash');

 $this->error_enable = FALSE;

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

public function _mail_require_check($str) {
if ($this->data_require_check()==FALSE) {
  $this->error_enable = TRUE;
  $this->form_validation->set_message('_mail_require_check', 'メールアドレスもしくは電場番号が必須です。');
  return FALSE;
 } else {
  return TRUE;
 }
}

public function _phone_require_check($str) {
 if ($this->error_enable == FALSE) {
  if ($this->data_require_check()==FALSE){
   $this->form_validation->set_message('_phone_require_check', 'メールアドレスもしくは電場番号が必須です。');
   return FALSE;
  } else {
   return TRUE;
  }
 }
 return TRUE;
}

public function data_require_check() {
 if (($_POST['mail1']=='') AND ($_POST['phone1']=='')) {
  return FALSE;
 } else {
  return TRUE;
 }
}

まずpost()メソッドを見ていただくとCodeIgniterのバリデーションクラスを利用してデータ検証を行なっているのがわかるかと思います。名前データは必須でメールアドレスもしくは電話番号の入力が必要としたかったのでユーザ定義のバリデーションを行なっています。

ユーザ定義のバリデーションはcontrollerの中に書けという風にドキュメントにも記載されているのですがそれだとブラウザからのアクセスを許可してしまうことになるので先程ご紹介したメソッド名にアンダースコアを付ける方法を採用。実際にバリデーションするときにはcallback_を付加するとのことでアンダースコアが2重になってしまっていますがいわゆるバッドノウハウってやつでしょうかねぇ。本来なら別ファイルに定義したデータを読み込むとかの手法の方がよいように思うのですがどうなんでしょう?controllerに定義を記述するのってなんとなくイヤンな感じがします。

また個別にバリデーション定義をし片方のバリデーションで一度エラーがとなった時には再度エラーメッセージを追加しないようにしています。もうちょっとスマートな記述方法がありそうですが私のスキルではこれが精一杯でした。(汗

$this->form_validation->run()という部分でバリデーションを実行している訳ですがバリデーションを通ったら連絡先一覧をviewとして読込み通らなかった場合はadd_contacts_viewを読み込んでエラー表示をしています。



続いては実際にGoogleDataAPIを通じてデータ登録を行なっているpost()メソッドを見てみましょう

public function post() {
    // check for required input
    /* バリデーションを実施するので削除
    if (empty($this->input->post('name'))) {
        die('ERROR: Missing name');
    }

    if (empty($_POST['email'])) {
        die('ERROR: Missing email address');
    }

    if (empty($_POST['org'])) {
        die('ERROR: Missing organization');
    }
    */

    try {
        // perform login and set protocol version to 3.0
        $client = Zend_Gdata_ClientLogin::getHttpClient($this->user, $this->pass, 'cp');
        $gdata = new Zend_Gdata($client);
        $gdata->setMajorProtocolVersion(3);

        // create new entry
        $doc = new DOMDocument();
        $doc->formatOutput = true;
        $entry = $doc->createElement('atom:entry');
        $entry->setAttributeNS('http://www.w3.org/2000/xmlns/' ,'xmlns:atom', 'http://www.w3.org/2005/Atom');
        $entry->setAttributeNS('http://www.w3.org/2000/xmlns/' ,'xmlns:gd', 'http://schemas.google.com/g/2005');
        $entry->setAttributeNS('http://www.w3.org/2000/xmlns/' ,'xmlns:gContact', 'http://schemas.google.com/contact/2008');
        $doc->appendChild($entry);

        // add name element
        $name = $doc->createElement('gd:name');
        $entry->appendChild($name);
        //input->post()メソッドを利用してXSS対策
        $fullName = $doc->createElement('gd:fullName', $this->input->post('name', TRUE));
        $name->appendChild($fullName);

        // add org name element
        $org = $doc->createElement('gd:organization');
        $org->setAttribute('rel' ,'http://schemas.google.com/g/2005#work');
        $entry->appendChild($org);
        $orgName = $doc->createElement('gd:orgName', $this->input->post('org', TRUE));
        $org->appendChild($orgName);

        // add email elements
        $email = $doc->createElement('gd:email');
        $email->setAttribute('address', $this->input->post('mail1', TRUE));
        $email->setAttribute('rel', 'http://schemas.google.com/g/2005#' . (String)$this->input->post('mail1_place', TRUE));
        $entry->appendChild($email);

        // add phone elements
        $phone = $doc->createElement('gd:phoneNumber', $this->input->post('phone1', TRUE));
        $phone->setAttribute('rel', 'http://schemas.google.com/g/2005#' . (String)$this->input->post('phone1_place', TRUE));
        $entry->appendChild($phone);

        // add web elements
        $web = $doc->createElement('gContact:website');
        $web->setAttribute('href', $this->input->post('web1', TRUE));
        $web->setAttribute('rel', $this->input->post('web1_place', TRUE));
        $entry ->appendChild($web);

        // insert entry
        $hoge = $doc->saveXML();
        //$fp = fopen("c:\sampleEntry.txt", "w");
        //fwrite($fp, $hoge);
        //fclose($fp);

        $entryResult = $gdata->insertEntry($hoge, 'http://www.google.com/m8/feeds/contacts/default/full');

    } catch (Exception $e) {
        die('ERROR:' . $e->getMessage());
    }

    return $entryResult->id;
}

頭の部分のコメントアウトはデータ未入力チェックですがバリデーションでデータチェックは行なっているので必要ありません。入力データをxmlにしてgoogleに送るって登録する訳ですがかなり苦労しました。websiteのデータや記念日等についてはgContactというプレフィックスを付けないと登録できないなどドキュメントが英語だったので読んでいない弊害をまざまざと見せつけられちゃんと読まないとなぁと思ってはいますがどうなることやら。

<?xml version="1.0"?>
<atom:entry xmlns:atom="http://www.w3.org/2005/Atom" xmlns:gd="http://schemas.google.com/g/2005" xmlns:gContact="http://schemas.google.com/contact/2008">
  <gd:name>
    <gd:fullName>薬剤 三郎</gd:fullName>
  </gd:name>
  <gd:organization rel="http://schemas.google.com/g/2005#work">
    <gd:orgName>××株式会社</gd:orgName>
  </gd:organization>
  <gd:email address="fuga@gmail.com" rel="http://schemas.google.com/g/2005#work"/>
  <gd:phoneNumber rel="http://schemas.google.com/g/2005#work_fax">0300000000</gd:phoneNumber>
  <gContact:website href="http://www.fuga.com" rel="profile"/>
</atom:entry>

上記はmodelのpost()メソッドで作成したgoogleに投げるxmlファイルです。こんな形にして登録を行なっています。

またXSS対策として$this->input->post()メソッドつかってサニタイズしています。google contactsに登録できる項目はもっと沢山あるので徐々に登録方法を検証していきたいと思っています。また複数のメールアドレスや電話番号そしてwebサイトの登録機能を後で追加したいと思います。

さて大分機能が実装されてきましたがとりあえず次はデータ削除部分を実装する予定です。登録部分ももうちょっと後でモディファイしさらには修正も出来るようにテスト実装する予定です。しかしテストにいったい何日かかってるんだろう?(汗

2010年12月15日水曜日

CodeIgniterでGoogleContactsAPIのテスト(2)

さてIBMのサイトで公開されているZendFrameworkでGoogleContactsにアクセスするサンプルをCodeIgniter上で動かすテスト本番です。

配布されているサンプルはcontacts-index.phpにて一覧表示を行ないcontacts-delete.php(削除)とcontacts-save.php(新規追加)にリンクが貼られています。削除についてはそれぞれの連絡先のIDが埋め込まれておりそのまま削除を実行し結果を表示。新規追加については入力フォームを表示し自身を呼びだしGoogleへデータ追加した後その追加されたIDを表示するという構造になっています。

これをCodeIgniterに移植するにあたって以下のような方針を立てました。

・メールアドレスとパスワードはconfig_contacts.phpを作成して登録
・GoogleDataAPIにアクセスするモデルはgdata_model.php。
このモデル内でのみGoogleDataAPIにアクセスし結果をコントローラcontactsに返す。

○作成するメソッドデータ
ページ メソッド名 URL ビュー名
一覧 index() http://localhost/test/ contacts_view
入力 add() http://localhost/test/add/ add_contacts_view
登録 post() http://localhost/test/post/
登録後は一覧表示に遷移
contacts_view
削除 delete() http://localhost/test/delete/
削除確認ダイアログを表示
削除後は一覧表示に遷移
delete_view

こんな形で実装していくことにしました。

そんなこんなでまずはindexメソッドを実装してみたいと思います。
indexメソッドではGoogleDataAPIにアクセスして一覧を取得するという動作を予定しています。
つまりはIBMサンプルの中のcontacsts-index.phpの内容をmodelに移動して動作させればいいということになりますね。まずはGmailアドレスとパスワードを保持するconfig_contacts.phpから作成しましょう。

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

$config['contacts_user'] = 'test.kaepapa@gmail.com';
$config['contacts_pass'] = '********';

/* End of file config_contacts.php */
/* Location: ./system/application/config/config_contacts.php */

パスワードについては*で伏せてあります。これでcontrollerやmodelそしてviewから以下の手法でアクセスすることが出来るようになります。

$this->config->load('config_contacts', TRUE);
$user = $this->config->item('contacts_user','config_contacts');
$pass = $this->config->item('contacts_pass','config_contacts');

そしてcontacs_index.phpのロジック部分をgdata_model.phpに移設しました。


<?php

class Gdata_model extends Model {

    private $user;
    private $pass;

    public function __construct() {
        parent::Model();
        // load Zend Gdata libraries
        require_once 'Zend/Loader.php';
        Zend_Loader::loadClass('Zend_Gdata');
        Zend_Loader::loadClass('Zend_Gdata_ClientLogin');
        Zend_Loader::loadClass('Zend_Http_Client');
        Zend_Loader::loadClass('Zend_Gdata_Query');
        Zend_Loader::loadClass('Zend_Gdata_Feed');

        $this->config->load('config_contacts', TRUE);
        $this->user = $this->config->item('contacts_user','config_contacts');
        $this->pass = $this->config->item('contacts_pass','config_contacts');

    }

    public function getContactsList() {

        try {
            // perform login and set protocol version to 3.0
            $client = Zend_Gdata_ClientLogin::getHttpClient($this->user, $this->pass, 'cp');
            $gdata = new Zend_Gdata($client);
            $gdata->setMajorProtocolVersion(3);

            // perform query and get feed of all results
            $query = new Zend_Gdata_Query('http://www.google.com/m8/feeds/contacts/default/full');
            $query->maxResults = 0;
            $query->setParam('orderby', 'lastmodified');
            $query->setParam('sortorder', 'descending');
            $feed = $gdata->getFeed($query);

            $data['title'] = $feed->title;
            $data['totalResults'] = $feed->totalResults;

            // parse feed and extract contact information
            // into simpler objects
            $contacts = array();
            foreach($feed as $entry){
                $obj = new stdClass;
                $xml = simplexml_load_string($entry->getXML());
                $obj->edit = $entry->getEditLink()->href;
                $obj->name = (string) $entry->title;
                $obj->orgName = (string) $xml->organization->orgName;
                $obj->orgTitle = (string) $xml->organization->orgTitle;

                foreach ($xml->email as $e) {
                    $obj->emailAddress[] = (string) $e['address'];
                }

                foreach ($xml->phoneNumber as $p) {
                    $obj->phoneNumber[] = (string) $p;
                }
                foreach ($xml->website as $w) {
                    $obj->website[] = (string) $w['href'];
                }

                $contacts[] = $obj;
            }
        } catch (Exception $e) {
            die('ERROR:' . $e->getMessage());
        }
        $data['contacts'] = $contacts;
        return $data;
    }
}

/* End of file gdata_model.php */
/* Location: ./system/application/models/gdata_model.php */

コンストラクタにてZendGdataライブラリを読み込みと独自設定ファイルを読み込んでいます。それ以外のgetContactsListメソッドについてはほぼcontacts_index.phpをそのまま利用している状況です。

続いてはcontrollerであるcontacts.phpを作成しました。

<?php

class Contacts extends Controller {

 public function __construct(){
  parent::Controller();
  $this->load->model('gdata_model');
 }

 public function index(){
  $data = $this->gdata_model->getContactsList();
  $this->load->view('contacts-index', $data);
 }
}

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

コンストラクタで先程作成したmodelをロードしています。そしてデフォルトメソッドでcontactsの一覧配列を取得してviewに渡しています。

続いてcontacts-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>Listing contacts</title>
<style>
 body {
  font-family: Verdana;
 }
 div.name {
  color: red;
  text-decoration: none;
  font-weight: bolder;
 }
 div.entry {
  display: inline;
  float: left;
  width: 450px;
  height: 150px;
  border: 2px solid;
  margin: 10px;
  padding: 5px;
 }
 td {
  vertical-align: top;
 }
 span.links {
  float: right;
 }
</style>
</head>
<body>
 <h2>Contacts</h2>
 <h2><?php echo $title ?></h2>
 <div>
 <?php echo $totalResults ?> contact(s) found.
 </div>
 <div>
  <a href="contacts/add">Add a new contact</a>
 </div>

 <?php
 // display results
 foreach ($contacts as $r) {
 ?>
 <div class="entry">
  <div class="name"><?php echo (!empty($r->name)) ? $r->name : 'Name not available'; ?>
  <span class="links"><a href="contacts-delete.php?id=<?php echo $r->edit; ?>">Delete</a></span>
  </div>
  <div class="data">
  <table>
   <tr>
   <td>Organization:</td>
   <td><?php echo $r->orgName; ?></td>
   </tr>
   <tr>
   <td>Email:</td>
   <td><?php echo @join(', ', $r->emailAddress); ?></td>
   </tr>
   <tr>
   <td>Phone:</td>
   <td><?php echo @join(', ', $r->phoneNumber); ?></td>
   </tr>
   <tr>
   <td>Web:</td>
   <td><?php echo @join(', ', $r->website); ?></td>
   </tr>
  </table>
  </div>
 </div>
 <?php
 }
 ?>
</body>
</html>

サンプルからphpコードをごっそりと抜いて一部変数名が変更になっているくらいで済んでしまいました。

これでばっちりと当初と同様連絡先一覧の取得が出来ていますのでCI化が進んでいるといった感じでしょうか。すんなりと進んだように書いてあいますがもちろんいろいろとハマりまくっての結果です。どうも私はまだPHP自体を理解できていないようで変数が空になっていてエラーを乱発してしまったのは内緒です。(汗

2010年12月14日火曜日

CodeIgniterでGoogleContactsAPIのテスト(1)

さてGoogleContactsへPHPからアクセスしてもげもげという話の続きです。

IBMのサイトにある情報を参考にGoogleDataAPIとZendFrameworkの使い方を理解すべくちょっとテストです。



まずはテストプロジェクトを作成して本日公開されたばかりのCodeIgniter日本語パックのindex.htmlとsystemフォルダをコピりました。



もちろんtestフォルダの中には

RewriteEngine on
RewriteCond $1 !^(index\.php|css|user_guide|.+\.gif$|.+\.jpg$|.+\.png$|.+\.js$|robots\.txt)
RewriteRule ^(.*)$ /test/index.php/$1 [L]

という内容の.htaccessファイルも作っておきます。

デフォルトのwelcome関係のファイルはとりあえずサクっと削除しておきました。
このままですと http://localhost/test/ へのアクセスでデフォルトコントローラがないと怒られると思うのでroutes.phpの内容を編集して

$route['default_controller'] = "contacts";
$route['scaffolding_trigger'] = "";

としてあります。

またCodeIgniter徹底入門の通りconfig.phpの内容も

$config['base_url'] = "http://localhost/test/";
$config['index_page'] = "";
$config['uri_protocol'] = "PATH_INFO";
$config['language'] = "japanese";
$config['log_threshold'] = 4;

とそれぞれ変更してある状態です。

IBMで配布されているサンプルをとりあえずはそのまま使ってみるためにcontacts.phpコントローラを以下のように編集しました。

<?php

class Contacts extends Controller {

 function __construct()
 {
  parent::Controller();
 }

 function index()
 {
  $this->load->view('contacts-index');
 }
}

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

そして http://localhost/test/ にアクセスすると



ばっちりテスト用アカウントに登録されている連絡先情報を取得出来ることが確認できました!ちょっと前の記事なので仕様の変更とかあったら動かないよなぁと思っていたのですがそのまま動いて良かったぁ。じっくりとソースを読み勉強しようと思います。

さてこれを今後ちまちまといじっていく訳ですがとりあえずCodeIgniter上でのDeleteやAdd a new contact.がちゃんと動くようにしてGoogleDataAPIでのアクセスをModelで行なうように変更。取得したデータをViewに表示という風にMVCアプリらしく変更してみる予定です。

wordpressも勉強中だしこのブログのcssやjsもちょっといじりたいし時間が足りません。(汗

2010年12月13日月曜日

PHPからGoogleDataAPIをいじる。

仕事で使うためにPHPとCodeIgniterを勉強しているところでな訳です。

あまり好きなタイプの言語ではないもののやはりユーザが多いということは環境構築ノウハウやレンタルサーバでの対応等についても良好なのでこの選択をしました。

GoogleDataAPIを使ってちょっとGoogleカレンダーや連絡先管理ツールをほげほげするようなものを作る予定でZendFrameworkを使ってアクセスする予定。しかしあまり情報がないですなぁ。
ドキュメントが一部日本語化されていることとIBMが公開している情報等を手掛りになんとか頑張る予定。

TokyoGoogleTechnologyUsersGroupという団体にも行きついたものの現在はあまり活動していない様子。

webアプリからGoogle提供のサービスをいじろうという需要ってあまりないのかな?とりあえず勉強がてらいろいろテストをする予定です。

2010年12月9日木曜日

CodeIgniterでindex.phpを省略する

さて先日からCodeIgniterをいじってみている訳ですが先日のHelloWorldの後ようやく購入した徹底入門を読んでいます。
それに従っていろいろと設定をいじったりしている訳ですがよくもまあここまでと云うほどハマりまくり。

先日のHelloWorldなプロジェクトでアクセスしたlocalhostのURIは

http://localhost/helloWorld/ もしくは http://localhost/index.php/helloWorld/hello/index


などとアクセスしていた訳ですが後者のindex.phpが邪魔くさい訳でapacheを利用している場合は.htaccessを使うことで省略することが可能らしいことを知りやってみた訳です。

実際に用いた.htaccessファイルの内容は

RewriteEngine on
RewriteCond $1 !^(index\.php|css|user_guide|.+\.gif$|.+\.jpg$|.+\.png$|.+\.js$|robots\.txt)
RewriteRule ^(.*)$ /helloworld/index.php/$1 [L]

ですが中のドットが一つ抜けてしまっていて30分もハマってしまいましたとさ。

あまりにも一般的な内容ですが自分の忘備録として記事にしました。(笑

なにはともあれ徹底入門を熟読してしっかりとCodeIgniterをマスターしないといけません。ってかその前にPHPがわかっていないという噂もちらほら。加えてhtml+cssやらFireworksとかも覚えたいとかちょっと欲張りすぎかも?(汗

2010年12月7日火曜日

HelloWorld of CodeIgniter on eclipse.

さて技術ネタ一発目としてCodeIgniterってのを使ってみようとしております。web系の言語としてはいままでJava一辺倒でしたがそれだけじゃイカンだろってことでphpもと欲張った結果です。

まあJavaもOSSに喧嘩を売りまくっているOracleの手中に落ちて今後どうなるかわかりませんし他のものも出来たほうがいいのかな?とも思わなくもありませんでしたしね。

phpは以前にもテンプレートエンジンのsmartyを使ってちょこっといじったことはあったのですが数年前の話で今元気がありそうなテンプレートエンジンはなんぞや!?と探していたところ行き着いたのがCodeIgniterでした。

まだ国内ではあまり使われてない様子ですが世界ではわりと人気のテンプレートエンジンのようで国内ユーザ会が提供するユーザーズガイドがしっかりしているのが利点でしょうか。

php再入門にあたり今回構築した環境はWindows上にxamppでphpとApacheそしてMySQLをお手軽にインストールしてJavaで使い慣れたeclipseのPDT環境です。インストール先はそれぞれC:\直下としてあります。eclipseのworkspaceはそのままApacheでアクセス出来るようにC:\xampp\htdocsとしてあります。

途中xamppのapacheが起動しなくなってハマりましたが原因はskypeを起動していたからでした。skypeがポート80を使っていたので設定を変更することで回避できましたとさ。(汗
ってなことでeclipseを起動してさっさとプロジェクトを作成してみましょう。



PHPエクスプローラのウインドウで右クリック[新規]-[PHPプロジェクト]を選択します。すると以下の新規PHPプロジェクトウインドウが開きます。



プロジェクト名はとりあえず「helloWorld」とありきたりな名称で。完了ボタンでプロジェクトが作成されます。Wを大文字にするあたりphpのお作法がわかっていませんがとりあえずってことでお許しください。



これでhelloWorldプロジェクトが作成されました。

ではCodeIgniterのファイル群を突っ込みます。一つのファイル群を複数プロジェクトから参照することも出来ると思いますがバージョン依存等のことも考えたりするととても小さなCodeIgniterですからプロジェクトにそのまま含めてしまうのも一つの手かな?とこの方法を選択しました。



最新のCodeIgniterを日本語ドキュメントが欲しい場合にはこちら[http://sourceforge.jp/projects/codeigniter/releases]からそして現在のように最新版が本家にある場合はこちら[http://codeigniter.com/]からダウンロードしてzipファイルの中にあるsystemフォルダとindex.phpファイルをプロジェクトフォルダ内にコピーしてください。



そしてPHPエクスプローラ上でプロジェクトをリフレッシュすると先程コピーしたsystemフォルダとindex.phpファイルが表示されます。



ではちゃんとCodeIgniterが動くかどうかの確認をしてみたいと思います。
http://localhost/helloWorld/ にアクセスしてみると



上記のようにデフォルトのwelcomeクラスが表示されちゃんと動いていることが確認できました。めでたし!

もしも表示されないときはapacheがちゃんと動いているか等xamppのコントロールパネルで確認してみてください。



で終わってしまってはちょと理解というものがなさすぎなので検証してみましょう。

http://localhost/helloWorld/ にアクセスした時に読み込まれるファイルはプロジェクトの中にコピーしたCodeIgniterに含まれていたhelloWorld/index.phpです。このファイルを覗いてみるとsystemフォルダやapplicationフォルダ等が指定されています。systemフォルダの場所を指定できるということは今回のようにプロジェクトにsystemファイルをごっそり入れない場合はここで指定してあげるのでしょうねぇ。

index.phpの中でapplicationフォルダが読み込まれる環境があるってことはその中にブラウザに表示されたsystem/application/controllers/welcome.php等のファイルを読み込む鍵があるはずってことで探してみるとsyste/application/config/routes.phpの中にありましたよ。

$route['default_controller'] = "welcome";
$route['scaffolding_trigger'] = "";

ここでデフォルトで読み込まれるコントローラが指定されていました。自分で作ったコントローラをデフォルトで表示させたいときはここで指定してあげればよさそうです。syste/application/configフォルダには他にもconfig.phpやdatabase.php等の設定ファイルがあるのでここでいろいろと設定することが出来るのでしょう。

ではwelcome_message.phpとwelcome.phpを覗いてみましょう。

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>CodeIgniterへようこそ</title>

<style type="text/css">
~省略~
</style>
</head>
<body>

<h1>CodeIgniterへようこそ!</h1>

<p>今ご覧のこのページは、CodeIgniterによって動的に生成されました。</p>

<p>このページを編集したい場合は、次の場所にあります:</p>
<code>system/application/views/welcome_message.php</code>

<p>このページのコントローラは次の場所にあります:</p>
<code>system/application/controllers/welcome.php</code>

<p>CodeIgniterを使うのが初めてなら、<a href="user_guide_ja/">ユーザガイド</a>を読むことから始めてください。</p>


<p><br />このページは、{elapsed_time} 秒でレンダリングされました。</p>

</body>
</html>

見ればそのままなんのことはなく普通のhtmlファイルな内容でした。
じゃあコントローラの方はなにをやってるの?と覗くと

<?php

class Welcome extends Controller {

    function Welcome(){
        parent::Controller(); 
    }
 
    function index(){
        $this->load->view('welcome_message');
    }
}

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

Controllerクラスを継承したWolcomeクラス内でコンストラクタとindexメソッドが存在してindexメソッドの中でviewを呼びだしているだけでした。

しかしここまで来てなんとなくHelloWorldしてみるときにやる必要のありそうなことがだいたい把握出来たような気がします。

では実際にHelloWorldに挑戦といってみましょう。

上記の例を見る限りcontrollerからprint "Hello World!!!";とかでもいいような気もしますがCodeIgniterはMVCアプリケーショを作るためのテンプレートエンジンですからちゃんとmodelとviewも使ったものにしてみましょう。といってもmodelでなにをやるかが一番の問題ではありますが…。

ということでまずはmodelから作成します。helloWorld/system/application/models/hello_model.phpファイルを作成しました。

<?php

class Hello_model extends Model{

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

    function hello(){
        return "Hello World!!!";
    }
}
/* End of file hello_model.php */
/* Location: ./helloWorld/models/hello_model.php */

Modelクラスを継承したHello_modelクラスを作成。コンストラクタメソッドの__construct()と表示する文字列を返すHello()メソッドを作りました。コンストラクタはphp5でのお作法に乗っ取っております。結局やれることといったら文字列返すくらいしか思いつきませんでした。(笑

続いてはviewの作成です。helloWorld/system/application/views/hello_message.phpを作成します。

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
    <title><?php echo $title;?></title>
</head>
<body>
    <?php echo $hello;?>
</body>
</html>

としました。titleと挨拶の文字列を展開するだけのものです。

最後にcontrollerの作成です。helloWorld/system/application/controllers/hello.phpを作成します。

<?php
class Hello extends Controller{

    function __construct(){
        parent::Controller();
    }

    function index(){
        //hello_modelの呼出し
        $this->load->model('hello_model');
        //hello_modelのhelloメソッドを呼びだして文字列を代入
        $data['hello'] = $this->hello_model->hello();
        //titleを指定
        $data['title'] = "HelloWorld of CodeIgniter on eclipse.";
        // viewを呼んで値を渡す
        $this->load->view('hello_message',$data);
    }
}
/* End of file hello.php */
/* Location: ./helloWorld/controllers/hello.php */

としました。Controllerクラスを継承したHelloクラスでコンストラクタメソッドの__construct()では親クラスControllerのコンストラクタを呼びだしています。またデフォルトで呼ばれるとのことのindex()メソッドを実装してここでモデルを呼びだし配列に文字列をセットしてviewに渡すという処理を行なっています。

これで動くはず…。確認に http://localhost/helloworld/index.php/hello/index/ へアクセスしてみると。



ばっちり動いてくれました。

ではこのhelloクラスがデフォルトで表示されるように先程のdefault_controllerにhelloクラスを指定してみましょう。

syste/application/config/routes.phpの中で

$route['default_controller'] = "hello";
$route['scaffolding_trigger'] = "";

と変更し http://localhost/helloworld/ にアクセスしてみると先程のwelcomeメッセージに代わって作成したhelloクラスが動作していることが確認できました。



いろいろとハマりどころがありそうですがなんとかHelloWorldしてみることが出来たようです。ちょっと安堵。(笑

2010年12月6日月曜日

さらにブログ開設

さて退院もして徐々にペースを上げようなどと思っている訳ではありませんが「技術系のネタを薬剤師ブログに書くのはどうなの?」ということで個人事業主も立ち上げる予定ですし技術系ブログも作ってしまいましょう!ってことでサクっとBloggerでもう一つ作成してみました。

薬剤師系ブログで医療ITのアプリ等の紹介は致しますが基本技術ネタはこちらのブログってことで行きたいと思います。

というかいったいいくつブログを持てば気が済むんでしょう?これで公開中なのは4つ目かも?(笑
まあ残り2つは100%趣味のブログなんでこういうのもアリってことにしておきましょうかね。

こちらのブログでは文字通り技術系。プログラム開発やらweb作成といったネタを扱っていく予定です。
知り得た知識の忘備録やらそういったものが中心になることと思いますが個人事業主としての宣伝等も兼てつらつらと書き連ねていこうと考えております。