イントロダクション
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);
}
}