PHP
Laravel
ORM
Eloquent
Day 1

Laravel のバリデーションルール exists に Eloquent を使う

千株式会社の初 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'), // これ
],

すっきり!


追伸

使ってみるとカラム名を指定することが多い&主キーで探すことが多いので、指定がないときはフィールド名と同じではなく主キーのほうがより良さそう

あとで変えるかも