【CakePHP】PaginatorComponentの$whitelistを使用し、ページネーションの条件を制限する。

【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に設定していることを忘れて、「あれ、ソートされないけどなんで?」みたいな事が起こりそうです。

  • このエントリーをはてなブックマークに追加

この記事を読んだ人にオススメ