2011/12/11 追記
当初、ソース内にValidation::factory()と記述をしていましたが、近々廃止予定とのことです。
Validation::forge()を使うべきとご指摘を頂きました。有難うございます。
当記事の該当箇所について、修正してあります。
FuelPHP Advent Calendar 2011 11日目です。
@madmamor です。
前日の
@kenji_s さんの
FuelPHP での Migration の使い方
でもご紹介頂いたとおり、今回は、Facebookアプリの実装編です。
尚、先にこちらをお読み下さるよう、お願いします。
FuelPHPでFacebookアプリを作ってみよう。準備編。
今回実装する機能は
* Facebookでのログイン/ログアウト機能
* Facebook情報のDB保存(バリデーション付き)
* Facebookのウォール投稿機能(バリデーション付き)
です。
各項にポイントを書いています。
では、一気にいきましょう。
【DBとテーブルを作成】
MySQLを使います。DB名はfuelfb。テーブル名はusers。とします。
尚、今回、Facebook情報はusersテーブルに保存するのみです。
検索して使用する。という処理は組み込みませんが、DB周りの機能紹介を兼ねて、記載します。
create database fuelfb;
create table fuelfb.users
(
id int not null auto_increment,
created_at int not null,
updated_at int not null,
facebook_id varchar(255) not null unique,
facebook_name varchar(255) not null,
facebook_link varchar(255) not null,
primary key (id)
);
【ポイント】
idはFuelPHPが定める主キーカラム名です。
created_atはFuelPHPが定めるレコード作成時刻カラム名です。
updated_atはFuelPHPが定めるレコード更新時刻カラム名です。
created_atとupdated_atはデフォルトだとUNIXタイムスタンプとなります。
datetime型にすることも可能ですが、今回はint(UNIXタイムスタンプ)で。
その他のカラムは、Facebook認証時に取得するデータ用のカラムです。
ユーザ入力ではありませんが、外部データなので、制約は緩めにしてあります。
【ORMモデルを作成】
前述のusersテーブルに対するモデルです。
fuel/app/classes/model/user.phpを作成。
<?php
namespace Model;
class User extends \Orm\Model
{
protected static $_observers = array(
'Orm\Observer_CreatedAt' => array('events'=>array('before_insert')),
'Orm\Observer_UpdatedAt' => array('events'=>array('before_save')),
'Orm\Observer_Validation'=> array('events'=>array('before_save')),
);
protected static $_properties = array(
'id',
'created_at',
'updated_at',
'facebook_id' => array(
'validation' => array(
'trim',
'required',
'max_length' => array(255),
'valid_string' => array('integer'),
),
),
'facebook_name' => array(
'validation' => array(
'trim',
'required',
'max_length' => array(255),
),
),
'facebook_link' => array(
'validation' => array(
'trim',
'required',
'max_length' => array(255),
),
),
);
}
【ポイント】
今回は、Orm\Modelを継承したモデルとします。
$_observersで、様々な振る舞いを指定可能です。
今回は、created_at値、updated_at値の自動登録と、
insert or update前のバリデーション自動実行。を設定しています。
$_propertiesでカラム毎にバリデーションを定義可能です。
バリデーションは、"単一のStringを受け、Stringを返却するPHP関数"を指定可能です。
今回は、trimを設定しています。
$_propertiesには全てのカラムを記述して下さい。エラーとなります。
【ORMパッケージの有効化】
fuel/app/config/config.phpを修正
'packages' => array(
//'orm',
),
↓
'packages' => array(
'orm',
),
【ポイント】
前述のORMモデルが使用可能となります。
【DB接続情報を設定】
fuel/app/config/development/db.phpを修正
<?php
/**
* The development database settings.
*/
return array(
'default' => array(
'connection' => array(
'dsn' => 'mysql:host=localhost;dbname=fuel_dev',
'username' => 'root',
'password' => 'root',
),
),
);
↓
<?php
/**
* The development database settings.
*/
return array(
'default' => array(
'type' => 'mysql',
'connection' => array(
'hostname' => 'localhost',
'port' => '3306',
'database' => 'fuelfb',
'username' => 'root',
'password' => '',
),
),
);
【ポイント】
必要に応じて、接続情報は書き換えて下さい。
【ビューの修正】
fuel/app/views/index/index.phpを修正
<p>Index</p>
<?php if ($is_login): ?>
<a href="http://127.0.0.1/fuelfb/public/index/logout">Log out.</a>
<?php else: ?>
<a href="http://127.0.0.1/fuelfb/public/index/login">Login with Facebook.</a>
<?php endif; ?>
↓
<?php if ($is_login): ?>
<?php echo(Form::open('index/index/'))?>
<?php echo(Form::textarea('message'))?>
<?php echo(Form::submit('submit','Post to Facebook'))?>
<?php echo(Form::close())?>
<a href="http://127.0.0.1/fuelfb/public/index/logout">Log out.</a>
<?php else: ?>
<a href="http://127.0.0.1/fuelfb/public/index/login">Login with Facebook.</a>
<?php endif; ?>
【ポイント】
ログインしていれば入力フォームとログアウトリンクを表示します。
ログインしていなければログインリンクを表示します。
判断基準の$is_loginは後述のコントローラでセットします。
【コントローラに機能を実装】
fuel/app/classes/controller/index.phpを修正
<?php
require_once APPPATH.'vendor/facebook-php-sdk/src/facebook.php';
class Controller_Index extends Controller_Template {
private $fb;
public function before()
{
parent::before();
$this->fb = new Facebook(Config::get('facebook.init'));
}
public function action_index()
{
$this->template->title = 'Index » Index';
$data = array(
'is_login' => $this->fb->getUser()?true:false,
);
$this->template->content = View::forge('index/index',$data);
}
public function action_login()
{
exit('TODO : login');
}
public function action_callback()
{
exit('TODO : callback');
}
public function action_logout()
{
exit('TODO : logout');
}
}
↓
<?php
require_once APPPATH.'vendor/facebook-php-sdk/src/facebook.php';
use Model\User;
class Controller_Index extends Controller_Template {
private $fb;
public function before()
{
parent::before();
$this->fb = new Facebook(Config::get('facebook.init'));
}
public function action_index()
{
$this->template->title = 'Index » Index';
$is_login = $this->fb->getUser()?true:false;
$data = array(
'is_login' => $is_login,
);
if($is_login and Input::method() == 'POST')
{
$v = Validation::forge();
$v->add('message', 'message')->add_rule('required');
if(!$v->run())
{
Session::set_flash('notice', $v->errors('message')->get_message());
}
else
{
$message = $v->validated('message');
try
{
$res = $this->fb->api(array(
'method' => 'stream.publish',
'message' => $message,
));
Session::set_flash('notice', 'complete!!');
}
catch (FacebookApiException $e)
{
Session::set_flash('notice', $e->getMessage());
}
Response::redirect('index/index/');
}
}
$this->template->content = View::forge('index/index',$data);
}
public function action_login()
{
$url = $this->fb->getLoginUrl(Config::get('facebook.login'));
Response::redirect($url);
}
public function action_callback()
{
try
{
$me = $this->fb->api('/me');
$user = User::find_by_facebook_id($me['id']);
if(!$user) $user = new User;
$user->facebook_id = $me['id'];
$user->facebook_name = $me['name'];
$user->facebook_link = $me['link'];
$user->save();
Response::redirect('/index/index/');
}
catch (Orm\ValidationFailed $e)
{
throw new Exception($e->getMessage());
}
catch (FacebookApiException $e)
{
throw new Exception($e->getMessage());
}
}
public function action_logout()
{
$url = $this->fb->getLogoutUrl(Config::get('facebook.logout'));
$this->fb->destroySession();
Response::redirect($url);
}
}
【ポイント】
以下の順を想定して、各メソッドのポイントを記載します。
(1) http://127.0.0.1/fuelfb/public/index/index/ にアクセス。
(2) "Login with Facebook."をクリック。
(3) Facebook認証をして http://127.0.0.1/fuelfb/public/index/index/ に戻ってくる。
(4) Facebookのウォールへ投稿。
(5) ログアウト。
■before()
Facebookオブジェクト($fb)の初期化。全アクションに対しての、事前処理みたいな感じです。
■action_login()
Facebook認証画面にリダイレクトします。
fuel/app/config/custom.phpのfacebook.loginには、
認証後のリダイレクト先とパーミッション設定が設定してあるはずです。
■action_callback()
Facebook認証後のリダイレクト先になります。
$me = $this->fb->api('/me');
自身のFacebook情報を取得しています。
$user = User::find_by_facebook_id($me['id']);
usersテーブルに対してFacebookのIDで検索を行なっています。
find_by_[カラム名]はFuelPHPのORMが標準に提供する検索機能です。
$user->save();
前述のfind_by_facebook_idでデータがヒットしなければinsert。
ヒットすればupdateを行います。
Response::redirect('/index/index/');
action_index()へリダイレクトします。
catch (Orm\ValidationFailed $e)
ORMモデルにバリデーションルールを書きましたが、それに反する場合、ここに入ります。
今回はユーザ入力データでは無いので、実質発生しないはずです。
なので、画面表示はしていません。
catch (FacebookApiException $e)
$this->fb->api('/me'); でエラーが発生した場合、ここに入ります。
その他、FacebookのAPIでエラーが発生した場合も、FacebookApiExceptionが発生します。
■action_logout()
Facebookのセッションを破棄してリダイレクトします。
リダイレクト先はfuel/app/config/custom.phpのfacebook.logout.nextに書いてあるはずです。
■action_index()
if($is_login and Input::method() == 'POST')
フォームに入力してsubmitボタンを押すと、この中に入ります。
$v = Validation::forge();
$v->add('message', 'message')->add_rule('required');
if(!$v->run())
{
Session::set_flash('notice', $v->errors('message')->get_message());
}
必須チェックを行なっています。
入力が空だった場合、エラーメッセージを表示します。
具体的には、fuel/app/views/template.phpの
Session::get_flash('notice')
の箇所で表示されます。
$message = $v->validated('message');
try
{
$res = $this->fb->api(array(
'method' => 'stream.publish',
'message' => $message,
));
Session::set_flash('notice', 'complete!!');
}
catch (FacebookApiException $e)
{
Session::set_flash('notice', $e->getMessage());
}
Response::redirect('index/index/');
バリデーション完了後のデータを取得して、Facebookのウォールに投稿(stream.publish)します。
FacebookApiExceptionについてはaction_callback()に記載した通りとなります。
最後にリダイレクトしているのは、リロード対策です。
■その他
再現性が乏しいのですが、Facebookの証明書エラーが発生するケースがありました。
その場合、ブラウザの関連するキャッシュをクリアして下さい。
また、XAMPPのApacheを再起動してみて下さい。
これで、私の場合、エラーが発生しなくなりました。
以下、画面キャプチャを掲載しておきます。
まずは、この画面が表示されるはずです。
Login with Facebook.をクリックして、Facebook側に飛びます。
Facebook側の認証画面です。
認証が済むと、今回作成したプログラム側に戻ります。
正しく実装されていると、この画面に戻るはずです。
textareaとpostボタンが表示されます。
postが正常に行われると、"complete!!"と表示されます。
この後、ログアウトをして、最初の画面に戻れば、一通りOKと思います。
以上、実装編でした。
fuel/app/classes/welcome.php等のゴミファイルが残っていたり
fuel/app/config/config.phpのbase_urlが空だったり
その他、色々とやり残したことはありますが、ミニマムな手順は、こんな感じと思います。
有難うございました。
明日の12日目は
@mataga さんです。
FuelPHP動作実験 - 実験くんソースをModulesに閉じ込めてモジュール分割してみる。
P.S.
次回は、軽めのネタでいきます。。。