<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-2176839319791523666</id><updated>2012-03-16T18:21:35.345+09:00</updated><category term='CakePHP Snippets'/><category term='CakePHP Helpers'/><category term='このブログについて'/><category term='CakePHP'/><title type='text'>JamNite Japan</title><subtitle type='html'>&lt;a href="http://jamnite.blogspot.com/"&gt;"&lt;strong&gt;JamNite&lt;/strong&gt;(英語)"&lt;/a&gt;
の日本語版ブログです。ウェブディベロッパーの Kyo が、CakePHP の話題を中心に掲載していきます。基本的に、本家ブログと平行して更新いていく予定ですが、たまに内容が変わるかもしれません。最新記事は  "&lt;a href="http://jamnite.blogspot.com/"&gt;&lt;strong&gt;JamNite&lt;/strong&gt;&lt;/a&gt;" をご覧ください。</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://jamnite-japan.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2176839319791523666/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://jamnite-japan.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>Kyo (AKA Yasuhiro Sota)</name><uri>http://www.blogger.com/profile/07153458695920463702</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/_O5-xBj0LMAo/ScYzkNBaPyI/AAAAAAAAACU/PLn1mHaoYBE/S220/its_not_a_code_its_me.png'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>6</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-2176839319791523666.post-4832377144481550080</id><published>2009-05-08T07:01:00.013+09:00</published><updated>2009-06-05T11:58:15.764+09:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='CakePHP'/><category scheme='http://www.blogger.com/atom/ns#' term='CakePHP Helpers'/><title type='text'>CakePHP 星座/干支 ヘルパークラス (Zodiac Sign Helper Class for CakePHP)</title><content type='html'>&lt;p&gt;Date/datetime 文字列から１２星座/干支を表示するシンプルな CakePHP ヘルパークラスです。&lt;/p&gt;

&lt;h4&gt;リリース:&lt;/h4&gt;
&lt;ul&gt;&lt;li&gt;メジャーバージョンのリリース&lt;strong&gt; 1.0.0.0&lt;span style="color: rgb(255, 102, 0);"&gt; (New!)&lt;/span&gt;&lt;/strong&gt;&lt;/li&gt;&lt;/ul&gt;

&lt;h4&gt;動作環境:&lt;/h4&gt;
&lt;ul&gt;&lt;li&gt;CakePHP 1.2 (CakePHP 1.1.x.x ではテストしてません)&lt;/li&gt;&lt;li&gt;PHP バージョン 4/5&lt;/li&gt;&lt;/ul&gt;

&lt;h4&gt;ライセンス:&lt;/h4&gt;
&lt;ul&gt;&lt;li&gt;&lt;a href="http://www.opensource.org/licenses/mit-license.php"&gt;The MIT License&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;

&lt;h4&gt;ダウンロード:&lt;/h4&gt;
&lt;ul&gt;&lt;li&gt;&lt;a href="http://code.google.com/p/jamnite/"&gt;http://code.google.com/p/jamnite/&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;

&lt;h4&gt;インストール:&lt;/h4&gt;
&lt;ul&gt;&lt;li&gt;&lt;a href="http://code.google.com/p/jamnite/"&gt;ZodiacSign helper&lt;/a&gt; をダウンロード&lt;/li&gt;&lt;li&gt;zip ファイルを解凍して zodiac_sign.php を "app/views/helpers" に配置&lt;/li&gt;&lt;/ul&gt;

&lt;h4&gt;使用例:&lt;/h4&gt;

&lt;p&gt;ビューで ZodiacSignHelper::name() をコールします。 引数に date か datetime のどちらかの文字列を指定します。&lt;/p&gt;
&lt;pre class="prettyprint"&gt;
echo $zodiacSign-&gt;name($data['User']['birthday']);
&lt;/pre&gt;

&lt;p&gt;２番目の引数を 'Chinese' にすると干支を返します。&lt;/p&gt;

