2009年5月8日金曜日

CakePHP 星座/干支 ヘルパークラス (Zodiac Sign Helper Class for CakePHP)

2009年5月8日金曜日 0

Date/datetime 文字列から12星座/干支を表示するシンプルな CakePHP ヘルパークラスです。

リリース:

  • メジャーバージョンのリリース 1.0.0.0 (New!)

動作環境:

  • CakePHP 1.2 (CakePHP 1.1.x.x ではテストしてません)
  • PHP バージョン 4/5

ライセンス:

ダウンロード:

インストール:

  • ZodiacSign helper をダウンロード
  • zip ファイルを解凍して zodiac_sign.php を "app/views/helpers" に配置

使用例:

ビューで ZodiacSignHelper::name() をコールします。 引数に date か datetime のどちらかの文字列を指定します。

echo $zodiacSign->name($data['User']['birthday']);

2番目の引数を 'Chinese' にすると干支を返します。

echo $zodiacSign->name($data['User']['birthday'], 'Chinese');

戻り値は、12星座/干支とも英語の文字列 (Virgo, Leo, Sagittarius etc.) なので、必要に応じて po ファイルに翻訳文を用意しましょう。

2009年4月23日木曜日

CakePHP のアプリケーション間で User 情報をセキュアに共有する

2009年4月23日木曜日 0

前回の記事 "CakePHP でドメインの異なるサイトでセッションを共有する" で、 CakePHP のアプリケーション間でセッションのユーザ情報を共有するシンプルな方法について書きました。 CakePHP 開発チームによって、CakePHP はすでにセキュアなセッション・ハンドリングを備えていますが、なかにはリンクにセッションIDを直接付加するのは安全ではないと考える開発者の方もいるでしょう。サーチエンジンのなかにはセッションIDを付加した URL ごとインデックスしてしまうものもあると聞きます。本当に「やばい」ですネ。ソリューションとしては、ページ上のいかなるリンクにもセッションIDを付加しないことです。

まず、siteA.com と siteB.com、2つのサイトがあると想定します。 Auth Component で認証されたユーザが、 siteA.com のページ上のリンクをクリックして、セッションのユーザ情報を保持したまま siteB.com にリダイレクトすることが今回の主旨です。

まず初めに、設定することがいくつかあります。:

  • siteB.com のコアファイル (/app/config/core.php) の 'Security.level' を 'low' に設定します。
  • セッション・ハンドリング・メソッド(app/config/core.php 内の 'Session.save') を 'database' にします。
  • 両サイトとも同じ Security.salt (/app/config/core.php) を使用します。

両サイトとも同じセッションデータベースを使うところがミソです。

CakePHP のコンソール でセッションテーブルを作ります。

$ cake schema run create Sessions

SQL ファイル (app/config/sql/sessions.sql) でも作成できます。

siteA.com と siteB.com それぞれのアプリケーション内に SiteTransfer モデルを作成します。

class SiteTransfer extends AppModel {
    var $name = 'SiteTransfer';
}

基本的に、site_transfers は以下のようなテーブル構造でよいでしょう。

create table site_transfers (
 id varchar(36) not null,
 sess_id varchar(26) not null,
 primary key (id)
);

siteA.com に Users コントローラを作ります。

class UsersController extends AppController {

  var $name = 'Users';
  
  function index() {}

  function redirectem() {
    $this->autoRender = false;
    App::import('Core', 'String');
    $data['SiteTransfer']['sess_id'] = $this->Session->id();
    $this->SiteTransfer->id = String::uuid();
    if($this->SiteTransfer->save($data)) {
      $this->redirect('http://siteB.com/users/catchem?uuid='.$this->SiteTransfer->id);
    }
  }

} 

ビューの index.ctp に‘Go to siteB.com’というリンクを貼ります。

echo $html->link('Go to siteB.com', array(
  'action' => 'redirectem'
));

ここでどういう仕組みかを見ていきましょう。
siteA.com の Index ページにいる認証済みユーザがリンクをクリックすると, redirectem() アクション (このアクションにビューは必要ありません) にリダイレクトします。 この redirectem() メソッドでは, 現在のセッションID を取得して、site_transfers に UUID (例えば String::uuid を使用) で保存する処理をします。そして、/users/catchem on siteB.com にリダイレクトします。

それでは次に、siteB.comUser の Users コントローラを作成しましょう。

class UsersController extends AppController {

