2022年1月25日修正
RIGHT JOIN時にバグがあったので修正しました。
joinModel
- $cond .= ' AND ' . $model->table . '.' . $model->deletedField . ' IS NULL';
+ $this->where($model->table . '.' . $model->deletedField . ' IS NULL');
joinModelOnlyDeleted
- $cond .= ' AND ' . $model->table . '.' . $model->deletedField . ' IS NOT NULL';
+ $this->where($model->table . '.' . $model->deletedField . ' IS NOT NULL');
※先に、以下の記事を読んでいただくと理解が深まると思います
Model で useSoftDelete を使いながらJOINをしようと思うと面倒くさい
getCompiledSelect で useSoftDelete が効くようにする の解決部で触れているが、メンテナンス性を上げるためにModelの機能を使いながらJOIN句を使おうとするとどうしてもサブクエリが発生してしまう。
以下、getCompiledSelect が useSoftDelete を認識してくれる場合 より
$hogeModel = model('HogeModel');
$piyoModel = model('PiyoModel');
$hogePiyoList = $hogeModel
->join('(' . $piyoModel->getCompiledSelect() . ') piyo', 'hoge.id = piyo.hoge_id', 'inner')
->find();
// SELECT *
// FROM hoge
// INNER JOIN (SELECT * FROM piyo WHERE deleted_at IS NULL) piyo ON hoge.id = piyo.hoge_id ← このサブクエリが憎い
// WHERE hoge.deleted_at IS NULL;
小規模な開発では問題にならないが、JOINが増えたり、レコード数が巨大になっていくほどサブクエリがボトルネックになっていくことは想像に難くない。
そこで、getCompiledSelectを使わない(使えない)場合1 のように手動でJOINしたものが簡単に作成できるようにカスタムのModelを作成した。
カスタムモデルの作成
[前回作成した MyModel.php] (https://qiita.com/bananacoffee/items/1b9b56ce541174a3fb7d#%E3%81%A4%E3%81%BE%E3%82%8Amodel%E3%81%AB-getcompiledselect-%E3%83%A1%E3%82%BD%E3%83%83%E3%83%89%E3%82%92%E8%BF%BD%E5%8A%A0%E3%81%97%E3%81%A6%E3%82%84%E3%82%8C%E3%81%B0%E3%82%88%E3%81%84) に追記する、
/**
* JOIN Model without delted
*
* モデル同士をJOINする(useSoftDeleteを利用する)
*
* @param object $model
* @param string $cond The join condition
* @param string $type The type of join
* @param boolean $escape Whether not to try to escape identifiers
*
* @return $this
*/
public function joinModel(object $model, string $cond, string $type = '', bool $escape = null) {
if ($model->useSoftDeletes) {
$this->where($model->table . '.' . $model->deletedField . ' IS NULL');
}
return parent::join($model->table, $cond, $type, $escape);
}
/**
* JOIN Model include Deleted
*
* モデル同士をJOINする(deletedを含む)
*/
public function joinModelWithDeleted(object $model, string $cond, string $type = '', bool $escape = null) {
return parent::join($model->table, $cond, $type, $escape);
}
/**
* JOIN Model Only Deleted
*
* モデル同士をJOINする(deletedのみ)
*/
public function joinModelOnlyDeleted(object $model, string $cond, string $type = '', bool $escape = null) {
if ($model->useSoftDeletes) {
$this->where($model->table . '.' . $model->deletedField . ' IS NOT NULL');
}
return parent::join($model->table, $cond, $type, $escape);
}
見ての通り、useSoftDeletes
が設定されていたら $cond
に追記してビルダに渡しているだけである。
使い方
join()
のテーブルの代わりにモデルを渡せば良い。
MyModel
の継承の仕方は前回の記事を参照
$hogeModel = model('HogeModel'); // MyModelを継承していること
$piyoModel = model('PiyoModel'); // MyModelは必要ないが継承しておいて損はないと思う
$hoteModel
->joinModel($piyoModel, $hogeModel->table . '.id = ' . $piyoModel->table . '.hoge_id', 'inner')
->find();
// SELECT *
// FROM hoge
// INNER JOIN piyo ON hoge.id = piyo.hoge_id
// WHERE hoge.deleted_at IS NULL AND piyo.deleted_at IS NULL;
$hoteModel
->joinModelWithDeleted($piyoModel, $hogeModel->table . '.id = ' . $piyoModel->table . '.hoge_id', 'inner')
->find();
// SELECT *
// FROM hoge
// INNER JOIN piyo ON hoge.id = piyo.hoge_id
// WHERE hoge.deleted_at IS NULL;
$hoteModel
->joinModelOnlyDeleted($piyoModel, $hogeModel->table . '.id = ' . $piyoModel->table . '.hoge_id', 'inner')
->find();
// SELECT *
// FROM hoge
// INNER JOIN piyo ON hoge.id = piyo.hoge_id
// WHERE hoge.deleted_at IS NULL AND piyo.deleted_at IS NOT NULL;
複雑なJoin句には対応できないかもしれないが、結構使えるはず。