&lt;pre class="prettyprint"&gt;
echo $zodiacSign-&gt;name($data['User']['birthday'], 'Chinese');
&lt;/pre&gt;

&lt;p&gt;
戻り値は、１２星座/干支とも英語の文字列 (Virgo, Leo, Sagittarius etc.) なので、必要に応じて po ファイルに翻訳文を用意しましょう。
&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2176839319791523666-4832377144481550080?l=jamnite-japan.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jamnite-japan.blogspot.com/feeds/4832377144481550080/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://jamnite-japan.blogspot.com/2009/05/cakephp-zodiac-sign-helper-class-for.html#comment-form' title='0 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2176839319791523666/posts/default/4832377144481550080'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2176839319791523666/posts/default/4832377144481550080'/><link rel='alternate' type='text/html' href='http://jamnite-japan.blogspot.com/2009/05/cakephp-zodiac-sign-helper-class-for.html' title='CakePHP 星座/干支 ヘルパークラス (Zodiac Sign Helper Class for CakePHP)'/><author><name>Kyo (AKA Yasuhiro Sota)</name><uri>http://www.blogger.com/profile/07153458695920463702</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/_O5-xBj0LMAo/ScYzkNBaPyI/AAAAAAAAACU/PLn1mHaoYBE/S220/its_not_a_code_its_me.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2176839319791523666.post-6711181111179269094</id><published>2009-04-23T23:52:00.000+09:00</published><updated>2009-05-08T17:09:28.339+09:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='CakePHP Snippets'/><category scheme='http://www.blogger.com/atom/ns#' term='CakePHP'/><title type='text'>CakePHP のアプリケーション間で User 情報をセキュアに共有する</title><content type='html'>&lt;p&gt;
前回の記事 "&lt;a href="http://jamnite-japan.blogspot.com/2009/03/cakephp.html"&gt;CakePHP でドメインの異なるサイトでセッションを共有する&lt;/a&gt;" で、 CakePHP のアプリケーション間でセッションのユーザ情報を共有するシンプルな方法について書きました。 CakePHP 開発チームによって、CakePHP はすでにセキュアなセッション・ハンドリングを備えていますが、なかにはリンクにセッションIDを直接付加するのは安全ではないと考える開発者の方もいるでしょう。サーチエンジンのなかにはセッションIDを付加した URL ごとインデックスしてしまうものもあると聞きます。本当に「やばい」ですネ。ソリューションとしては、ページ上のいかなるリンクにもセッションIDを付加しないことです。
&lt;/p&gt;

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

&lt;p&gt;まず初めに、設定することがいくつかあります。:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;siteB.com のコアファイル (/app/config/core.php) の 'Security.level' を 'low' に設定します。 &lt;/li&gt;
&lt;li&gt;セッション・ハンドリング・メソッド(app/config/core.php 内の 'Session.save') を 'database' にします。&lt;/li&gt;
&lt;li&gt;両サイトとも同じ Security.salt (/app/config/core.php) を使用します。&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;CakePHP のコンソール でセッションテーブルを作ります。&lt;/p&gt;
&lt;pre class="prettyprint"&gt;
$ cake schema run create Sessions
&lt;/pre&gt;
&lt;p&gt;SQL ファイル (app/config/sql/sessions.sql) でも作成できます。 &lt;/p&gt;

&lt;p&gt;siteA.com と siteB.com　それぞれのアプリケーション内に SiteTransfer モデルを作成します。&lt;/p&gt;
&lt;pre class="prettyprint"&gt;
class SiteTransfer extends AppModel {
    var $name = 'SiteTransfer';
}
&lt;/pre&gt;
&lt;p&gt;基本的に、site_transfers は以下のようなテーブル構造でよいでしょう。&lt;/p&gt;
&lt;pre class="prettyprint"&gt;
create table site_transfers (
 id varchar(36) not null,
 sess_id varchar(26) not null,
 primary key (id)
);
&lt;/pre&gt;

