Laravel 10.x Precognition

イントロダクション

Laravel Precognition(プリコグニション:予知)により、将来のHTTPリクエストの結果を予測できます。Precognitionの主な使用例の1つは、アプリケーションのバックエンドのバリデーションルールを複製せずとも、フロントエンドのJavaScriptアプリケーションの「ライブ」バリデーションを提供する能力です。Precognitionは、LaravelのInertiaベースのスターターキットと特に相性がよいです。

Laravelが「事前認識型リクエスト」を受け取ると、ルートのミドルウェアをすべて実行し、フォームリクエストのバリデーションを含む、ルートのコントローラの依存解決を行いますが、実際にはルートのコントローラメソッドを実行しません。

ライブバリデーション

Vueの使用

Laravel Precognitionを使用すると、フロントエンドのVueアプリケーションでバリデーションルールを複製することなく、ユーザーにライブバリデーション体験を提供できます。その仕組みを説明するため、アプリケーション内で新規ユーザーを作成するためのフォームを作成してみましょう。

最初に、ルートに対するPrecognitionを有効にするには、ルート定義へHandlePrecognitiveRequestsミドルウェアを追加する必要があります。さらに、ルートのバリデーションルールを格納するため、フォームリクエストを作成する必要もあります。

use App\Http\Requests\CreateUserRequest;
use Illuminate\Foundation\Http\Middleware\HandlePrecognitiveRequests;

Route::post('/users', function (CreateUserRequest $request) {
    // ...
})->middleware([HandlePrecognitiveRequests::class]);

次に、Vue用のLaravel PrecognitionフロントエンドヘルパをNPMにより、インストールします。

npm install laravel-precognition-vue

LaravelのPrecognitionパッケージをインストールすれば、PrecognitionのuseForm関数を使用して、HTTPメソッド(post)、ターゲットURL(/users)、初期フォームデータを指定して、フォームオブジェクトを作成できるようになります。

次に、ライブバリデーションを有効にするには、各入力の change イベントでフォームのvalidateメソッドを起動し、入力名を指定します。

<script setup>
import { useForm } from 'laravel-precognition-vue';

const form = useForm('post', '/users', {
    name: '',
    email: '',
});

const submit = () => form.submit();
</script>

<template>
    <form @submit.prevent="submit">
        <label for="name">Name</label>
        <input
            id="name"
            v-model="form.name"
            @change="form.validate('name')"
        />
        <div v-if="form.invalid('name')">
            {{ form.errors.name }}
        </div>

        <label for="email">Email</label>
        <input
            id="email"
            type="email"
            v-model="form.email"
            @change="form.validate('email')"
        />
        <div v-if="form.invalid('email')">
            {{ form.errors.email }}
        </div>

        <button>Create User</button>
    </form>
</template>

これで、フォームがユーザーにより入力されると、Precognitionはルートのフォームリクエストのバリデーションルールに従い、ライブバリデーション出力を提供します。フォームの入力が変更されると、デバウンスされた「事前認識型」バリデーションリクエストをLaravelアプリケーションへ送信します。デバウンスのタイムアウトは、フォームの setValidationTimeout 関数を呼び出すことで設定できます。

form.setValidationTimeout(3000);

バリデーション要求がやり取り中の場合、フォームのvalidatingプロパティはtrue になります。

<div v-if="form.validating">
    Validating...
</div>

バリデーションリクエストやフォーム送信時に返される全てのバリデーションエラーは、自動的にフォームのerrorsオブジェクトへ格納します。

<div v-if="form.invalid('email')">
    {{ form.errors.email }}
</div>

フォームにエラーがあるかは、フォームのhasErrorsプロパティで判断できます。

<div v-if="form.hasErrors">
    <!-- ... -->
</div>

また、入力がバリデーションに合格したか失敗したかを判断するには、入力名をフォームのvalid関数かinvalid関数へ渡してください。

<span v-if="form.valid('email')">
    ✅
</span>

<span v-else-if="form.invalid('email')">
    ❌
</span>

Warning!! フォーム入力が変更され、バリデーションレスポンスを受信した時点で、初めて有効または無効として表示されます。

もちろん、フォーム送信に対するレスポンスに反応してコードを実行することもできます。フォームのsubmit関数は、AxiosのリクエストPromiseを返します。これは、レスポンスペイロードへのアクセス、送信成功時のフォーム入力のリセット、または失敗したリクエストの処理に便利な方法を提供します。

const submit = () => form.submit()
    .then(response => {
        form.reset();

        alert('User created.');
    })
    .catch(error => {
        alert('An error occurred.');
    });

VueとInertiaの使用

Note: VueとInertiaを使ってLaravelアプリケーションを開発するとき、有利に開始したい場合は、スターターキットの一つを使うことを検討してください。Laravelのスターターキットは、新しいLaravelアプリケーションにバックエンドとフロントエンドの認証へスカフォールドを提供します。

VueとInertiaと一緒にPrecognitionを使用する前に、より一般的なVueでPrecognitionを使用するドキュメントを必ず確認してください。VueをInertiaで使用する場合、NPM経由でInertia互換のPrecognitionライブラリーをインストールする必要があります。

