WHAT'S NEW?
Loading...

CakePHP2.0にSearch Pluginをいれてラクラク検索しよう【1/2】

■CakePHPの2.0ってところがミソ

昨年は『CakePHP1.3にSearch Pluginをいれてラクラク検索しよう』という記事を、2回にわけてお伝えしたんだけど、まぁ別にいちいち見なおさないで良いよ。

でもリンク貼っとくから、気になって眠れない人や、全国の俺ファンの女子高生は、ここからすすむことができるんだ。夜も安心。


あくまで個人的な感覚でしか無いけど、俺的には、もはや時代は2.0っぽさが満開だと思うわけよ。こら。

とは言っても決してWeb2.0ではない。Webなんかどんなバージョニングしているのか謎すぎて、屁で空飛べるくらいどうでもいい。

何が2.0かというと、タイトル見ればいーじゃん!
そう、俺達の大好きな、あのCakePHPの2.0だよ!ブタ野郎!!!



というわけで、最近CakePHP2.0ばっかりいじっている俺が、つい最近まで書いていたCakePHP1.3の記事を、CakePHP2.0でやってみようって話。その1回目。よかったね。

使ってるバージョンはCakePHP2.0.5。このバージョンで、CakeDCのSearchプラグインを使おうぜって話だ。


早速行ってみよう。

■CakePHPをデプロイしておく

動くようにしておいていただきたい。
ブラウザで見れればいいって意味で。

CakePHP2.0はここから落とせるよ。
俺が落としたバージョンは2.0.5だ。

http://cakephp.jp/

■Searchプラグインをインストール

まずはダウンロードだ。
ダウンロード元はこちらのGithub。

https://github.com/CakeDC/Search

前回もGithubだったから、前回試してみた人は安心してダウンロードすることができるけど、たかだかダウンロードで安心もクソもねぇでやんす。

とっとと落とすと良いよ。

でも1個だけ注意が必要かもね。
ページの右側にこんなのがある。


ここが2.0になってないと、もしかしたら君は1.3用のSearchプラグインを落としてしまうかもしれないんだ。

だからこんな感じに変えておくと、寝覚め良いよ。


そもそもバージョンは2.0で説明しているのに、使ってるプラグインのバージョンが1.3だから動かないとか、エラーがでるとか、そんな話は里のおふくろにでもメールして、返事が来るまで風呂にでも入ってろといいたいけど、俺もうっかりさんの代表みたいなもんだし、よくやるパターンなので、このように親切に書いておいたから、迂闊でやんちゃな君でももう大丈夫だよね。

2.0はいろいろなフォルダが大文字から始まるCamelCaseになっているので、以下のような大文字から始まるフォルダの階層構造になっていればもう、大丈夫。


当然、『Current branch』がmasterなのに、ちゃんとフォルダが大文字から始まっていたなら、それはそれで素直にダウンロードすればいいと思うよ。

今回はこんなファイル名でダウンロードできた。

CakeDC-search-1.1-20-g98da0a6.zip

ダウンロードしたzipファイルは解凍しよう。
別に解凍しなくてもいいよ。先に進まないだけだから。

解凍したら、今回はこんな感じになっていた。
.
├── Controller
│   └── Component
│       └── PrgComponent.php
├── Locale
│   ├── deu
│   │   └── LC_MESSAGES
│   │       └── search.po
│   ├── fre
│   │   └── LC_MESSAGES
│   │       └── search.po
│   ├── por
│   │   └── LC_MESSAGES
│   │       └── search.po
│   ├── rus
│   │   └── LC_MESSAGES
│   │       └── search.po
│   ├── search.pot
│   └── spa
│       └── LC_MESSAGES
│           └── search.po
├── Model
│   └── Behavior
│       └── SearchableBehavior.php
├── Test
│   ├── Case
│   │   ├── AllSearchPluginTest.php
│   │   ├── Controller
│   │   │   └── Component
│   │   │       └── PrgComponentTest.php
│   │   └── Model
│   │       └── Behavior
│   │           └── SearchableBehaviorTest.php
│   └── Fixture
│       ├── ArticleFixture.php
│       ├── PostFixture.php
│       ├── TagFixture.php
│       └── TaggedFixture.php
├── license.txt
└── readme.md