&lt;p&gt;siteA.com に Users コントローラを作ります。 &lt;/p&gt;
&lt;pre class="prettyprint"&gt;
class UsersController extends AppController {

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

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

} 
&lt;/pre&gt;
&lt;p&gt;ビューの index.ctp に‘Go to siteB.com’というリンクを貼ります。&lt;/p&gt;
&lt;pre class="prettyprint"&gt;
echo $html-&gt;link('Go to siteB.com', array(
  'action' =&gt; 'redirectem'
));
&lt;/pre&gt;
&lt;p&gt;
ここでどういう仕組みかを見ていきましょう。&lt;br /&gt;
siteA.com の Index ページにいる認証済みユーザがリンクをクリックすると, redirectem() アクション (このアクションにビューは必要ありません) にリダイレクトします。 この redirectem() メソッドでは, 現在のセッションID を取得して、site_transfers に UUID (例えば String::uuid を使用) で保存する処理をします。そして、/users/catchem on siteB.com にリダイレクトします。
&lt;/p&gt;

&lt;p&gt;それでは次に、siteB.comUser の Users コントローラを作成しましょう。&lt;/p&gt;
&lt;pre class="prettyprint"&gt;
class UsersController extends AppController {

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

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

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

} 
&lt;/pre&gt;

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

&lt;p&gt;終了です。これでユーザは siteA.com と siteB.com、両サイトでセッションのユーザ情報を共有できました。&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2176839319791523666-6711181111179269094?l=jamnite-japan.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jamnite-japan.blogspot.com/feeds/6711181111179269094/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://jamnite-japan.blogspot.com/2009/04/cakephp-user.html#comment-form' title='0 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2176839319791523666/posts/default/6711181111179269094'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2176839319791523666/posts/default/6711181111179269094'/><link rel='alternate' type='text/html' href='http://jamnite-japan.blogspot.com/2009/04/cakephp-user.html' title='CakePHP のアプリケーション間で User 情報をセキュアに共有する'/><author><name>Kyo (AKA Yasuhiro Sota)</name><uri>http://www.blogger.com/profile/07153458695920463702</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/_O5-xBj0LMAo/ScYzkNBaPyI/AAAAAAAAACU/PLn1mHaoYBE/S220/its_not_a_code_its_me.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2176839319791523666.post-6791230534808466176</id><published>2009-03-31T01:45:00.006+09:00</published><updated>2009-05-08T17:07:40.972+09:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='CakePHP Snippets'/><category scheme='http://www.blogger.com/atom/ns#' term='CakePHP'/><title type='text'>CakePHP でドメインの異なるサイトでセッションを共有する</title><content type='html'>&lt;p&gt;
これは簡単なティップですが、ドキュメントとしてここに記しておきます。ドメインの異なる CakePHP サイトでセッションを共有する方法を紹介します。
&lt;/p&gt;

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

&lt;p&gt;まず、設定する項目がいくつかあります。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
siteB.com のコアファイル (/app/config/core.php) の 'Security.level' を 'low' に設定します。
'high' and 'medium' に設定すると、埋め込まれたセッションID は無効だと判断されます。
&lt;/li&gt;
&lt;li&gt;セッション・ハンドリング・メソッド(app/config/core.php 内の 'Session.save') を 'php' か 'database' にします。siteA.com と siteB.com のセッション・ハンドリング・メソッド (Session.save) を同じにする必要があります。２つのサイトが同じセッションストーレッジを使用してセッションを共有するようにします。
&lt;/li&gt;
&lt;li&gt;
両サイトとも同じ Security.salt (/app/config/core.php) を使用します。
&lt;/li&gt;
&lt;/ul&gt;

