予知 (Precognition)
導入 (Introduction)
Laravel Precognition を使用すると、将来の HTTP リクエストの結果を予測できます。 Precognition の主な使用例の 1 つは、アプリケーションのバックエンド検証ルールを複製することなく、フロントエンド JavaScript アプリケーションに「ライブ」検証を提供できることです。 Precognition は、Laravel の Inertia ベースの スターターキット と特によく合います。
Laravel が「事前認識リクエスト」を受信すると、ルートのすべてのミドルウェアが実行され、フォームリクエスト の検証を含むルートのコントローラの依存関係が解決されますが、実際にはルートのコントローラ メソッドは実行されません。
ライブ検証 (Live Validation)
Vueの使用
Laravel Precognition を使用すると、フロントエンド Vue アプリケーションで検証ルールを複製することなく、ライブ検証エクスペリエンスをユーザーに提供できます。その仕組みを説明するために、アプリケーション内で新しいユーザーを作成するためのフォームを構築してみましょう。
まず、ルートの事前認識を有効にするには、HandlePrecognitiveRequests ミドルウェアをルート定義に追加する必要があります。ルートの検証ルールを格納する フォームリクエスト も作成する必要があります。
use App\Http\Requests\StoreUserRequest;
use Illuminate\Foundation\Http\Middleware\HandlePrecognitiveRequests;
Route::post('/users', function (StoreUserRequest $request) {
// ...
})->middleware([HandlePrecognitiveRequests::class]);
次に、NPM 経由で Vue 用の Laravel Precognition フロントエンド ヘルパをインストールする必要があります。
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 :disabled="form.processing">
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>
フォーム入力は、変更されて検証応答が受信された場合にのみ、有効または無効として表示されます。
Precognition を使用してフォームの入力のサブセットを検証している場合、エラーを手動でクリアすると便利な場合があります。これを実現するには、フォームの forgetError 関数を使用できます。
<input
id="avatar"
type="file"
@change="(e) => {
form.avatar = e.target.files[0]
form.forgetError('avatar')
}"
>
もちろん、フォーム送信に対する応答に応じてコードを実行することもできます。フォームの submit 関数は、Axios リクエストの Promise を返します。これにより、応答ペイロードにアクセスしたり、送信成功時にフォーム入力をリセットしたり、失敗したリクエストを処理したりするための便利な方法が提供されます。
const submit = () => form.submit()
.then(response => {
form.reset();
alert('User created.');
})
.catch(error => {
alert('An error occurred.');
});
フォームの processing プロパティを検査することで、フォーム送信リクエストが処理中かどうかを判断できます。
<button :disabled="form.processing">
Submit
</button>
Vue と Inertia の使用
Vue と Inertia を使用して Laravel アプリケーションを開発する際に有利なスタートを切りたい場合は、スターターキット のいずれかの使用を検討してください。 Laravel のスターター キットは、新しい Laravel アプリケーションにバックエンドおよびフロントエンドの認証スキャフォールディングを提供します。
Vue および Inertia で Precognition を使用する前に、Vue での Precognition の使用 の一般ドキュメントを必ず確認してください。 Inertia で Vue を使用する場合は、NPM 経由で Inertia 互換の Precognition ライブラリをインストールする必要があります。
npm install laravel-precognition-vue-inertia
インストールされると、Precognition の useForm 関数は、上で説明した検証機能で強化された Inertia フォームヘルパ を返します。
フォーム ヘルパの submit メソッドが合理化され、HTTP メソッドまたは URL を指定する必要がなくなりました。代わりに、Inertia の 訪問オプション を最初で唯一の引数として渡すことができます。さらに、上記の Vue の例にあるように、submit メソッドは Promise を返しません。代わりに、submit メソッドに指定される訪問オプションで、Inertia がサポートする イベントコールバック のいずれかを指定できます。
<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>
反応の使用
Laravel Precognition を使用すると、フロントエンド React アプリケーションで検証ルールを複製することなく、ライブ検証エクスペリエンスをユーザーに提供できます。その仕組みを説明するために、アプリケーション内で新しいユーザーを作成するためのフォームを構築してみましょう。
まず、ルートの事前認識を有効にするには、HandlePrecognitiveRequests ミドルウェアをルート定義に追加する必要があります。ルートの検証ルールを格納する フォームリクエスト も作成する必要があります。
use App\Http\Requests\StoreUserRequest;
use Illuminate\Foundation\Http\Middleware\HandlePrecognitiveRequests;
Route::post('/users', function (StoreUserRequest $request) {
// ...
})->middleware([HandlePrecognitiveRequests::class]);
次に、NPM 経由で React 用の Laravel Precognition フロントエンド ヘルパをインストールする必要があります。
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>}
<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>}
<button disabled={form.processing}>
Create User
</button>
</form>
);
};
ユーザーがフォームに入力すると、Precognition はルートのフォーム リクエスト内の検証ルールを活用したライブ検証出力を提供します。フォームの入力が変更されると、デバウンスされた「プリコグニティブ」検証リクエストが Laravel アプリケーションに送信されます。フォームの setValidationTimeout 関数を呼び出すことで、デバウンス タイムアウトを構成できます。
form.setValidationTimeout(3000);
検証リクエストが進行中の場合、フォームの validating プロパティは true になります。
{form.validating && <div>Validating...</div>}
検証リクエストまたはフォームの送信中に返された検証エラーは、フォームの errors オブジェクトに自動的に設定されます。
{form.invalid('email') && <div>{form.errors.email}</div>}
フォームの hasErrors プロパティを使用して、フォームにエラーがあるかどうかを確認できます。
{form.hasErrors && <div><!-- ... --></div>}
入力の名前をフォームの valid 関数と invalid 関数にそれぞれ渡すことで、入力が検証に合格したか失敗したかを判断することもできます。
{form.valid('email') && <span>✅</span>}
{form.invalid('email') && <span>❌</span>}
フォーム入力は、変更されて検証応答が受信された場合にのみ、有効または無効として表示されます。
Precognition を使用してフォームの入力のサブセットを検証している場合、エラーを手動でクリアすると便利な場合があります。これを実現するには、フォームの forgetError 関数を使用できます。
<input
id="avatar"
type="file"
onChange={(e) =>
form.setData('avatar', e.target.value);
form.forgetError('avatar');
}
>
もちろん、フォーム送信に対する応答に応じてコードを実行することもできます。フォームの submit 関数は、Axios リクエストの Promise を返します。これにより、応答ペイロードにアクセスしたり、フォーム送信が成功したときにフォームの入力をリセットしたり、失敗したリクエストを処理したりするための便利な方法が提供されます。
const submit = (e) => {
e.preventDefault();
form.submit()
.then(response => {
form.reset();
alert('User created.');
})
.catch(error => {
alert('An error occurred.');
});
};
フォームの processing プロパティを検査することで、フォーム送信リクエストが処理中かどうかを判断できます。
<button disabled={form.processing}>
Submit
</button>
React と Inertia の使用
React と Inertia を使用して Laravel アプリケーションを開発する際に有利なスタートを切りたい場合は、スターターキット のいずれかの使用を検討してください。 Laravel のスターター キットは、新しい Laravel アプリケーションにバックエンドおよびフロントエンドの認証スキャフォールディングを提供します。
React および Inertia で Precognition を使用する前に、React で Precognition を使用する の一般ドキュメントを必ず確認してください。 React with Inertia を使用する場合は、NPM 経由で Inertia 互換の Precognition ライブラリをインストールする必要があります。
npm install laravel-precognition-react-inertia
インストールされると、Precognition の useForm 関数は、上で説明した検証機能で強化された Inertia フォームヘルパ を返します。
フォーム ヘルパの submit メソッドが合理化され、HTTP メソッドまたは URL を指定する必要がなくなりました。代わりに、Inertia の 訪問オプション を最初で唯一の引数として渡すことができます。さらに、上記の React の例に見られるように、submit メソッドは Promise を返しません。代わりに、submit メソッドに指定される訪問オプションで、Inertia がサポートする イベントコールバック のいずれかを指定できます。
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(),
});
};
Alpine と Blade の使用
Laravel Precognition を使用すると、フロントエンド Alpine アプリケーションで検証ルールを複製することなく、ライブ検証エクスペリエンスをユーザーに提供できます。その仕組みを説明するために、アプリケーション内で新しいユーザーを作成するためのフォームを構築してみましょう。
まず、ルートの事前認識を有効にするには、HandlePrecognitiveRequests ミドルウェアをルート定義に追加する必要があります。ルートの検証ルールを格納する フォームリクエスト も作成する必要があります。
use App\Http\Requests\CreateUserRequest;
use Illuminate\Foundation\Http\Middleware\HandlePrecognitiveRequests;
Route::post('/users', function (CreateUserRequest $request) {
// ...
})->middleware([HandlePrecognitiveRequests::class]);
次に、NPM 経由で Alpine 用の Laravel Precognition フロントエンド ヘルパをインストールする必要があります。
npm install laravel-precognition-alpine
次に、resources/js/app.js ファイルで Precognition プラグインを Alpine に登録します。
import Alpine from 'alpinejs';
import Precognition from 'laravel-precognition-alpine';
window.Alpine = Alpine;
Alpine.plugin(Precognition);
Alpine.start();
Laravel Precognition パッケージをインストールして登録すると、Precognition の $form "マジック" を使用してフォーム オブジェクトを作成し、HTTP メソッド (post)、ターゲット URL (/users)、および初期フォーム データを提供できるようになります。
ライブ検証を有効にするには、フォームのデータを関連する入力にバインドし、各入力の change イベントをリッスンする必要があります。 change イベント ハンドラーでは、入力の名前を指定してフォームの validate メソッドを呼び出す必要があります。
<form x-data="{
form: $form('post', '/register', {
name: '',
email: '',
}),
}">
@csrf
<label for="name">Name</label>
<input
id="name"
name="name"
x-model="form.name"
@change="form.validate('name')"
/>
<template x-if="form.invalid('name')">
<div x-text="form.errors.name"></div>
</template>
<label for="email">Email</label>
<input
id="email"
name="email"
x-model="form.email"
@change="form.validate('email')"
/>
<template x-if="form.invalid('email')">
<div x-text="form.errors.email"></div>
</template>
<button :disabled="form.processing">
Create User
</button>
</form>
ユーザーがフォームに入力すると、Precognition はルートのフォーム リクエスト内の検証ルールを活用したライブ検証出力を提供します。フォームの入力が変更されると、デバウンスされた「プリコグニティブ」検証リクエストが Laravel アプリケーションに送信されます。フォームの setValidationTimeout 関数を呼び出すことで、デバウンス タイムアウトを構成できます。
form.setValidationTimeout(3000);
検証リクエストが進行中の場合、フォームの validating プロパティは true になります。
<template x-if="form.validating">
<div>Validating...</div>
</template>
検証リクエストまたはフォームの送信中に返された検証エラーは、フォームの errors オブジェクトに自動的に設定されます。
<template x-if="form.invalid('email')">
<div x-text="form.errors.email"></div>
</template>
フォームの hasErrors プロパティを使用して、フォームにエラーがあるかどうかを確認できます。
<template x-if="form.hasErrors">
<div><!-- ... --></div>
</template>
入力の名前をフォームの valid 関数と invalid 関数にそれぞれ渡すことで、入力が検証に合格したか失敗したかを判断することもできます。
<template x-if="form.valid('email')">
<span>✅</span>
</template>
<template x-if="form.invalid('email')">
<span>❌</span>
</template>
フォーム入力は、変更されて検証応答が受信された場合にのみ、有効または無効として表示されます。
フォームの processing プロパティを検査することで、フォーム送信リクエストが処理中かどうかを判断できます。
<button :disabled="form.processing">
Submit
</button>
古いフォームデータを再入力する
上で説明したユーザー作成の例では、Precognition を使用してライブ検証を実行しています。ただし、フォームを送信するために従来のサーバー側のフォーム送信を実行しています。したがって、サーバー側のフォーム送信から返された「古い」入力エラーと検証エラーをフォームに入力する必要があります。
<form x-data="{
form: $form('post', '/register', {
name: '{{ old('name') }}',
email: '{{ old('email') }}',
}).setErrors({{ Js::from($errors->messages()) }}),
}">
あるいは、XHR 経由でフォームを送信したい場合は、Axios リクエスト Promise を返すフォームの submit 関数を使用することもできます。
<form
x-data="{
form: $form('post', '/register', {
name: '',
email: '',
}),
submit() {
this.form.submit()
.then(response => {
form.reset();
alert('User created.')
})
.catch(error => {
alert('An error occurred.');
});
},
}"
@submit.prevent="submit"
>
Axiosの構成
Precognition 検証ライブラリは、Axios HTTP クライアントを使用して、アプリケーションのバックエンドにリクエストを送信します。便宜上、アプリケーションで必要に応じて Axios インスタンスをカスタマイズできます。たとえば、laravel-precognition-vue ライブラリを使用する場合、アプリケーションの resources/js/app.js ファイル内の各送信リクエストに追加のリクエスト ヘッダーを追加できます。
import { client } from 'laravel-precognition-vue';
client.axios().defaults.headers.common['Authorization'] = authToken;
または、アプリケーション用に構成された Axios インスタンスがすでにある場合は、代わりにそのインスタンスを使用するように Precognition に指示することもできます。
import Axios from 'axios';
import { client } from 'laravel-precognition-vue';
window.axios = Axios.create()
window.axios.defaults.headers.common['Authorization'] = authToken;
client.use(window.axios)
Inertia フレーバーの Precognition ライブラリは、検証リクエストに構成された Axios インスタンスのみを使用します。フォームの送信は常に Inertia によって送信されます。
検証ルールのカスタマイズ (Customizing Validation Rules)
リクエストの isPrecognitive メソッドを使用して、予測リクエスト中に実行される検証ルールをカスタマイズできます。
たとえば、ユーザー作成フォームでは、最終的なフォーム送信時にのみパスワードが「侵害されていない」ことを検証したい場合があります。予知的検証リクエストの場合、パスワードが必須であり、最低 8 文字であることを単純に検証します。 isPrecognitive メソッドを使用すると、フォーム リクエストで定義されたルールをカスタマイズできます。
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Validation\Rules\Password;
class StoreUserRequest extends FormRequest
{
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
protected function rules()
{
return [
'password' => [
'required',
$this->isPrecognitive()
? Password::min(8)
: Password::min(8)->uncompromised(),
],
// ...
];
}
}
ファイルのアップロードの処理 (Handling File Uploads)
デフォルトでは、Laravel Precognition は、事前認識検証リクエスト中にファイルをアップロードまたは検証しません。これにより、大きなファイルが不必要に複数回アップロードされることがなくなります。
この動作のため、フィールドを指定するアプリケーション 対応するフォームリクエストの検証ルールをカスタマイズします が完全なフォーム送信の場合にのみ必要であることを確認する必要があります。
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
protected function rules()
{
return [
'avatar' => [
...$this->isPrecognitive() ? [] : ['required'],
'image',
'mimes:jpg,png'
'dimensions:ratio=3/2',
],
// ...
];
}
すべての検証リクエストにファイルを含めたい場合は、クライアント側のフォーム インスタンスで validateFiles 関数を呼び出すことができます。
form.validateFiles();
副作用の管理 (Managing Side-Effects)
HandlePrecognitiveRequests ミドルウェアをルートに追加するときは、予測リクエスト中にスキップする必要がある他のミドルウェアに副作用があるかどうかを考慮する必要があります。
たとえば、各ユーザーがアプリケーションと行う「インタラクション」の合計数を増加させるミドルウェアがある場合でも、事前認識リクエストをインタラクションとしてカウントしたくない場合があります。これを実現するには、インタラクション数を増やす前に、リクエストの isPrecognitive メソッドをチェックします。
<?php
namespace App\Http\Middleware;
use App\Facades\Interaction;
use Closure;
use Illuminate\Http\Request;
class InteractionMiddleware
{
/**
* Handle an incoming request.
*/
public function handle(Request $request, Closure $next): mixed
{
if (! $request->isPrecognitive()) {
Interaction::incrementFor($request->user());
}
return $next($request);
}
}
テスト (Testing)
テストで事前認識リクエストを作成したい場合、Laravel の TestCase には、Precognition リクエストヘッダーを追加する withPrecognition ヘルパが含まれています。
さらに、予知的リクエストが成功したことを主張したい場合、たとえば検証エラーを返さなかった場合は、レスポンスで assertSuccessfulPrecognition メソッドを使用できます。
public function test_it_validates_registration_form_with_precognition()
{
$response = $this->withPrecognition()
->post('/register', [
'name' => 'Taylor Otwell',
]);
$response->assertSuccessfulPrecognition();
$this->assertSame(0, User::count());
}