いちいち中身は全部調べないで良いとはおもうけど、前回書いておいたから今回も書いてみた。ただtreeコマンド使いたかっただけという話もある。

ではインストールしよう。

フォルダ名を『CakeDC-search-98da0a6』から『Search』に変更し、CakePHPの『app/Plugin』の中にフォルダごと入れる。インストールはこれで完了だ。

■モデルをつくろう

今回も、UserモデルとProfileモデルで、モデルをまたいだ検索をページネーションで再現してみようと思う。

要するに、検索結果を維持したままページネーションでページをジャンプするということだ。
1.3と2.0では結構違うので、そこんとこよろしく。

まずはモデルだが、以下の構造でテーブルを作っておくと良い。
CREATE TABLE IF NOT EXISTS `profiles` (
  `id` int(5) NOT NULL AUTO_INCREMENT,
  `user_id` int(5) NOT NULL,
  `nickname` varchar(32) NOT NULL,
  `gender` tinyint(1) NOT NULL,
  `birthday` date NOT NULL,
  `created` datetime NOT NULL,
  `modified` datetime NOT NULL,
  PRIMARY KEY (`id`),
  KEY `user_id` (`user_id`,`nickname`,`gender`,`birthday`),
  KEY `modified` (`modified`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8;

CREATE TABLE IF NOT EXISTS `users` (
  `id` int(5) NOT NULL AUTO_INCREMENT,
  `username` varchar(64) NOT NULL,
  `password` varchar(64) NOT NULL,
  `created` datetime NOT NULL,
  `modified` datetime NOT NULL,
  PRIMARY KEY (`id`),
  KEY `username` (`username`,`password`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8;

データは以下のSQLを使うと楽だよ。
INSERT INTO `profiles` (`id`, `user_id`, `nickname`, `gender`, `birthday`, `created`, `modified`) VALUES
(1, 1, 'マッキー太郎', 1, '1990-10-01', '2011-11-03 18:44:32', '2011-11-03 18:44:32'),
(2, 2, '倍アグラン', 1, '1980-05-04', '2011-11-03 18:44:32', '2011-11-03 18:44:32'),
(3, 3, 'エキサイト多恵子', 2, '1992-12-08', '2011-11-03 18:44:32', '2011-11-03 18:44:32'),
(4, 4, '史彦パインステート', 1, '1965-11-24', '2011-11-03 18:44:32', '2011-11-03 18:44:32'),
(5, 5, 'Yas-Kaz', 1, '1984-09-21', '2011-11-03 18:44:32', '2011-11-03 18:44:32'),
(6, 6, 'ウッキー氏田', 2, '1999-08-08', '2011-11-03 18:44:32', '2011-11-03 18:44:32'),
(7, 7, 'リック', 2, '1996-03-30', '2011-11-03 18:44:32', '2011-11-03 18:44:32'),
(8, 8, 'puripuri-chan', 2, '1984-12-15', '2011-11-03 18:44:32', '2011-11-03 18:44:32'),
(9, 9, 'チョーサンピル', 1, '1989-05-14', '2011-11-03 18:44:32', '2011-11-03 18:44:32'),
(10, 10, '道端御三郎', 1, '1963-01-24', '2011-11-03 18:44:32', '2011-11-03 18:44:32');

INSERT INTO `users` (`id`, `username`, `password`, `created`, `modified`) VALUES
(1, 'taroh@asdf.com', '0dedc1e879a73184aaaf574e95436e80d95bf563', '2011-11-03 18:44:32', '2011-11-03 18:44:32'),
(2, 'jiro@qwer.net', '0dedc1e879a73184aaaf574e95436e80d95bf563', '2011-11-03 18:44:32', '2011-11-03 18:44:32'),
(3, 'yamada.haruko@example.co.jp', '0dedc1e879a73184aaaf574e95436e80d95bf563', '2011-11-03 18:44:32', '2011-11-03 18:44:32'),
(4, 'jimmy@ledzeppelin.com', '0dedc1e879a73184aaaf574e95436e80d95bf563', '2011-11-03 18:44:32', '2011-11-03 18:44:32'),
(5, 'james@metallica.co.jp', '0dedc1e879a73184aaaf574e95436e80d95bf563', '2011-11-03 18:44:32', '2011-11-03 18:44:32'),
(6, 'saburo@zcxv.tv', '0dedc1e879a73184aaaf574e95436e80d95bf563', '2011-11-03 18:44:32', '2011-11-03 18:44:32'),
(7, 'sonofabitch@fxxkyou.com', '0dedc1e879a73184aaaf574e95436e80d95bf563', '2011-11-03 18:44:32', '2011-11-03 18:44:32'),
(8, 'youaremydestiny@not.com', '0dedc1e879a73184aaaf574e95436e80d95bf563', '2011-11-03 18:44:32', '2011-11-03 18:44:32'),
(9, 'titty.twister@fromdusktilldown.com', '0dedc1e879a73184aaaf574e95436e80d95bf563', '2011-11-03 18:44:32', '2011-11-03 18:44:32'),
(10, 'myfriends@was.gone.com', '0dedc1e879a73184aaaf574e95436e80d95bf563', '2011-11-03 18:44:32', '2011-11-03 18:44:32');

データベースにテーブルを作ったら、今度はモデルファイルだ。

app/Model/User.php
class User extends AppModel {
  public $name = 'User';

  // 検索プラグイン
  public $actsAs = array('Search.Searchable');

  // アソシエーション
  public $hasOne = array(
    'Profile' => array(
      'className'  => 'Profile',
      'foreignKey' => 'user_id',
      'conditions' => null,
      'fields'     => null,
      'dependent'  => true,
    ),
  );
}

今回使うプラグインを、actsAsプロパティにセットしておく。
アソシエーションはUserに対して1つのProfileを持たせるので、hasOneとしておいた。
ここで躓く人はいないと思うので、次に行くよ。

■コントローラをつくろう

次はコントローラだ。


app/Controller/UsersController.php
class UsersController extends AppController {

  public $name = 'Users';
  public $uses = array('User', 'Profile');
  
  public function index()
  {
    $this->set('users', $this->User->find('all'));
  }  

}

ファイル名に気をつけるんだ。2.0からは、クラス定義されたファイルのファイル名は大文字から指定するというルールになっている。

オブジェクト指向っぽいかんじでウハウハだよね。
ウハウハしない人は、嘘でもいいからウハウハしておくと、このあとも引き続き愉快な気分を満喫できるのでおすすめだ。

そしてこのコントローラは短いから、特に説明はいらないかな。
いらないね。だからしない。

■ビューをつくろう

ビューも意外に簡単に作っておく。

app/Views/Users/index.ctp
<table>
<tr>
  <th>ID</th>
  <th>ユーザID</th>
  <th>ニックネーム</th>
  <th>作成日</th>
  <th>更新日</th>
</tr>
<?php foreach($users as $user):?>
<tr>
  <td><?php echo $user['User']['id']?></td>
  <td><?php echo $user['User']['username']?></td>
  <td><?php echo $user['Profile']['nickname']?></td>
  <td><?php echo $user['User']['created']?></td>
  <td><?php echo $user['User']['modified']?></td>
  </tr>
<?php endforeach?>
</table>


1.3と違うところがある。2.0ではechoのエイリアスであるe()が使えなくなった。なので、いちいちechoしている。気をつけろ!!

■ブラウザで見てみよう

上記設定をし終わったあと、ブラウザで閲覧んすると、こんなふうになったはずだ。


なんの変哲もない普通のインデックス。一覧ページ。リスト。

■検索フォームをつけよう

次にこの一覧の上に、検索フォームをつけてみよう。
検索フォームはエレメントで作っておいて、それをindex.ctpで読み込む形にしておく。

app/View/Elements/searchForm.ctp
<?php echo $this->Form->create('User', array('url' => '/users/index'))?>

<fieldset>
  <legend>Search or Die!</legend>
  <dl>
    <dt><label>ユーザID</label></dt>
    <dd><?php echo $this->Form->input('id', array(
      'type' => 'text', 'div' => false, 'label' => false))?></dd>
    <dt><label>ユーザ名</label></dt>
    <dd><?php echo $this->Form->input('username', array(
      'type' => 'text', 'div' => false, 'label' => false ))?></dd>
    <dt><label>ニックネーム</label></dt>
    <dd><?php echo $this->Form->input('nickname', array(
      'type' => 'text', 'div' => false, 'label' => false ))?></dd>
  </dl>

  <?php echo $this->Form->submit('検索', array('div' => false, 'escape' => false))?>

</fieldset>

<?php echo $this->Form->end()?>

これを読み込むために、index.ctpも若干変更を加えよう。
tableタグの上に以下を追記だ。
<?php echo $this->element('searchForm')?>

これで画面はこんなふうになったはずだ。


ようやくここまでたどり着いた。
この状態で、
  1. ページャ機能を使う
  2. 検索結果もページャに反映
ということがやりたいわけだ。
この2の部分を、Searchプラグインで気軽にやっちまおうぜ、というのがこの記事の趣旨。

■ページャをつけよう

まずはページャだ。
今回データが10件しか入ってないので、1ページ20件表示のデフォルト設定だと、検索結果がかならず1ページで完結してしまう。
1ページ3件表示にすれば、少なくとも3~4ページは稼げるので、上記1と2の連携もテストしやすいというもの。

それからジャンプリンクの設定をプリフィルタでやっておきたいので、beforeFilterメソッドを作成し、その中にpager_numbersというビュー変数を使える設定をしておこう。

というわけで、まずはコントローラにページャの設定を書いておこう。

app/Controller/UserController.php
  public function beforeFilter()
  {
    // ページャ設定
    $pager_numbers = array(
      'before' => ' - ',
      'after'=>' - ',
      'modulus'=> 10,
      'separator'=> ' ',
      'class'=>'pagenumbers'
    );
    $this->set('pager_numbers', $pager_numbers);
  }

  public function index()
  {
    $this->paginate = array(
      'limit' => 3
    );

    $this->set('users', $this->paginate('User'));
  }

indexアクションの中身をページャに変更させ、limitに3を指定してみた。
これで1ページ3件までとなるわけだ。


3件にはなったけど、ジャンプリンクがないからページ移動ができんばい!
となるので、ページャも作ってしまおう。

ページャは一覧の上下に付けたいから、同じ内容を二箇所に書くのはよろしく無いので、こちらもエレメントとして作っておくことにする。

app/View/Elements/pager.ctp
<div class="pagers">
  <?php echo ($this->Paginator->hasPrev())?$this->Paginator->first('&laquo;', array('class'=>'first', 'escape'=>false)):'<span class="disabled">&laquo;</span>'?>
  <?php echo $this->Paginator->prev('&lsaquo;', array('escape' => false), null, array('class'=>'disabled', 'tag' => 'span', 'escape' => false));?>
  <?php echo $this->Paginator->numbers($pager_numbers);?>
  <?php echo $this->Paginator->next('&rsaquo;', array('escape' => false), null, array('class' => 'disabled', 'tag' => 'span', 'escape' => false));?>
  <?php echo ($this->Paginator->hasNext())?$this->Paginator->last('&raquo;', array('class'=>'last','escape'=>false)):'<span class="disabled">&raquo;</span>'?>
</div>

なんどか試しては見たんだけど、$Paginator->first()、$Paginator->last()は、ない場合nullになってしまうのは1.3から変わってないようだ。これは残念。

したがって、$Paginator->hasPrev()、$Paginator->hasNext()で判定させている状態。

さて、このページャをindex.ctpから読み込ませるので、tableタグの上下に以下を追記しておく。
<?php echo $this->element('pager')?>

このままだと非常に汚いページャになってしまうので、CSSを少々いじってみよう。
最後の行に以下を追記すればOK。

app/webroot/css/cake.generic.css
.pagers {
  text-align:center;
  font-size:large;
}
.pagers a,
.pagers span {
  padding:0 0.2em;
}
.pagers .disabled {
  color:#ccc;
}

さて、ブラウザ見てみよう。


■次回のまとめ

今回で
  • ページャ機能を使う
はクリアできた。

次回は
  • 検索結果もページャに反映
部分をやる予定。

つまりSearchプラグインの具体的な使い方ってことだね。
ではお楽しみに。