siteA.com のビューで、次のようにセッションIDをリンクに埋め込みます。
&lt;pre class="prettyprint"&gt;
echo $html-&gt;link('Go to siteB.com',
 "http://siteB.com/tests/index?sid=" . $session-&gt;id()
);
&lt;/pre&gt;
受け手側の siteB.com の任意のコントローラ、beforeFilter メソッド内で, $this-&gt;Session-&gt;id($this-&gt;params['url']['sid']) を使います。
&lt;pre class="prettyprint"&gt;
function beforeFilter() {
  if (!empty($this-&gt;params['url']['sid'])) {
    $this-&gt;Session-&gt;id($this-&gt;params['url']['sid']);
  }
}
&lt;/pre&gt;
ユーザが siteA.com のリンクをクリックすると、siteB.com へリダイレクトして、パラメータとして渡されたセッションIDから新しいセッションIDを生成します。&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2176839319791523666-6791230534808466176?l=jamnite-japan.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jamnite-japan.blogspot.com/feeds/6791230534808466176/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://jamnite-japan.blogspot.com/2009/03/cakephp.html#comment-form' title='0 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2176839319791523666/posts/default/6791230534808466176'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2176839319791523666/posts/default/6791230534808466176'/><link rel='alternate' type='text/html' href='http://jamnite-japan.blogspot.com/2009/03/cakephp.html' title='CakePHP でドメインの異なるサイトでセッションを共有する'/><author><name>Kyo (AKA Yasuhiro Sota)</name><uri>http://www.blogger.com/profile/07153458695920463702</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/_O5-xBj0LMAo/ScYzkNBaPyI/AAAAAAAAACU/PLn1mHaoYBE/S220/its_not_a_code_its_me.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2176839319791523666.post-4666491220335712908</id><published>2009-03-16T01:59:00.002+09:00</published><updated>2009-05-08T17:07:22.736+09:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='CakePHP Snippets'/><category scheme='http://www.blogger.com/atom/ns#' term='CakePHP'/><title type='text'>AppController::beforeRender を活用して CakePHP のコントローラ属性をアサインする</title><content type='html'>これは好みの問題かもしれませんが、Kyo は、 ビューに渡すためのコントローラ属性を、CakePHP のコントローラー内でその都度セットするのが苦痛に感じます。なので、こんな方法をいつもとっています。

Add a $this-&gt;set into the AppController::beforeRender to always read $this-&gt;data.
&lt;pre class="prettyprint"&gt;
class AppController extends Controller {
  function beforeRender() {
    if (!isset($this-&gt;viewVars['data'])) {
      $this-&gt;set('data', $this-&gt;data);
    }
  }
}
&lt;/pre&gt;
コントローラ（app controller を継承したコントローラ)で、 もう $this-&gt;data をセットする必要はありません。
&lt;pre class="prettyprint"&gt;
class PostsController extends AppController {
  function index() {
    $this-&gt;data = $this-&gt;paginate();
  }
}
&lt;/pre&gt;
同様に次のようにもできます。
&lt;pre class="prettyprint"&gt;
function beforeRender() {
  if (!isset($this-&gt;viewVars['data'])) {
    $this-&gt;set('data', $this-&gt;data);
  }
  if (!isset($this-&gt;viewVars['modelClass'])) {
    $this-&gt;set('modelClass', $this-&gt;modelClass);
  }
}
&lt;/pre&gt;
これで、どのビューからでも $data と $modelClass という変数でアクセスできるようになりました。
&lt;pre class="prettyprint"&gt;
&amp;lt;? if($data): ?&amp;gt;
&amp;lt;? pr($data)?&amp;gt;
&amp;lt;? endif; ?&gt;

