少しはまったので、忘れない為のメモとして投稿。
paginatorコンポーネントを使用した一覧ページにて、virtualFieldで定義したjoin先のテーブルの
カラムでソートをする際の記述方法。
###Controller
Model1にModel2をjoinしてModel1.idでGROUP BYし、Model1に紐づくjoin先のテーブルのidをカウントする記述。
$this->paginate('Model1')
を行う前に前にバーチャルフィールドを定義する。
// join先のテーブルのカラムをcountするvirtualField
$this->Model1->virtualFields['model2VFColumnName'] = 'count(distinct Model2.model1_id)';
// paginate
$this->paginate = array('Model1' => array(
'fields' => array(
'id',
'title',
'model2VFColumnName',
),
'joins' => array(
'table' => 'model2s',
'alias' => 'Model2',
'type' => 'left',
'conditions' =>
'Model1.id = Model2.model1_id'
),
'conditions' => array(
is_delete = 0,
),
'limit'=>100,
'group' => array('Model1.id'),
'order' => array('Model1.id' => 'desc'),
);
$model1Data = $this->paginate('Model1');
###View
View側ではコントローラー側で定義したvirtualField名を記述する。
$this->paginator->sort('model2VFColumnName', '項目名');
以上で、virtualFiledで定義したjoin先のカラムで並び替えができる。
下記に調査の際に読んだPaginatorComponent.php内の仕組みをメモ。
###PaginatorComponent.php
paginateメソッド内で呼び出されているvalidateSortメソッド397行目辺り
並び替えに必要なorderをセットする処理
if ($correctAlias && $object->lib/Cake/Controller/Component/PaginatorComponent.php) {
$order[$object->alias . '.' . $field] = $value;
} elseif ($correctAlias && $object->hasField($key, true)) {
$order[$field] = $value;
} elseif (isset($object->{$alias}) && $object->{$alias}->hasField($field, true)) {
$order[$alias . '.' . $field] = $value;
}
上記コードの最初の条件文でhasField($field)
で呼び出し元モデルのテーブルに、
並び替え対象のカラムが存在するかどうかチェックしている。今回のケースはjoin先のテーブルでのソートなので、この条件分には入らない。
次の条件文ではhasField($field, true)
となっており、第2引数にtrueがセットされているのでvitrualFieldも含めてカラムが存在するかを確認している。
paginateをコントローラーで呼び出す前にvirtualFieldを定義する事でこの条件分に入りViewで定義する$this->paginator->sort('model2VFColumnName')
で生成されるリンクをクリックした際に渡るカラム名model2VFColumnName
が並び替えの対象となる。