開発ブログ

株式会社Nextatのスタッフがお送りする技術コラムメインのブログ。

電話でのお問合わせ 075-744-6842 ([月]-[金] 10:00〜17:00)

  1. top >
  2. 開発ブログ >
  3. PHP >
  4. Laravel >
  5. 【Laravel 9】Laravel 9に追加されたバリデーションルール「missing」を紹介

【Laravel 9】Laravel 9に追加されたバリデーションルール「missing」を紹介

こんにちは、モリです。 先日Laravel10がリリースされ、その新機能について注目されていると思われますが、今回の記事では、同じく今月にリリースされたLaravel 9.49の変更点の中から1つご紹介したいと思います。 Laravel 9.49で新たなバリデーションルールである「missing」が追加されました。(参考リンク:https://laravel-news.com/laravel-9-49-0)

リンク先では「missing」は「prohibits」ルールの厳密バージョンであると説明されています。しかし実際は「prohibited」ルールの厳密バージョンだと思われるので、prohibitedとmissingを比較してみたいと思います。

※ややこしいですが、「prohibits」ルールと「prohibited」ルールが存在します。

環境

  • PHP: 8.1
  • Laravel Framework: 9.52.4

missingとprohibitedを比較

結論としては、それぞれのルールは次のようになります。

  • prohibited
    • フィールドが存在しないか、または空であることを検証
    • 空文字・空配列、nullの場合はスルー
  • missing
    • フィールドが存在しないことを検証
    • 値は関係なくフィールド(キー)が存在していたらNG


まず下記のようなFormRequestクラスを実装したルートについてHTTPテストを実行しながら比較します

namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;

class CreatePost extends FormRequest
{
    /**
     * Determine if the user is authorized to make this request.
     *
     * @return bool
     */
    public function authorize(): bool
    {
        return true;
    }

    /**
     * Get the validation rules that apply to the request.
     *
     * @return array
     */
    public function rules(): array
    {
        return [
            'name' => ['prohibited']
        ];
    }
}
namespace Tests;

use Tests\TestCase;

class VaridationTest extends TestCase
{
    public function testVatidation()
    {
        $attribute = ['name' => 'Taro'];
        
        $response = $this->json(
            'post',
            "api/test",
            $attribute
        );

        $response->assertStatus(422); // PASS。バリデーションが通らない
    }
}

上記のようにバリデーションルールとして'name' => ['prohibited'] を設定し、$attribute = ['name' => 'Taro']; のように「name」フィールドが存在し、値が文字列のリクエストを送ると422が返りバリデーションが通っていません。 'name' => ['prohibited']ルールを設定したまま、リクエスト形式を変えて試してみます。

// prohibitedの場合

$attribute = ['name' => 'Taro']; // 値が文字列。通らない

$attribute = ['name' => ['Taro']]; // 値が配列形式。通らない

$attribute = ['name' => '']; // 値が空文字。通る

$attribute = ['name' => ' ']; // 値がスペース。通る

$attribute = ['name' => null]; // 値がnull。通る

$attribute = ['name' => []]]; //  値が空配列。通る

上記のように「prohibited」ルールは、空文字・空配列、nullの場合はチェックがスルーされるところが特徴となります。 今度は「missing」ルールで同じように試します。

// missingの場合

$attribute = ['name' => 'Taro']; // 値が文字列。通らない

$attribute = ['name' => ['Taro']]; // 値が配列形式。通らない

$attribute = ['name' => '']; // 値が空文字。通らない

$attribute = ['name' => ' ']; // 値がスペース。通らない

$attribute = ['name' => null]; // 値がnull。通らない

$attribute = ['name' => []]]; //  値が空配列。通らない

「prohibited」ルールで通っていた空文字・空配列、nullがスルーされていません。 「prohibited」ルールでは値がどうこうというよりも、フィールド(キー)が存在すること自体をNGとするルールとなります。「prohibited」をより厳しくしたルールといえます。

個人的にはnull等を通してしまうと後続の処理でバグが発生しそうなので、prohibitedではなくmissingを利用するケースが増えそうだと感じています。

missingの他バリエーションルール

今回追加されたmissingには他のバリエーションも追加されています。

  • missing_if:attribute,value
  • missing_unless:attribute,value
  • missing_with:attribute1,attribute2
  • missing_with_all:attribute1,attribute2


これらは「required_if」等と同じ使い方ですので、ここでの説明は割愛します。

ただ「prohibited」では複雑な条件についてはRule::prohibitedIfメソッドを利用して作成することができていました。

// リクエストのidが禁止リストに含まれている場合、questionフィールドに値が存在することを禁止
'question' => [
    Rule::prohibitedIf(function () {
        $id = $this->get('id');
        $denyList = [1, 2, 3];
        return in_array($id, $denyList);
    })
]

上記もあくまで「prohibited」ルールであるため、値が空文字・空配列、nullの場合がスルーされます。 複雑な条件が必要で、特定フィールドの存在自体を許したくない場合はどうすればいいか。 Rule::missingIfメソッドが追加されましたといいたいところですが、今回それは追加されていません。

ルールを自作するか下記のようなwithValidatorメソッド内に追加のチェックを記述して対応するしかないと思います。

public function withValidator(Validator $validator): void
    {
        $validator->after(function ($validator) {
            $id = $this->get('id');

            $denyList = [1, 2, 3];

            if (in_array($id, $denyList)) {
                if (array_key_exists('question', $this->toArray())) {
                    $validator->errors()->add(
                        'question',
                        '質問に回答が許可されていないIDです'
                    );
                }
            }
        });
    }

array_key_existsでチェックしていますが、追加された「missing」ルールはarray_key_existsを利用したバリデーションといえます。(https://github.com/laravel/framework/pull/45717

おまけ

冒頭で言及した「prohibits」ルールについて軽く触れておきます。

「prohibits」ルールは、フィールドが存在し空でない場合、全ての他のフィールドは存在しないか、空であることをバリデートします。

// 例:question_Aに回答した場合は、question_Bへの回答は禁止

// FormRequestクラス
public function rules(): array
{
    return [
        'question_A' => ['prohibits:question_B'],
    ];
}
// リクエスト
$attribute = [
    'question_A' => 'aaa',
    'question_B' => 'bbb'
];

$response = $this->json( 'post', "api/test", $attribute );

$response->assertStatus(422); // PASS。バリデーションが通らない

最後に

今回は、Laravel 9.49で追加されたバリデーションルール「missing」を既存の「prohibited」と併せて紹介しました。 実装にふさわしいバリデーションを適切に選択するためにも、既存ルールとの違いをしっかり把握しておきたいですね。 以上となります。ありがとうございました。

TOPに戻る