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

※以前の記事「[getCompiledSelect の罠、 useSoftDelete が抜け落ちる](https://qiita.com/bananacoffee/items/0aff3f5cd005a5f1bfc6)」を解決するための記事です。

# 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を使わない（使えない）場合１

- `PiyoModel` 側の `$deletedField` を手動で解決しなければならない（`AND piyo.deleted_at IS NOT NULL` の部分）
- 見ての通りメンテナンス性が悪いが、クエリの効率は一番良い

```php
$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を使わない（使えない）場合２

- 前回記事、「[getCompiledSelect の罠、 useSoftDelete が抜け落ちる](https://qiita.com/bananacoffee/items/0aff3f5cd005a5f1bfc6)」での解決法
- `$deletedField` を解決するために一度 `PiyoModel` でクエリを実行し `getLastQuery` で実行クエリを取得する
- つまり、無駄に一回クエリが発火してしまう


```php
$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を使わない（使えない）場合２** を `getCompiledSelect` が解決できる

```php
$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();
```

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