npm install laravel-precognition-vue-inertia

一度インストールしたら、PrecognitionのuseForm関数は、上で説明したバリデーション機能で強化した、Inertiaフォームヘルパを返します。

フォームヘルパのsubmitメソッドが効率化され、HTTPメソッドやURLを指定する必要がなくなりました。その代わりに、Inertiaのvisitオプションを最初で唯一の引数として渡してください。また、submitメソッドは、上記のVueの例で見られるように、Promiseを返すことはありません。代わりに、Inertiaがサポートしているイベントコールバックsubmitメソッドへ指定するvisitオプションに指定します。

<script setup>
import { useForm } from 'laravel-precognition-vue-inertia';

const form = useForm('post', '/users', {
    name: '',
    email: '',
});

const submit = () => form.submit({
    preserveScroll: true,
    onSuccess: () => form.reset(),
});
</script>

Reactの使用

Laravel Precognitionを使用すると、フロントエンドのReactアプリケーションへバリデーションルールを複製することなく、ユーザーにライブバリデーション体験を提供できます。その仕組みを説明するため、アプリケーション内で新規ユーザーを作成するためのフォームを作成してみましょう。

最初に、ルートでPrecognitionを有効にするには、ルート定義でHandlePrecognitiveRequestsミドルウェアを追加する必要があります。また、ルートのバリデーションルールを格納するために、フォームrequestを作成する必要があります。

use App\Http\Requests\CreateUserRequest;
use Illuminate\Foundation\Http\Middleware\HandlePrecognitiveRequests;

Route::post('/users', function (CreateUserRequest $request) {
    // ...
})->middleware([HandlePrecognitiveRequests::class]);

次に、React用のLaravel PrecognitionフロントエンドヘルパをNPMによりインストールします。

npm install laravel-precognition-react

LaravelのPrecognitionパッケージをインストールすれば、PrecognitionのuseForm関数を使用して、HTTPメソッド(post)、ターゲットURL(/users)、初期フォームデータを指定し、フォームオブジェクトを作成できます。

ライブバリデーションを有効にするには、各入力のchangeイベントとblurイベントをリッスンする必要があります。changeイベントハンドラでは、setData関数でフォームのデータをセットし、入力名と新しい値を渡します。次に、blurイベントハンドラで、入力名を指定し、フォームのvalidateメソッドを呼び出します。

import { useForm } from 'laravel-precognition-react';

export default function Form() {
    const form = useForm('post', '/users', {
        name: '',
        email: '',
    });

    const submit = (e) => {
        e.preventDefault();

        form.submit();
    };

    return (
        <form onSubmit={submit}>
            <label for="name">Name</label>
            <input
                id="name"
                value={form.data.name}
                onChange={(e) => form.setData('name', e.target.value)}
                onBlur={() => form.validate('name')}
            />
            {form.invalid('name') ? (<div>{form.errors.name}</div>) : null}

            <label for="email">Email</label>
            <input
                id="email"
                value={form.data.email}
                onChange={(e) => form.setData('email', e.target.value)}
                onBlur={() => form.validate('email')}
            />
            {form.invalid('email') ? (<div>{form.errors.email}</div>) : null}

            <button>Create User</button>
        </form>
    );
};

フォームがユーザーにより入力されると、Precognitionはルートのフォームリクエストのバリデーションルールにより、ライブバリデーション出力を提供します。フォームの入力が変更されると、デバウンスされた「事前認識型」バリデーションリクエストをLaravelアプリケーションへ送信します。デバウンスのタイムアウトは、フォームのsetValidationTimeout関数を呼び出し設定します。

form.setValidationTimeout(3000);

バリデーション要求がやり取り中の場合、フォームのvalidatingプロパティはtrue になります。

{form.validating ? (<div>Validating...</div>) : null}

バリデーションリクエストやフォーム送信時に返される全てのバリデーションエラーは、自動的にフォームのerrorsオブジェクトへ格納します。

{form.invalid('email') ? (<div>{form.errors.email}</div>) : null}

フォームにエラーがあるかは、フォームのhasErrorsプロパティで判断できます。

{form.hasErrors ? (<div><!-- ... --></div>) : null}

また、入力がバリデーションに合格したか失敗したかを判断するには、入力名をフォームのvalid関数かinvalid関数へ渡してください。

{form.valid('email') ? (<span>✅</span>) : null}

{form.invalid('email') ? (<span>❌</span>) : null}

Warning!! フォーム入力が変更され、バリデーションレスポンスを受信した時点で、初めて有効または無効として表示されます。

もちろん、フォーム送信に対するレスポンスに反応してコードを実行することもできます。フォームのsubmit関数は、AxiosのリクエストPromiseを返します。これは、レスポンスペイロードへのアクセス、送信成功時のフォーム入力のリセット、または失敗したリクエストの処理に便利な方法を提供します。

const submit = (e) => {
    e.preventDefault();

    form.submit()
        .then(response => {
            form.reset();

            alert('User created.');
        })
        .catch(error => {
            alert('An error occurred.');
        });
};

