内職メモ

うにゃあ(←挨拶)

cakePHPでプログラムを書く内職で、学んだことをメモるのです。

ヘルパーの使い方

src/View/AppView.php の中で、

$this->loadHelper('▲▲');

と定義。その後、src/View/ Helper というディレクトリで、 ▲▲ Helper.php というファイル名で作ればとりあえず動く。

ヘルパー内で余所のヘルパーを使いたかったら、class内で $helpers という配列に書けば使えるように登録してくれる。

<?php
namespace Cake\View\Helper;
use Cake\View\Helper;
use Cake\Datasource\ModelAwareTrait; //←後述
class ▲▲Helper extends Helper
 {
     use ModelAwareTrait; //←後述
    var $helpers = ['Html', 'Form']; // HTMLヘルパーとFORMヘルパーを使いたい時の例
 
    public function calendar( $y, $m ){ // このよーに定義する
        $this->modelFactory('Table', ['Cake\ORM\TableRegistry', 'get']); // ←後述
        $this->loadModel( 'Tools' );  // ←後述 
         ~~~
    }
}

「後述」の所について。

この部分を書いておけば、ヘルパー内からモデル(つまりデータベース)へアクセス出来るとのこと。

グチャグチャな条件下でのソート

あるデータベースの同じカラムに親カテゴリと子カテゴリが混ざって入ってる値を別々に取り出して、ソートをしてからマージする必要があった。設計ミスだと思うけどゴリ押しした。

parent_idが0なら、親カテゴリ。自然数なら、その数のIDを親に持つ子カテゴリ。sub_noが兄弟カテゴリ間での並び順。

雰囲気で読んで!

$query = $this->Categorise->find('all', [
	'conditions' => [
		'del' => 0,
		'parent_id' => 0,
	],
	'order' => [ 'sub_no'=>'desc' ],
]);
$maincat = $query->toArray();
$this->set( 'maincat', $maincat );

$query = $this->Categorise->find('all', [
	'conditions' => [
		'del' => 0,
		'parent_id <>' => 0,
	],
	'order' => [ 'sub_no'=>'desc' ],
]);
$subcat = $query->toArray();
$this->set( 'subcat', $subcat );

// 親カテゴリの後に子カテゴリが、その後に次の親カテゴリが並ぶように調整
// ムダがある書き方だけどしょうがない
$cate = [];
foreach( $maincat as $m ){
	$cate[ $m['id'] ] = $m;
	foreach( $subcat as $s ){
		if( $s['parent_id'] != $m['id'] ){ continue; }
		$cate[ $s['id'] ] = $s;
	}
}
$this->set( 'cate', $cate );

もっとグチャグチャな条件下でのソート

カテゴリID(英文字)とコロン(:)区切りで並び順がくっついてるものが、同じカラムに複数セミコロン(;)区切りで入ってる。それを目的のカテゴリIDだけ取り出して、並び順を取り出してソート。さらにページングもします。

カテゴリIDが 含まれてる行は問答無用で全部取ってくるので、酷い代物だと思います。もっとやり様はなかったのか。。。

$p = (int)$this->request->getQuery( 'p' );

$tmpItems = $this->Items->find('all', [
	'conditions' => [
			'del' => 0,
			'visible' => 1,
			'category LIKE' => '%CAMP%',
	],
]);
$tmpItems = $tmpItems->toArray();
$allItems = [];
$itemId = [];
$sortId = [];
// ソート済みID配列を作るために、ソート番号の配列を作る
// ついでに商品データもコピーしておく
foreach( $tmpItems as $ai ){
	$result = preg_match('/[;^]*?'.'CAMP'.'[^:]*:([\d]+)[$;]*?/', $ai['category'], $m);
	if( $result === 1 ){
		$itemId[] = $ai['id'];
		$sortId[] = $m[1];
	}
	else{
		$itemId[] = $ai['id'];
		$sortId[] = 0;
	}
	// DBから落としたデータ。あとで選び取る。
	$allItems[ $ai['id'] ] = $ai;
}
$tmpItemId = $this->makeSortNo( $itemId, $sortId );
if( $p < 1 ){ $p = 1; }
$viewItems = [];
$i = 0;
$j = 0;
// ソート済みのID配列を使って、DBから落としたデータを順に選び取る
foreach( $tmpItemId as $item ){
	if( $i++ < ($p-1)*6 ){						continue; }
	elseif( $j++ >= 6 ){						break;	}
	if( isset( $allItems[ $item ] ) and $allItems[ $item ]['id'] > 0 ){
		$viewItems[] = $allItems[ $item ];
	}
}
$this->set( 'items', $viewItems );

ソートは array_multisort() を使いました。実は初めて使いました、てへ。

private function makeSortNo( $itemId, $sortId ){
	array_multisort( $sortId, SORT_DESC, SORT_NUMERIC,
                         $itemId, SORT_DESC, SORT_NUMERIC );
	return( $itemId );
}

ぶっちゃけると、カテゴリIDが入ってるのを全部持ってきて、正規表現でカテゴリIDに対応する数値を抜き出して、あとはその数値でソート、です。

ただし連想配列のソートなので、ちょっと一工夫必要です。