cakephp + UNION + paginator + sort
訳あって cakephp で UNION を使うことになったんだが。。。
cakephp customな pagenateで $orderが emptyになってしまう件
どうもcakephpにはUNIONはないようで、->query()をおこなうことに。
(SELECT --- FROM --- WHERE ---)
UNION
(SELECT --- FROM --- WHERE ---)
な感じで。
UNIONを使う場合、どうやらpublic $useTable = false;にするらしいので、わかりやすく別モデルを用意。
まあ、これで済むものならいいんだけど、paginateしたい。
となると、自分でごりっと実装するか、paginatorのコンポーネントを利用するかになるんだけど、できれば標準のpaginatorでなんとかしたい。
paginatorの準備
まあ、このpaginatorについては使える記事がたくさん出てくるから、労はなかった。良い記事書いてる人tks。
英語
http://blog.andolasoft.com/2014/08/how-to-do-custom-pagination-in-cakephp.html
日本語
http://d.hatena.ne.jp/sutara_lumpur/20120825/1345885278
日本語
http://u2k772.blog95.fc2.com/blog-entry-332.html
ORDER そして SORT
nで、詰まったのが、ORDERまわりだった。。。単純に
$query = array (
'order' => 'hoga DESC' ,
'limit' => 50,
'extra' => array (
'type' => (ここにクエリ突っ込むやり方が単純にわかりやすかった。)
)
);
$this->Paginator->settings = $query;
これなら、とりあえずorderは効くんだけど、viewで$this->Paginator->sort ()しても上手いこといかないんだな。
単純にORDERするだけなら、parameな感じで指定するより生クエリーに書いてしまえばいいんだけど、$this->Paginator->sort ()を使いたいんだな。
普通にpaginateを使う場合には特に意識することもなかったんだけど、今回はそもそも、'order'=> array( 'hoga' => 'asc' )みたいな感じで渡しても、overrideしたfunction paginateには空のarray()がわたってくるんだな。
'order' => 'hoga DESC' なら大丈夫ってなんでだよ。。。
PaginatorComponentを見たらわかった
だらだら書いてごめんね。ちょっと愚痴りたかった気分だ。
cake/lib/Cake/Controller/Component/PaginatorComponent.phpの400行目あたりの箇所。
public function validateSort(Model $object, array $options, array $whitelist = array()) {
~~略~~
if ($correctAlias && $object->hasField($field)) {
$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)ではじかれるね。
ググれかすしたらここがヒット。猫が可愛い。。。
http://blog.livedoor.jp/hal_can/archives/52346896.html
ググれかすしたらここがヒット。猫が可愛い。。。
http://blog.livedoor.jp/hal_can/archives/52346896.html
なわけで、とりあえず突っ込んだら、
function hasField($name, $checkVirtual = false) {
return true;
}
動いてなーーーーーい。
なぜだ?
UNIONを使うからややこしくなってる(疲)
public $useTable = false;
にしてるから当然 aliasはnullだね。
という訳で、書き直し。
UNIONを使うモデルを切り出して用意。
App::uses ( 'AppModel', 'Model' );
App::uses ( 'Fuge', 'Model' ); //UNIONに使うモデル
class Hoga extends AppModel {
public $useTable = false;
//alias名はなんでもいい。無くてもいいけどNULLは気持ち悪いので念のため。
//alias名はなんでもいい。無くてもいいけどNULLは気持ち悪いので念のため。
public $alias = 'KABARANAINAMAE';
public $virtualFields = array (
//AS でsortするなら virtualFields を設定
);
//hasFieldをoverride
function hasField($name, $checkVirtual = false) {
/*
* 実際に使用するモデルのfieldを取得して
* columnがあるかチェックする
*/
$Fuge = new Fuge(); //使用するモデル
* 実際に使用するモデルのfieldを取得して
* columnがあるかチェックする
*/
$Fuge = new Fuge(); //使用するモデル
$hasFields = array_keys ( $Fuge->getColumnTypes () ); //field取得
if (in_array ( $name, $hasFields )) {
return true;
}
if ($checkVirtual && !empty($this->virtualFields)) {
if (array_key_exists ($name, $this->virtualFields )) {
return true;
}
}
//_schemaとかは別に必要ないから省く
return false;
}
//paginateをoverride
function paginate($conditions, $fields, $order, $limit, $page = 1, $recursive = null, $extra = array()) {
/*
* $orderのarray()には 『 'KABARANAINAMAE.sort_key' => 'DESC'』
* みたいな感じで来る
* みたいな感じで来る
*/
$orderStr = '';
if (! empty ( $order )) {
foreach ( $order as $k => $ord ) {
//'KABARANAINAMAE.'を削除しないとUNIONをしている関係でエラーになる
//'KABARANAINAMAE.'を削除しないとUNIONをしている関係でエラーになる
$k = str_replace ( $this->alias . '.', '', $k );
$orderStr [] = $k . ' ' . $ord;
}
$orderStr = 'ORDER BY ' . implode ( ', ', $orderStr );
}
$sql = $extra ['extra'] ['type'];
$sql .= $orderStr;
$sql .= ' LIMIT ' . $limit;
if ($page > 1) {
$sql .= ' OFFSET ' . ($limit * ($page - 1));
}
return $this->query ( $sql );
}
//paginateCountをoverride
function paginateCount($conditions = null, $recursive = 0, $extra = array()) {
return count ( $this->query ( preg_replace ( '/LIMIT \d+ OFFSET \d+$/u', '', $extra ['extra'] ['type'] ) ) );
}
}
先人の試行錯誤をもとに、小細工加えて無事動きました。
感謝感謝。
それにしても・・・コードが不細工だな。。。
それにしても・・・コードが不細工だな。。。
コメント
コメントを投稿