&amp;lt;? if($modelClass): ?&amp;gt;
&amp;lt;? pr($modelClass)?&amp;gt;
&amp;lt;? endif; ?&gt;
&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2176839319791523666-4666491220335712908?l=jamnite-japan.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jamnite-japan.blogspot.com/feeds/4666491220335712908/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://jamnite-japan.blogspot.com/2009/03/utilizing-appcontrollerbeforerender-to.html#comment-form' title='0 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2176839319791523666/posts/default/4666491220335712908'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2176839319791523666/posts/default/4666491220335712908'/><link rel='alternate' type='text/html' href='http://jamnite-japan.blogspot.com/2009/03/utilizing-appcontrollerbeforerender-to.html' title='AppController::beforeRender を活用して CakePHP のコントローラ属性をアサインする'/><author><name>Kyo (AKA Yasuhiro Sota)</name><uri>http://www.blogger.com/profile/07153458695920463702</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/_O5-xBj0LMAo/ScYzkNBaPyI/AAAAAAAAACU/PLn1mHaoYBE/S220/its_not_a_code_its_me.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2176839319791523666.post-6391313478089789346</id><published>2009-03-10T01:08:00.002+09:00</published><updated>2009-05-08T17:07:06.849+09:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='CakePHP Snippets'/><category scheme='http://www.blogger.com/atom/ns#' term='CakePHP'/><title type='text'>CakePHP 1.2 で自己参照HABTMアソシエーション (Self-Referential HABTM Relationships) をページングする</title><content type='html'>Cakebaker (Daniel Hofstetter) が以前書いた記事の中に、"&lt;a href="http://cakebaker.42dh.com/2007/10/17/pagination-of-data-from-a-habtm-relationship/" target="_new"&gt;Pagination of data from a HABTM relationship&lt;/a&gt;" という素晴らしい記事があります。この記事は 、HABTMアソシエーションのデータを CakePHP 1.2 を使ってどのようにページングするのかについて書かれたものです。 Daniel  に触発されて、Kyo は自己参照HABTMアソシエーション (Self-Referential HABTM Relationships) をページングする方法を紹介します。 この方法は CakePHP 1.2.1.8004  でテストしましたが、それより古いバージョンでは動作しないかもしれません。