  var $name = 'Users';
  var $components = array('Session');

  function beforeFilter() {
    if(!empty($this->params['url']['uuid'])) {
      $uuid = $this->params['url']['uuid'];
      $data = $this->SiteTransfer->findById($uuid);
      $this->Session->id($data['SiteTransfer']['sess_id']);
      $this->SiteTransfer->del($uuid);
    }
  }
  
  function catchem() {
    $this->redirect(array('action' => 'index'));
  }

  function index() {
    pr($this->Session->read('Auth')); 
    exit; 
  }

} 

ユーザが siteB.com にリダイレクトしたとき、何が起きるのでしょう?
UUID をトークンとして site_transfers テーブルを検索します。一致したレコードからセッションを生成します。最後にセキュリティ上、site_transfers テーブルからレコード(セッションID と UUID)を削除します。

終了です。これでユーザは siteA.com と siteB.com、両サイトでセッションのユーザ情報を共有できました。

2009年3月31日火曜日

CakePHP でドメインの異なるサイトでセッションを共有する

2009年3月31日火曜日 0

これは簡単なティップですが、ドキュメントとしてここに記しておきます。ドメインの異なる CakePHP サイトでセッションを共有する方法を紹介します。

2つのドメインの異なるサイト siteA.com と siteB.com があるとします。siteA.com にいるログイン認証済みのユーザがあるリンクをクリックして siteB.com へ移動するとき、セッション情報を保持したままリダイレクトするようにします。

まず、設定する項目がいくつかあります。

  • siteB.com のコアファイル (/app/config/core.php) の 'Security.level' を 'low' に設定します。 'high' and 'medium' に設定すると、埋め込まれたセッションID は無効だと判断されます。
  • セッション・ハンドリング・メソッド(app/config/core.php 内の 'Session.save') を 'php' か 'database' にします。siteA.com と siteB.com のセッション・ハンドリング・メソッド (Session.save) を同じにする必要があります。2つのサイトが同じセッションストーレッジを使用してセッションを共有するようにします。
  • 両サイトとも同じ Security.salt (/app/config/core.php) を使用します。
siteA.com のビューで、次のようにセッションIDをリンクに埋め込みます。
echo $html->link('Go to siteB.com',
 "http://siteB.com/tests/index?sid=" . $session->id()
);
受け手側の siteB.com の任意のコントローラ、beforeFilter メソッド内で, $this->Session->id($this->params['url']['sid']) を使います。
function beforeFilter() {
  if (!empty($this->params['url']['sid'])) {
    $this->Session->id($this->params['url']['sid']);
  }
}
ユーザが siteA.com のリンクをクリックすると、siteB.com へリダイレクトして、パラメータとして渡されたセッションIDから新しいセッションIDを生成します。

2009年3月16日月曜日

AppController::beforeRender を活用して CakePHP のコントローラ属性をアサインする

2009年3月16日月曜日 0
これは好みの問題かもしれませんが、Kyo は、 ビューに渡すためのコントローラ属性を、CakePHP のコントローラー内でその都度セットするのが苦痛に感じます。なので、こんな方法をいつもとっています。 Add a $this->set into the AppController::beforeRender to always read $this->data.
class AppController extends Controller {
  function beforeRender() {
    if (!isset($this->viewVars['data'])) {
      $this->set('data', $this->data);
    }
  }
}
コントローラ(app controller を継承したコントローラ)で、 もう $this->data をセットする必要はありません。
class PostsController extends AppController {
  function index() {
    $this->data = $this->paginate();
  }
}
同様に次のようにもできます。
function beforeRender() {
  if (!isset($this->viewVars['data'])) {
    $this->set('data', $this->data);
  }
  if (!isset($this->viewVars['modelClass'])) {
    $this->set('modelClass', $this->modelClass);
  }
}
これで、どのビューからでも $data と $modelClass という変数でアクセスできるようになりました。
<? if($data): ?>
<? pr($data)?>
<? endif; ?>

<? if($modelClass): ?>
<? pr($modelClass)?>
<? endif; ?>

2009年3月10日火曜日

CakePHP 1.2 で自己参照HABTMアソシエーション (Self-Referential HABTM Relationships) をページングする

