※以前の記事「getCompiledSelect の罠、 useSoftDelete が抜け落ちる」を解決するための記事です。
getCompiledSelect で useSoftDelete が効かない原因
- system/BaseModel.php
- system/Model.php
に getCompiledSelect
に関する記述がなく、直接Bulderが発火しているため。
つまり、Modelに getCompiledSelect メソッドを追加してやればよい
とは言え system クラスを直接上書きはできない(?)ので、1つModelを噛むことにする
app/Models/MyModel.php
<?php
namespace App\Models;
use CodeIgniter\Model;
class MyModel extends Model {
public function __construct() {
parent::__construct();
}
public function getCompiledSelect(bool $reset = true) {
if ($this->tempUseSoftDeletes) {
$this->where($this->table . '.' . $this->deletedField, null);
}
return parent::getCompiledSelect($reset);
}
}
※ tempUseSoftDeletes
プロパティを使っているのは withDeleted
や onlyDeleted
で一時的に useSoftDelete
の挙動が変わるため。
実際に利用する場合はこうなる
app/Models/HogeModel.php
namespace App\Models;
use CodeIgniter\Model;
class HogeModel extends MyModel { // Modelではなく、MyModelを継承する
protected $table = 'hoge';
protected $primaryKey = 'id';
protected $returnType = 'array';
protected $useSoftDeletes = true;
protected $allowedFields = [];
protected $useTimestamps = false;
protected $createdField = 'created_at';
protected $updatedField = 'updated_at';
protected $deletedField = 'deleted_at';
protected $validationRules = [];
protected $validationMessages = [];
protected $skipValidation = false;
protected $afterFind = [];
public function fuga() {
echo $this->getCompiledSelect();
// SELECT * FROM `hoge` WHERE `hoge`.`deleted_at` IS NULL
echo $this->onlyDeleted()->getCompiledSelect();
// SELECT * FROM `hoge` WHERE `hoge`.`deleted_at` IS NOT NULL
echo $this->withDeleted()->getCompiledSelect();
// SELECT * FROM `hoge`
}
}
これができるとどう便利なの?
Model同士をJOIN句でテーブル結合をしたい時に有用。
特に集計を取るときや子テーブル側に検索条件が存在するに活躍する。
例えば上記サンプルと同様に PiyoModel
があり、hoge.id = piyo.hoge_id
のリレーションが存在する場合
getCompiledSelectを使わない(使えない)場合1
-
PiyoModel
側の$deletedField
を手動で解決しなければならない(AND piyo.deleted_at IS NOT NULL
の部分) - 見ての通りメンテナンス性が悪いが、クエリの効率は一番良い
$hogeModel = model('HogeModel');
$piyoModel = model('PiyoModel');
$hogePiyoList = $hogeModel
->join('piyo', 'hoge.id = piyo.hoge_id AND piyo.deleted_at IS NULL', 'inner')
->find();
// SELECT *
// FROM hoge
// INNER JOIN piyo ON hoge.id = piyo.hoge_id AND piyo.deleted_at IS NULL
// WHERE hoge.deleted_at IS NULL
getCompiledSelectを使わない(使えない)場合2
- 前回記事、「getCompiledSelect の罠、 useSoftDelete が抜け落ちる」での解決法
-
$deletedField
を解決するために一度PiyoModel
でクエリを実行しgetLastQuery
で実行クエリを取得する - つまり、無駄に一回クエリが発火してしまう
$hogeModel = model('HogeModel');
$piyoModel = model('PiyoModel');
$piyoModel->find(); // 空打ちする
$hogePiyoList = $hogeModel
->join('(' . $piyoModel->getLastQuery() . ') 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
getCompiledSelect
が useSoftDelete
を認識してくれる場合
-
getCompiledSelectを使わない(使えない)場合2 を
getCompiledSelect
が解決できる
$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;
// より安全に書くならテーブル名もModelに解決させる(どうせリレーションは書かないといけないが)
$hogePiyoList = $hogeModel
->join(
'(' . $piyoModel->getCompiledSelect() . ') ' . $piyoModel->table,
$hogeModel->table . '.id = ' . $piyoModel->table . '.hoge_id',
'inner'
)
->find();
感想: 「何かあったときのために」って言うけど、どうせまず見ないんだから物理削除させて!