まず、 User model  を用意しましょう。ある  User に対して Friends  をページングします。テーブル定義は次のようになります。
&lt;pre class="prettyprint"&gt;
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)
);
&lt;/pre&gt;
作成したテーブルにテストデータをインサートしましょう。
&lt;pre class="prettyprint"&gt;
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);
&lt;/pre&gt;
自己参照HABTMアソシエーションを  User model で設定します。
&lt;pre class="prettyprint"&gt;
class User extends AppModel {
  var $name = 'User';
  var $hasAndBelongsToMany = array(
    'Friend' =&gt; array(
      'className' =&gt; 'User',
      'joinTable' =&gt; 'users_friends',
      'foreignKey' =&gt; 'user_id',
      'associationForeignKey' =&gt; 'friend_id',
      'unique' =&gt; true,
    )
  );
}
&lt;/pre&gt;
ユーザ “kevin” の ID から、その友達をページングします。Nate のチュートリアル "&lt;a href="http://bakery.cakephp.org/articles/view/quick-tip-doing-ad-hoc-joins-in-model-find" target="_new"&gt;Quick Tip - Doing Ad-hoc Joins in Model::find()&lt;/a&gt;" を参考に、コントローラを作成します。
&lt;pre class="prettyprint"&gt;
// app/controllers/friends_controller.php
class FriendsController extends AppController {
  function index() {
    $this-&gt;paginate = array(
      'Friend' =&gt; array(
        'limit' =&gt; 2,
        'joins' =&gt; array(
          array(
            'table' =&gt; 'users_friends',
            'alias' =&gt; 'UsersFriend',
            'type' =&gt; 'inner',
            'conditions'=&gt; array(
              'UsersFriend.friend_id = Friend.id',
            ),
          ),
          array(
            'table' =&gt; 'users',
            'alias' =&gt; 'User',
            'type' =&gt; 'inner',
            'conditions'=&gt; array(
              'User.id = UsersFriend.user_id',
              'User.id' =&gt; 1
            )
          )
        )
      )
    );
    $data = $this-&gt;paginate('Friend');
    debug($data);
    exit;
  }
}
&lt;/pre&gt;
デバッグアウトプットは以下の通りです。
&lt;pre class="prettyprint"&gt;
Array
(
[0] =&gt; Array
   (
       [Friend] =&gt; Array
           (
               [id] =&gt; 2
               [username] =&gt; stephanie
               [0] =&gt; Array
                   (
                       [id] =&gt; 1
                       [username] =&gt; kevin
                       [UsersFriend] =&gt; Array
                           (
                               [id] =&gt; 3
                               [user_id] =&gt; 2
                               [friend_id] =&gt; 1
                           )
                   )
               [1] =&gt; Array
                   (
                       [id] =&gt; 4
                       [username] =&gt; jennifer
                       [UsersFriend] =&gt; Array
                           (
                               [id] =&gt; 4
                               [user_id] =&gt; 2
                               [friend_id] =&gt; 4
                           )
                   )
           )
   )
[1] =&gt; Array
   (
       [Friend] =&gt; Array
           (
               [id] =&gt; 3
               [username] =&gt; michael
               [0] =&gt; Array
                   (
                       [id] =&gt; 1
                       [username] =&gt; kevin
                       [UsersFriend] =&gt; Array
                           (
                               [id] =&gt; 5
                               [user_id] =&gt; 3
                               [friend_id] =&gt; 1
                           )
                   )
               [1] =&gt; Array
                   (
                       [id] =&gt; 4
                       [username] =&gt; jennifer
                       [UsersFriend] =&gt; Array
                           (
                               [id] =&gt; 6
                               [user_id] =&gt; 3
                               [friend_id] =&gt; 4
                           )
                   )
           )
   )
)
&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2176839319791523666-6391313478089789346?l=jamnite-japan.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jamnite-japan.blogspot.com/feeds/6391313478089789346/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://jamnite-japan.blogspot.com/2009/03/how-to-paginate-self-referential-habtm.html#comment-form' title='0 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2176839319791523666/posts/default/6391313478089789346'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2176839319791523666/posts/default/6391313478089789346'/><link rel='alternate' type='text/html' href='http://jamnite-japan.blogspot.com/2009/03/how-to-paginate-self-referential-habtm.html' title='CakePHP 1.2 で自己参照HABTMアソシエーション (Self-Referential HABTM Relationships) をページングする'/><author><name>Kyo (AKA Yasuhiro Sota)</name><uri>http://www.blogger.com/profile/07153458695920463702</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/_O5-xBj0LMAo/ScYzkNBaPyI/AAAAAAAAACU/PLn1mHaoYBE/S220/its_not_a_code_its_me.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2176839319791523666.post-2426976129845695284</id><published>2009-03-07T00:51:00.003+09:00</published><updated>2009-04-29T18:28:07.884+09:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='このブログについて'/><title type='text'>このブログについて</title><content type='html'>ウェブディベロッパー Kyo のブログ "JamNite" の日本語版ブログです。CakePHP の話題を中心に掲載していきます。基本的に、本家ブログと平行して更新いていく予定ですが、たまに内容が変わるかもしれません。最新記事は  "&lt;a href="http://jamnite.blogspot.com/"&gt;JamNite&lt;/a&gt;" をご覧ください。&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2176839319791523666-2426976129845695284?l=jamnite-japan.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jamnite-japan.blogspot.com/feeds/2426976129845695284/comments/default' title='コメントの投稿'/><link rel='replies' type='text/html' href='http://jamnite-japan.blogspot.com/2009/03/about-this-channel.html#comment-form' title='0 件のコメント'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2176839319791523666/posts/default/2426976129845695284'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2176839319791523666/posts/default/2426976129845695284'/><link rel='alternate' type='text/html' href='http://jamnite-japan.blogspot.com/2009/03/about-this-channel.html' title='このブログについて'/><author><name>Kyo (AKA Yasuhiro Sota)</name><uri>http://www.blogger.com/profile/07153458695920463702</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/_O5-xBj0LMAo/ScYzkNBaPyI/AAAAAAAAACU/PLn1mHaoYBE/S220/its_not_a_code_its_me.png'/></author><thr:total>0</thr:total></entry></feed>
