LoginSignup
0
0

More than 1 year has passed since last update.

[CodeIgniter4 Tips] getCompiledSelect で useSoftDelete が効くようにする

Last updated at Posted at 2021-07-31

※以前の記事「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

$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

getCompiledSelectuseSoftDelete を認識してくれる場合

  • getCompiledSelectを使わない(使えない)場合2getCompiledSelect が解決できる
$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();

感想: 「何かあったときのために」って言うけど、どうせまず見ないんだから物理削除させて!

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0