CakePHP3/アソシエーション,Entityのアクセサ,曖昧検索,カラムの型抽出

投稿者: | 2018-03-15

概要

本日は雑多なテーマでお送りいたします。

孫アソシエーションの指定

以下参照
CakePHP3のハマリどころ:孫associationを指定する記法はメソッドによって異なる

継承Model同士のアソシエーション

継承されたModel同士を繋ぐ場合、定義を継承Modelに記述する。以下、例。

// ExtAaasはExtBbbs, ExtCccsと関係を持つ
class ExtAaas extends AaasTable
{

    // 省略 

    public function initialize(array $config)
    {
        parent::initialize($config);
        $this->entityClass('App\Model\Entity\ExtAaas');

        $this->belongsTo('ExtBbbs', [
            'foreignKey' => 'aaas_id'
        ]);
        $this->hasMany('ExtCccs', [
            'foreignKey' => 'aaas_id'
        ]);
    }
}

この例の場合、ExtAaasだけでなく、ExtBbbs, ExtCccsにもExtAaasと関係を持つように定義する。
また、$this->entityClass(‘App\Model\Entity\ExtAaas’);の部分で、この継承Modelから成るEntityも継承したものを使うことを宣言している。
Controller側で継承Model同士を繋ぐためには以下のように記述する。

$aaas = TableRegistry::get('ExtAaas')->find('all');
$aaasHaveBbbs = $aaas->contain(['ExtBbbs']);

bake model hoge 実行時、id以外で関係を持つものは自動で定義されない

っぽいです。従って別途定義してやる必要がある。拡張ModelとEntityを作成すること。

Entityに仮想フィールドを定義する

参考:Cakephp3 エンティティー

Entityには仮想のフィールド(アクセサー)を定義することができる。これにより、対象Entityの仮想フィールドを参照した時、処理が行われ値が返される。以下、例。

class ExtAaa extends Aaa
{
    protected function _getHelloWorld()
    {
        return 'Hello World';
    }
}

対象Entityのアクセサーは以下のようにして参照する。

echo $aaa->hello_world;
echo $aaa->get('hello_world');

適当な加工・計算を行なった後値を返却することも可能。

子孫に条件付けをしつつModelを繋ぐ

子、孫、曾孫などとアソシエーションを持ち、それらを引っ張るときかつ子孫に条件付けをしてレコードを引き出したい時はcontain()にクロージャを渡して絞り込みを行なったり、同時にさらに深い関係を取り出す(非推奨:できれば避けたい)。

$aaas = TableRegistry::get('Aaas')->find('all');


// 関係は Aaas - Bbbs - Cccs - Ddds ひ孫までアソシエーションがある(地獄)
// Cccsのステータスが済になっており、指定期間のものでかつ他の3つと関係があるものを抽出(Hell)

$aaas = $aaas->contain(['Bbbs' => ['Cccs' => function($q) use ($selected_date){
        return $q->where("DATE_FORMAT(Ddds.abd_date, '%Y-%m') = '" . $selected_date . "'")
                 ->where(['status_kubun' => '済'])
                 ->contain(['Ddds']);
}]]);

これは、Aaasが子にBbbss、孫にCccsを持ち、かつ、Cccsのステータスが’消込完了’かつ指定の期間のもので、適合するCccsと関連があるDddsを引っ張ってくる。Dddsは、Aaasから見れば曾孫の関係に当たる。

多の関係を持つアソシエーションはcontain()の後where()で絞り込むことはできない

参考:CakePHP 3 で hasMany か belongsToMany に対し条件を設定する

例えば、以下のようなクエリがある。HogesはFugasとhasManyの関係にある。

$hoges = TableRegistry::get('Hoges')->find('all');
$hogesHaveFugas = $customers->contain(['Fugas'])

この時、Fugasのカラムが持つ値によって絞り込みを行いたいとする。

$hogesHaveFugas = $hogesHaveFugas->where(['Fugas.id' => 1]);

これはDBエラーにより失敗する。参考記事にも書いてある通り、複数のSQLに分割されるらしいので失敗する。
複数のテーブルに跨ったキーワード検索をループで行いたい場合にこれに困らされる。
つまり、多数の子や孫を持ったqueryに対してwhere検索をかけようとした時、対象のテーブルや途中点のテーブルが基準テーブルに対して多の関係を持つ時、検索に失敗するということ。
こういう場合はcontain時にクロージャを渡してあげて絞り込みを行わないといけない。

整数型を持つカラムに曖昧検索(LIKE句)は使えない

以下のような処理があったとする。

$hoges= $hoges->where([ $column . ' LIKE' => '%' . $column_value . '%' ]);

この時、対象カラムが文字列型であれば検索は成功するが整数型だと失敗する。対象カラムの型に気をつけて厳格な検索と曖昧検索を使い分けないといけない。
テーブルのカラムが持つ型は以下で取ってこられるので一応分岐を作れる。

$hogesTypeMap = TableRegistry::get('Hoges')->getSchema()->typeMap();
debug($hogesTypeMap['id']) // 'integer' が出る