千株式会社の初 Advent Calendar、私の初 Qiita !
追記:強化版ができました
Laravel のバリデーションルール exists に Eloquent を使う v2
背景
Laravel の DB レコード存在チェックをするバリデーションルールとして exists がある
しかしハードコーディングな要素が多いので、Eloquent を使って要素を意識させないようにしたい
例えば、ソフトデリートを考慮するには、
- テーブル名
- 検索するカラム名
- ソフトデリートのカラム名
- ソフトデリートの削除されていないときの値
を知って、コントローラかフォームリクエストに書く
'id' => [
'required',
'integer',
'exists:hoges,id,deleted_at,null', // これ
],
Eloquent を使っていると、グローバルスコープでカラム名やその値を意識せずに書けるので、バリデーションもそうしたい
解決策
カスタムバリデーションルールのなかでは、ルールオブジェクトがシンプルに書けるので使う
ルールオブジェクトのクラスのソースコードが次
コンストラクタが使われていないので、そこでモデルクラスを渡す
<?php
namespace App\Rules;
use Illuminate\Contracts\Validation\Rule;
class Exists implements Rule
{
protected $modelName;
protected $propertyName;
public function __construct(string $modelName, ?string $propertyName = null)
{
$this->modelName = $modelName;
$this->propertyName = $propertyName;
}
public function passes($attribute, $value)
{
if ($this->propertyName === null) {
$this->propertyName = $attribute;
}
return $this->modelName::where($this->propertyName, $value)->exists();
}
public function message()
{
return ':attribute はデータが存在しません';
}
}
使い方
リクエストのフィールド名と同じカラム名で探すなら
use App\Hoge;
use App\Rules\Exists;
'id' => [
'required',
'integer',
new Exists(Hoge::class), // これ
],
と書けば、ソフトデリートなどのグローバルスコープも考慮してくれる
リクエストのフィールド名と異なるなら、カラム名を指定できる
'hoge_id' => [
'required',
'integer',
new Exists(Hoge::class, 'id'), // これ
],
すっきり!
追伸
使ってみるとカラム名を指定することが多い&主キーで探すことが多いので、指定がないときはフィールド名と同じではなく主キーのほうがより良さそう
あとで変えるかも