【CakePHP】PaginatorComponentの$whitelistを使用し、ページネーションの条件を制限する。
fukasawaです。こんにちは。先日エレベータに乗ろうとした際、先に乗っていた人に【閉】ボタンをガスガスと連打されて「ええっ」と思うことがありました。その後、目的の階で降りるときにその人が【開】ボタンを押してくれて、さらに「えええっ」となりました。ツンデレだったんでしょうか。
さて、CakePHPでページネーションを実装する際はPaginatorComponentを使用して実装をします。「ソート順」「取得件数」等を指定することができ、好きな形式で検索結果を取得できてとても便利な機能です。ですが、例えばユーザがブラウザから「http://****/***/***/limit:2/page:2」のようにURLにパラメータを指定した場合にも、指定のパラメータで検索結果を取得できてしまうという状態になっています。(一番の懸念点と思われる「最大取得件数」はデフォルト「maxLimit=100」と制限されているので、変更されたところでさほど問題にはならなそうですが。)
PaginatorComponent::whitelistを指定すると、「ソート順」や「取得件数」をリクエストパラメータで変更されないよう設定できるようです。簡単に動きを見てみます。
※CakePHP 2.6.3 で検証しています。
1. デフォルトの設定について
まず、デフォルトの設定を見てみます。PaginatorComponent.phpで下記のように指定されています。
■PaginatorComponent.php
public $whitelist = array(
'limit', 'sort', 'page', 'direction'
);
この設定により
- limit(取得件数)
- sort(ソートに使用するフィールド)
- page(取得するページ数)
- direction(昇順・降順)
の4つが、リクエストパラメータで指定可能な状態になっています。
// ※ブラウザからリクエストパラメータで渡される値
$this->request->params['named'] = array(
'limit' => '10'
, 'order' => 'title'
, 'page' => 2
, 'direction' => 'desc'
, 'fields' => array('id', 'price')
);
$result = $this->Paginator->paginate('Book');
リクエストパラメータで'fields'を設定していますが、$whitelistに含まれていないので無視されます。
2. $whitelistを指定する。
$whitelistはコンストラクタで指定できます。PaginatorComponentの$whitelistが定義されている箇所には「be careful with what you permit.」というコメントが付いていており、許可する項目を注意して設定するよう促されています。
【例1】「ページ数」のみを指定可能にする。(order, directoin は無視される。)
public $components = array('Paginator' => array(
'whitelist' => array('page')
));
...
$this->request->params['named'] = array(
'order' => 'title' // 無効
, 'page' => 2
, 'direction' => 'desc' // 無効
);
$result = $this->Paginator->paginate('Book');
【例2】'fields'(取得するフィールド)を指定出来るようにする(これは、迂闊に追加すると危なそうです。)
public $components = array('Paginator' => array(
'whitelist' => array('page')
));
...
$this->request->params['named'] = array(
'fields' => array(
'id'
, 'price'
)
);
$result = $this->Paginator->paginate('Book');
※CakePHP3でも同じ方法でいけるかと思ったのですが、上手く$whitelistが設定されませんでした。やり方がまずかったのでしょうか…?
3. PaginatorComponent::paginate()、PaginatorComponent::validateSort()の引数としての$whitelist
PaginatorComponent.phpのpaginate()とvalidateSort()が定義されいる箇所を見ると、下記のようになっています。
public function paginate($object = null, $scope = array(), $whitelist = array()) {
…
}
public function validateSort(Model $object, array $options, array $whitelist = array()) {
…
}
第3引数に$whitelistという変数が使われているのですが、どうやらこちらの$whitelistはプロパティとして定義されている$whitelistとはまた別物のようです。paginate()、validateSort()の第三引数として渡す$whitelistは「ソートに使用できるフィールド」を指定します。
// 'price'でしかソートできなくなる。
$result = $this->Paginator->paginate('Book'
, array()
, array('price')
);
デフォルトのarray()を指定すると、全てのフィールドが使用可能になります。
※CakePHP3では「sortWhitelist」を使うことで同様の動作が実現できるようです。(自分はまだ試せていないのですが…)
4. まとめ
$whitelistに設定していることを忘れて、「あれ、ソートされないけどなんで?」みたいな事が起こりそうです。