CakePHP はページネートとソートについて、シンプルな実装以外は独自拡張が要る。結構だるい。
以下、未検証コード混在。案件で利用したものも。
ページネートや検索条件について index → edit → index
みたいなフローで遷移した際に「さっきまでの検索/ページネート条件に戻りたい」みたいなケース。
/**
* Index of Users. ( search / pagination )
* @param array $conditions
* @return Users $Users
*/
public function index()
{
$session = $this->request->session();
$conditions = $this->request->getQuery();
if (empty($conditions)) {
if (!empty($session->read('Users.index'))) {
return $this->redirect([
'action' => 'index',
'?' => $session->consume('Users.index'),
]);
}
}
$session->write('Users.index', $conditions);
// Expecting to receive something reset flag.
if ($this->request->query('all')) {
$session->delete('Users.index');
return $this->redirect(['action' => 'index']);
}
try {
$Users = $this->paginate($this->Users, $conditions);
$this->set(compact('Users'));
} catch (\Cake\Network\Exception\NotFoundException $e) {
return $this->redirect(['action' => 'index']);
}
}
ステータスクラスとか使わず、複数のフラグ ( is_stopped
とか ) の組み合わせで成り立つ「状態」によるソートをしなくちゃならんってケース。この場合ページングがさっぱり聞かなくなるので、ソート条件 ( $Query->order()
) やページング条件 ( $Query->limit()
) を自分でがっつり書き込んでから $this->paginate()
に渡す必要がある。
Agents
には.is_stopped
やstopped_date
があり、停止・停止予約状態がるAgents
にはis_registered
による、仮登録・本登録状態があるAgents
は上記4つの状態について、「本登録状態」をノーマルとして「仮→本→停予→停止」のようなライフサイクルを送るAgents
は物理削除されるケースがあるが、これは完全なサービスからの離脱・または生成ミスの訂正なため、ステータスとして管理しない
/**
* AgentsController::index()
*
* view に $this->Paginator->sort('Agents.is_registered', 'ステータス') みたいなソートリンクがある前提
* sort に Agents.is_registered が投げられた時クエリを独自にカスタムして paginate へ渡す
* 上記ケース以外では通常通りプレーンなクエリ $this->Agents->find() を渡す
*/
public function index() {
$conditions = [ /* 何らかの抽出条件 */ ];
$agents = $this->Agents->find()->where($conditions);
if ($this->request->query('sort') == 'Agents.is_registered') {
// クエリビルダーにより Select 句に Case 文をぶち込んでソート可能な仮想カラム status を生成
$agents = $agents->select($this->Agents);
$agents = $agents->select(['status' => $agents->newExpr()->addCase(
[
$agents->newExpr()->add([
'is_registered' => false,
'is_stopped' => false,
]),
$agents->newExpr()->add([
'is_registered' => true,
'is_stopped' => false,
]),
$agents->newExpr()->add([
'stopped_date >' => date('Y-m-d'),
'is_stopped' => true,
]),
$agents->newExpr()->add([
'stopped_date <=' => date('Y-m-d'),
'is_stopped' => true,
]),
],
[0, 1, 2, 3],
['integer', 'integer', 'integer', 'integer']
)]);
// 仮想カラム status によるソートを行う
$agents->order(['status' => $this->request->query('direction')]);
// もし page がクエリストリングで投げられたら ( = ページネーションのリンクを踏んだら )
// page の値から offset を計算して開始位置を調整 ( パフォーマンスを気にするなら BETWEEN とかのがいいのかな )
if ($this->request->query('page')) {
$offset = (PAGINATE_LIMIT * ((int)$this->request->query('page') - 1));
$agents->offset($offset);
}
}
// PaginatorHelper に渡すためのクエリに変換
$agents = $this->paginate($agents, ['limit' => PAGINATE_LIMIT]);
$this->set(compact('agents'));
}