2009年3月10日火曜日 0
Cakebaker (Daniel Hofstetter) が以前書いた記事の中に、"Pagination of data from a HABTM relationship" という素晴らしい記事があります。この記事は 、HABTMアソシエーションのデータを CakePHP 1.2 を使ってどのようにページングするのかについて書かれたものです。 Daniel に触発されて、Kyo は自己参照HABTMアソシエーション (Self-Referential HABTM Relationships) をページングする方法を紹介します。 この方法は CakePHP 1.2.1.8004 でテストしましたが、それより古いバージョンでは動作しないかもしれません。 まず、 User model を用意しましょう。ある User に対して Friends をページングします。テーブル定義は次のようになります。
create table users (
  id int(11) not null auto_increment,
  username varchar(32) not null,
  primary key (id)
);

create table users_friends (
  id int(11) not null auto_increment,
  user_id int(11) not null,
  friend_id int(11) not null,
  primary key (id)
);
作成したテーブルにテストデータをインサートしましょう。
insert into users (id, username) values (1, 'kevin');
insert into users (id, username) values (2, 'stephanie');
insert into users (id, username) values (3, 'michael');
insert into users (id, username) values (4, 'jennifer');

insert into users_friends (user_id, friend_id) values (1, 2);
insert into users_friends (user_id, friend_id) values (1, 3);

insert into users_friends (user_id, friend_id) values (2, 1);
insert into users_friends (user_id, friend_id) values (2, 4);

insert into users_friends (user_id, friend_id) values (3, 1);
insert into users_friends (user_id, friend_id) values (3, 4);

insert into users_friends (user_id, friend_id) values (4, 2);
insert into users_friends (user_id, friend_id) values (4, 3);
自己参照HABTMアソシエーションを User model で設定します。
class User extends AppModel {
  var $name = 'User';
  var $hasAndBelongsToMany = array(
    'Friend' => array(
      'className' => 'User',
      'joinTable' => 'users_friends',
      'foreignKey' => 'user_id',
      'associationForeignKey' => 'friend_id',
      'unique' => true,
    )
  );
}
ユーザ “kevin” の ID から、その友達をページングします。Nate のチュートリアル "Quick Tip - Doing Ad-hoc Joins in Model::find()" を参考に、コントローラを作成します。
// app/controllers/friends_controller.php
class FriendsController extends AppController {
  function index() {
    $this->paginate = array(
      'Friend' => array(
        'limit' => 2,
        'joins' => array(
          array(
            'table' => 'users_friends',
            'alias' => 'UsersFriend',
            'type' => 'inner',
            'conditions'=> array(
              'UsersFriend.friend_id = Friend.id',
            ),
          ),
          array(
            'table' => 'users',
            'alias' => 'User',
            'type' => 'inner',
            'conditions'=> array(
              'User.id = UsersFriend.user_id',
              'User.id' => 1
            )
          )
        )
      )
    );
    $data = $this->paginate('Friend');
    debug($data);
    exit;
  }
}
デバッグアウトプットは以下の通りです。
Array
(
[0] => Array
   (
       [Friend] => Array
           (
               [id] => 2
               [username] => stephanie
               [0] => Array
                   (
                       [id] => 1
                       [username] => kevin
                       [UsersFriend] => Array
                           (
                               [id] => 3
                               [user_id] => 2
                               [friend_id] => 1
                           )
                   )
               [1] => Array
                   (
                       [id] => 4
                       [username] => jennifer
                       [UsersFriend] => Array
                           (
                               [id] => 4
                               [user_id] => 2
                               [friend_id] => 4
                           )
                   )
           )
   )
[1] => Array
   (
       [Friend] => Array
           (
               [id] => 3
               [username] => michael
               [0] => Array
                   (
                       [id] => 1
                       [username] => kevin
                       [UsersFriend] => Array
                           (
                               [id] => 5
                               [user_id] => 3
                               [friend_id] => 1
                           )
                   )
               [1] => Array
                   (
                       [id] => 4
                       [username] => jennifer
                       [UsersFriend] => Array
                           (
                               [id] => 6
                               [user_id] => 3
                               [friend_id] => 4
                           )
                   )
           )
   )
)

2009年3月7日土曜日

このブログについて

2009年3月7日土曜日 0
ウェブディベロッパー Kyo のブログ "JamNite" の日本語版ブログです。CakePHP の話題を中心に掲載していきます。基本的に、本家ブログと平行して更新いていく予定ですが、たまに内容が変わるかもしれません。最新記事は "JamNite" をご覧ください。
 
JamNite Japan ◄Design by Pocket, BlogBulk Blogger Templates