【CakePHP3】TableクラスのhasMany定義に、conditionsで最大値を持つレコードのみを抽出させる方法

masyus-work-eyecatch-cakephp3

Tableクラスに定義可能なhasManyアソシエーションは、conditionsにて抽出条件を書くことが可能だ。普通はjoin先テーブルの特定のカラムの値で絞り込むとか単純なことをさせるのに使うが、たまたま

hasMany()でjoinさせたいテーブルの中で、特定のカラムが最大値のレコードだけをデフォルトで抽出したい

というニーズがあったので、これをどうにかしてhasMany()を定義する際に書けないかと思い調べてみたら方法があったのでシェアする。検証したCakePHPのバージョンは3.6.13。

お題として取り扱うデータベースの解説

とある駐車場内に停車している車を、毎時で記録する監視システムがあったとしよう。テーブルリレーションはこんな感じ:

parking_obsevation_er_diagram各テーブルのレコードはこんな感じ:

parking_carsでは、各駐車場に毎時停車している車が記録されている。この中から、各駐車場に停車している最新の車情報だけを常に収集できるCakePHP3アプリケーションを作りたいとする。

発行したいSQLの解説

ずばりこんな感じ:

結果はこうなる:

各駐車場に停車している最新の車情報が取得できている。

実際のhasMany()実装事例

これをCakePHP3のhasMany()で表現してみよう。各駐車場に停車している最新の車情報を知りたい場合はこのような書き方が可能だ:

このように書いておけば、parkingsにcontainでparking_carsをjoinした際に、常に最新のparking_carsの情報だけを収集してくれるようになる。hasManyのconditionsに複雑なことをゴニョゴニョ書きたくない場合はfinderというキーを使って、join先テーブルにロジックを逃すのも手だ。

 

最大値を持つレコードだけ抽出するSQLは他にもwhere existsを使う方法もあると思うので、SQLパフォーマンスに気をつけながらトライしてみていただきたい。

 

参考)

https://book.cakephp.org/3/ja/orm/associations.html#hasmany

http://kaa-saan.hatenablog.com/entry/2018/07/10/181351