ReactとInertiaの使用

Note: ReactとInertiaを使ってLaravelアプリケーションを開発するとき、有利に開始したい場合は、スターターキットの一つを使うことを検討してください。Laravelのスターターキットは、新しいLaravelアプリケーションにバックエンドとフロントエンドの認証へスカフォールドを提供します。

ReactとInertiaと一緒にPrecognitionを使用する前に、より一般的なVueでPrecognitionを使用するドキュメントを必ず確認してください。VueをInertiaで使用する場合、NPM経由でInertia互換のPrecognitionライブラリーをインストールする必要があります。

npm install laravel-precognition-react-inertia

一度インストールしたら、PrecognitionのuseForm関数は、上で説明したバリデーション機能で強化した、Inertiaフォームヘルパを返します。

フォームヘルパのsubmitメソッドが効率化され、HTTPメソッドやURLを指定する必要がなくなりました。その代わりに、Inertiaのvisitオプションを最初で唯一の引数として渡してください。また、submitメソッドは、上記のReactの例で見られるように、Promiseを返すことはありません。代わりに、Inertiaがサポートしているイベントコールバックsubmitメソッドへ指定するvisitオプションに指定します。

import { useForm } from 'laravel-precognition-react-inertia';

const form = useForm('post', '/users', {
    name: '',
    email: '',
});

const submit = (e) => {
    e.preventDefault();

    form.submit({
        preserveScroll: true,
        onSuccess: () => form.reset(),
    });
};

バリデーションルールのカスタマイズ

リクエストのisPrecognitiveメソッドを使用すれば、事前認識型リクエスト時に実行するバリデーションルールをカスタマイズできます。

例えば、ユーザー作成フォームにおいて、最終的なフォーム送信時にのみ、パスワードの「データ漏洩」をバリデートしたい場合があります。事前認識型のバリデーションリクエストでは、パスワードが必須であることと、最低8文字であることをバリデートします。isPrecognitiveメソッドを使用すると、フォームリクエストで定義されたルールをカスタマイズできます。

<?php

namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Validation\Rules\Password;

class StoreUserRequest extends FormRequest
{
    /**
     * リクエストへ適用するバリデーションルールの取得
     *
     * @return array
     */
    protected function rules()
    {
        return [
            'password' => [
                'required',
                $this->isPrecognitive()
                    ? Password::min(8)
                    : Password::min(8)->uncompromised(),
            ],
            // ...
        ];
    }
}

副作用の管理

HandlePrecognitiveRequestsミドルウェアをルートに追加する場合、他のミドルウェアで事前認識型リクエスト時にスキップすべき副作用があるかどうかを検討する必要があります。

例えば、各ユーザーがアプリケーションとやり取りした「操作」の総数を増加させるミドルウェアがあり、事前認識型リクエストは操作としてカウントしたくない場合です。これを実現するには、操作数を増やす前にリクエストのisPrecognitiveメソッドをチェックする必要があるでしょう。

<?php

namespace App\Http\Middleware;

use App\Facades\Interaction;
use Closure;
use Illuminate\Http\Request;

class InteractionMiddleware
{
    /**
     * 受信リクエストの処理
     */
    public function handle(Request $request, Closure $next): mixed
    {
        if (! $request->isPrecognitive()) {
            Interaction::incrementFor($request->user());
        }

        return $next($request);
    }
}

ドキュメント章別ページ

ヘッダー項目移動

注目:アイコン:ページ内リンク設置(リンクがないヘッダーへの移動では、リンクがある以前のヘッダーのハッシュをURLへ付加します。

移動

クリックで即時移動します。

設定

適用ボタンクリック後に、全項目まとめて適用されます。

カラーテーマ
和文指定 Pagination
和文指定 Scaffold
Largeスクリーン表示幅
インデント
本文フォント
コードフォント
フォント適用確認

フォントの指定フィールドから、フォーカスが外れると、当ブロックの内容に反映されます。EnglishのDisplayもPreviewしてください。

フォント設定時、表示に不具合が出た場合、当サイトのクッキーを削除してください。

バックスラッシュを含むインライン\Code\Blockの例です。

以下はコードブロックの例です。

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class User extends Model
{
    /**
     * ユーザに関連する電話レコードを取得
     */
    public function phone()
    {
        return $this->hasOne('App\Phone');
    }
}

設定を保存する前に、表示が乱れないか必ず確認してください。CSSによるフォントファミリー指定の知識がない場合は、フォントを変更しないほうが良いでしょう。

キーボード・ショートカット

オープン操作

PDC

ページ(章)移動の左オフキャンバスオープン

HA

ヘッダー移動モーダルオープン

MS

移動/設定の右オフキャンバスオープン

ヘッダー移動

T

最初のヘッダーへ移動

E

最後のヘッダーへ移動

NJ

次ヘッダー(H2〜H4)へ移動

BK

前ヘッダー(H2〜H4)へ移動

その他

?

このヘルプページ表示
閉じる