Precognition
イントロダクション
Laravel Precognitionを使用すると、将来のHTTPリクエストの結果を予測できます。Precognitionの主なユースケースの1つは、アプリケーションのバックエンドのバリデーションルールを複製することなく、フロントエンドのJavaScriptアプリケーションに「ライブ」バリデーションを提供する機能です。Precognitionは、特にLaravelのInertiaベースのスターターキットと相性が良いです。
Laravelが「Precognitiveリクエスト」を受信すると、ルートのすべてのミドルウェアを実行し、フォームリクエストのバリデーションを含む、ルートのコントローラの依存関係を解決しますが、実際にはルートのコントローラメソッドを実行しません。
ライブバリデーション
Vueの使用
Laravel Precognitionを使用することで、フロントエンドのVueアプリケーションでバリデーションルールを複製することなく、ユーザーにライブバリデーション体験を提供できます。その仕組みを説明するために、アプリケーション内に新規ユーザーを作成するためのフォームを構築してみましょう。
まず、ルートでPrecognitionを有効にするには、HandlePrecognitiveRequestsミドルウェアをルート定義に追加する必要があります。また、ルートのバリデーションルールを格納するためにフォームリクエストを作成する必要があります。
1use App\Http\Requests\StoreUserRequest;2use Illuminate\Foundation\Http\Middleware\HandlePrecognitiveRequests;3 4Route::post('/users', function (StoreUserRequest $request) {5 // ...6})->middleware([HandlePrecognitiveRequests::class]);
次に、NPMを介してVue用のLaravel Precognitionフロントエンドヘルパーをインストールする必要があります。
1npm install laravel-precognition-vue
Laravel Precognitionパッケージがインストールされたら、PrecognitionのuseForm関数を使用してフォームオブジェクトを作成できます。HTTPメソッド(post)、ターゲットURL(/users)、および初期フォームデータを指定します。
次に、ライブバリデーションを有効にするには、各入力のchangeイベントでフォームのvalidateメソッドを呼び出し、入力の名前を指定します。
1<script setup> 2import { useForm } from 'laravel-precognition-vue'; 3 4const form = useForm('post', '/users', { 5 name: '', 6 email: '', 7}); 8 9const submit = () => form.submit();10</script>11 12<template>13 <form @submit.prevent="submit">14 <label for="name">Name</label>15 <input16 id="name"17 v-model="form.name"18 @change="form.validate('name')"19 />20 <div v-if="form.invalid('name')">21 {{ form.errors.name }}22 </div>23 24 <label for="email">Email</label>25 <input26 id="email"27 type="email"28 v-model="form.email"29 @change="form.validate('email')"30 />31 <div v-if="form.invalid('email')">32 {{ form.errors.email }}33 </div>34 35 <button :disabled="form.processing">36 Create User37 </button>38 </form>39</template>
これで、ユーザーがフォームに入力するにつれて、Precognitionはルートのフォームリクエスト内のバリデーションルールに基づいたライブバリデーションの出力を提供します。フォームの入力が変更されると、デバウンスされた「Precognitive」バリデーションリクエストがLaravelアプリケーションに送信されます。デバウンスのタイムアウトは、フォームのsetValidationTimeout関数を呼び出すことで設定できます。
1form.setValidationTimeout(3000);
バリデーションリクエストが進行中の場合、フォームのvalidatingプロパティはtrueになります。
1<div v-if="form.validating">2 Validating...3</div>
バリデーションリクエスト中またはフォーム送信中に返されたバリデーションエラーは、フォームのerrorsオブジェクトに自動的に入力されます。
1<div v-if="form.invalid('email')">2 {{ form.errors.email }}3</div>
フォームにエラーがあるかどうかは、フォームのhasErrorsプロパティを使用して判断できます。
1<div v-if="form.hasErrors">2 <!-- ... -->3</div>
また、入力がバリデーションに合格したか失敗したかを判断するには、フォームのvalid関数とinvalid関数にそれぞれの入力の名前を渡します。
1<span v-if="form.valid('email')">2 ✅3</span>4 5<span v-else-if="form.invalid('email')">6 ❌7</span>
フォームの入力は、変更され、バリデーションレスポンスが受信された後にのみ、有効または無効として表示されます。
Precognitionでフォームの入力の一部を検証している場合、手動でエラーをクリアすると便利な場合があります。これを実現するには、フォームのforgetError関数を使用できます。
1<input2 id="avatar"3 type="file"4 @change="(e) => {5 form.avatar = e.target.files[0]6 7 form.forgetError('avatar')8 }"9>
これまで見てきたように、入力のchangeイベントにフックし、ユーザーが操作するにつれて個々の入力を検証できます。しかし、ユーザーがまだ操作していない入力を検証する必要がある場合もあります。これは「ウィザード」を構築する際によくあることで、次のステップに進む前に、ユーザーが操作したかどうかに関わらず、表示されているすべての入力を検証したい場合です。
Precognitionでこれを実行するには、validateメソッドを呼び出し、検証したいフィールド名をonly設定キーに渡す必要があります。バリデーションの結果は、onSuccessまたはonValidationErrorコールバックで処理できます。
1<button2 type="button"3 @click="form.validate({4 only: ['name', 'email', 'phone'],5 onSuccess: (response) => nextStep(),6 onValidationError: (response) => /* ... */,7 })"8>Next Step</button>
もちろん、フォーム送信へのレスポンスに応じてコードを実行することもできます。フォームのsubmit関数はAxiosリクエストのPromiseを返します。これにより、レスポンスのペイロードにアクセスしたり、送信が成功したときにフォームの入力をリセットしたり、失敗したリクエストを処理したりする便利な方法が提供されます。
1const submit = () => form.submit()2 .then(response => {3 form.reset();4 5 alert('User created.');6 })7 .catch(error => {8 alert('An error occurred.');9 });
フォーム送信リクエストが進行中であるかどうかは、フォームのprocessingプロパティを調べることで判断できます。
1<button :disabled="form.processing">2 Submit3</button>
VueとInertiaの使用
VueとInertiaを使用してLaravelアプリケーションを開発する際に手早く始めたい場合は、私たちのスターターキットのいずれかを使用することを検討してください。Laravelのスターターキットは、新しいLaravelアプリケーションのバックエンドとフロントエンドの認証スカフォールディングを提供します。
VueとInertiaでPrecognitionを使用する前に、VueでのPrecognitionの使用に関する一般的なドキュメントを必ず確認してください。VueとInertiaを併用する場合は、NPMを介してInertia互換のPrecognitionライブラリをインストールする必要があります。
1npm install laravel-precognition-vue-inertia
インストールが完了すると、PrecognitionのuseForm関数は、上記で説明したバリデーション機能で拡張されたInertiaのフォームヘルパーを返します。
フォームヘルパーのsubmitメソッドは合理化されており、HTTPメソッドやURLを指定する必要がなくなりました。代わりに、Inertiaのvisit optionsを最初で唯一の引数として渡すことができます。さらに、submitメソッドは上記のVueの例で見たようなPromiseを返しません。代わりに、submitメソッドに与えられたvisit optionsで、Inertiaがサポートするイベントコールバックのいずれかを提供できます。
1<script setup> 2import { useForm } from 'laravel-precognition-vue-inertia'; 3 4const form = useForm('post', '/users', { 5 name: '', 6 email: '', 7}); 8 9const submit = () => form.submit({10 preserveScroll: true,11 onSuccess: () => form.reset(),12});13</script>
Reactの使用
Laravel Precognitionを使用することで、フロントエンドのReactアプリケーションでバリデーションルールを複製することなく、ユーザーにライブバリデーション体験を提供できます。その仕組みを説明するために、アプリケーション内に新規ユーザーを作成するためのフォームを構築してみましょう。
まず、ルートでPrecognitionを有効にするには、HandlePrecognitiveRequestsミドルウェアをルート定義に追加する必要があります。また、ルートのバリデーションルールを格納するためにフォームリクエストを作成する必要があります。
1use App\Http\Requests\StoreUserRequest;2use Illuminate\Foundation\Http\Middleware\HandlePrecognitiveRequests;3 4Route::post('/users', function (StoreUserRequest $request) {5 // ...6})->middleware([HandlePrecognitiveRequests::class]);
次に、NPMを介してReact用のLaravel Precognitionフロントエンドヘルパーをインストールする必要があります。
1npm install laravel-precognition-react
Laravel Precognitionパッケージがインストールされたら、PrecognitionのuseForm関数を使用してフォームオブジェクトを作成できます。HTTPメソッド(post)、ターゲットURL(/users)、および初期フォームデータを指定します。
ライブバリデーションを有効にするには、各入力のchangeイベントとblurイベントをリッスンする必要があります。changeイベントハンドラでは、setData関数を使用してフォームのデータを設定し、入力の名前と新しい値を渡します。次に、blurイベントハンドラでフォームのvalidateメソッドを呼び出し、入力の名前を指定します。
1import { useForm } from 'laravel-precognition-react'; 2 3export default function Form() { 4 const form = useForm('post', '/users', { 5 name: '', 6 email: '', 7 }); 8 9 const submit = (e) => {10 e.preventDefault();11 12 form.submit();13 };14 15 return (16 <form onSubmit={submit}>17 <label htmlFor="name">Name</label>18 <input19 id="name"20 value={form.data.name}21 onChange={(e) => form.setData('name', e.target.value)}22 onBlur={() => form.validate('name')}23 />24 {form.invalid('name') && <div>{form.errors.name}</div>}25 26 <label htmlFor="email">Email</label>27 <input28 id="email"29 value={form.data.email}30 onChange={(e) => form.setData('email', e.target.value)}31 onBlur={() => form.validate('email')}32 />33 {form.invalid('email') && <div>{form.errors.email}</div>}34 35 <button disabled={form.processing}>36 Create User37 </button>38 </form>39 );40};
これで、ユーザーがフォームに入力するにつれて、Precognitionはルートのフォームリクエスト内のバリデーションルールに基づいたライブバリデーションの出力を提供します。フォームの入力が変更されると、デバウンスされた「Precognitive」バリデーションリクエストがLaravelアプリケーションに送信されます。デバウンスのタイムアウトは、フォームのsetValidationTimeout関数を呼び出すことで設定できます。
1form.setValidationTimeout(3000);
バリデーションリクエストが進行中の場合、フォームのvalidatingプロパティはtrueになります。
1{form.validating && <div>Validating...</div>}
バリデーションリクエスト中またはフォーム送信中に返されたバリデーションエラーは、フォームのerrorsオブジェクトに自動的に入力されます。
1{form.invalid('email') && <div>{form.errors.email}</div>}
フォームにエラーがあるかどうかは、フォームのhasErrorsプロパティを使用して判断できます。
1{form.hasErrors && <div><!-- ... --></div>}
また、入力がバリデーションに合格したか失敗したかを判断するには、フォームのvalid関数とinvalid関数にそれぞれの入力の名前を渡します。
1{form.valid('email') && <span>✅</span>}2 3{form.invalid('email') && <span>❌</span>}
フォームの入力は、変更され、バリデーションレスポンスが受信された後にのみ、有効または無効として表示されます。
Precognitionでフォームの入力の一部を検証している場合、手動でエラーをクリアすると便利な場合があります。これを実現するには、フォームのforgetError関数を使用できます。
1<input2 id="avatar"3 type="file"4 onChange={(e) => {5 form.setData('avatar', e.target.value);6 7 form.forgetError('avatar');8 }}9>
これまで見てきたように、入力のblurイベントにフックし、ユーザーが操作するにつれて個々の入力を検証できます。しかし、ユーザーがまだ操作していない入力を検証する必要がある場合もあります。これは「ウィザード」を構築する際によくあることで、次のステップに進む前に、ユーザーが操作したかどうかに関わらず、表示されているすべての入力を検証したい場合です。
Precognitionでこれを実行するには、validateメソッドを呼び出し、検証したいフィールド名をonly設定キーに渡す必要があります。バリデーションの結果は、onSuccessまたはonValidationErrorコールバックで処理できます。
1<button2 type="button"3 onClick={() => form.validate({4 only: ['name', 'email', 'phone'],5 onSuccess: (response) => nextStep(),6 onValidationError: (response) => /* ... */,7 })}8>Next Step</button>
もちろん、フォーム送信へのレスポンスに応じてコードを実行することもできます。フォームのsubmit関数はAxiosリクエストのPromiseを返します。これにより、レスポンスのペイロードにアクセスしたり、フォーム送信が成功したときにフォームの入力をリセットしたり、失敗したリクエストを処理したりする便利な方法が提供されます。
1const submit = (e) => { 2 e.preventDefault(); 3 4 form.submit() 5 .then(response => { 6 form.reset(); 7 8 alert('User created.'); 9 })10 .catch(error => {11 alert('An error occurred.');12 });13};
フォーム送信リクエストが進行中であるかどうかは、フォームのprocessingプロパティを調べることで判断できます。
1<button disabled={form.processing}>2 Submit3</button>
ReactとInertiaの使用
ReactとInertiaを使用してLaravelアプリケーションを開発する際に手早く始めたい場合は、私たちのスターターキットのいずれかを使用することを検討してください。Laravelのスターターキットは、新しいLaravelアプリケーションのバックエンドとフロントエンドの認証スカフォールディングを提供します。
ReactとInertiaでPrecognitionを使用する前に、ReactでのPrecognitionの使用に関する一般的なドキュメントを必ず確認してください。ReactとInertiaを併用する場合は、NPMを介してInertia互換のPrecognitionライブラリをインストールする必要があります。
1npm install laravel-precognition-react-inertia
インストールが完了すると、PrecognitionのuseForm関数は、上記で説明したバリデーション機能で拡張されたInertiaのフォームヘルパーを返します。
フォームヘルパーのsubmitメソッドは合理化されており、HTTPメソッドやURLを指定する必要がなくなりました。代わりに、Inertiaのvisit optionsを最初で唯一の引数として渡すことができます。さらに、submitメソッドは上記のReactの例で見たようなPromiseを返しません。代わりに、submitメソッドに与えられたvisit optionsで、Inertiaがサポートするイベントコールバックのいずれかを提供できます。
1import { useForm } from 'laravel-precognition-react-inertia'; 2 3const form = useForm('post', '/users', { 4 name: '', 5 email: '', 6}); 7 8const submit = (e) => { 9 e.preventDefault();10 11 form.submit({12 preserveScroll: true,13 onSuccess: () => form.reset(),14 });15};
AlpineとBladeの使用
Laravel Precognitionを使用することで、フロントエンドのAlpineアプリケーションでバリデーションルールを複製することなく、ユーザーにライブバリデーション体験を提供できます。その仕組みを説明するために、アプリケーション内に新規ユーザーを作成するためのフォームを構築してみましょう。
まず、ルートでPrecognitionを有効にするには、HandlePrecognitiveRequestsミドルウェアをルート定義に追加する必要があります。また、ルートのバリデーションルールを格納するためにフォームリクエストを作成する必要があります。
1use App\Http\Requests\CreateUserRequest;2use Illuminate\Foundation\Http\Middleware\HandlePrecognitiveRequests;3 4Route::post('/users', function (CreateUserRequest $request) {5 // ...6})->middleware([HandlePrecognitiveRequests::class]);
次に、NPMを介してAlpine用のLaravel Precognitionフロントエンドヘルパーをインストールする必要があります。
1npm install laravel-precognition-alpine
次に、resources/js/app.jsファイルでPrecognitionプラグインをAlpineに登録します。
1import Alpine from 'alpinejs';2import Precognition from 'laravel-precognition-alpine';3 4window.Alpine = Alpine;5 6Alpine.plugin(Precognition);7Alpine.start();
Laravel Precognitionパッケージがインストールされ登録されたら、Precognitionの$form「マジック」を使用してフォームオブジェクトを作成できます。HTTPメソッド(post)、ターゲットURL(/users)、および初期フォームデータを指定します。
ライブバリデーションを有効にするには、フォームのデータを関連する入力にバインドし、各入力のchangeイベントをリッスンする必要があります。changeイベントハンドラでは、フォームのvalidateメソッドを呼び出し、入力の名前を指定します。
1<form x-data="{ 2 form: $form('post', '/register', { 3 name: '', 4 email: '', 5 }), 6}"> 7 @csrf 8 <label for="name">Name</label> 9 <input10 id="name"11 name="name"12 x-model="form.name"13 @change="form.validate('name')"14 />15 <template x-if="form.invalid('name')">16 <div x-text="form.errors.name"></div>17 </template>18 19 <label for="email">Email</label>20 <input21 id="email"22 name="email"23 x-model="form.email"24 @change="form.validate('email')"25 />26 <template x-if="form.invalid('email')">27 <div x-text="form.errors.email"></div>28 </template>29 30 <button :disabled="form.processing">31 Create User32 </button>33</form>
これで、ユーザーがフォームに入力するにつれて、Precognitionはルートのフォームリクエスト内のバリデーションルールに基づいたライブバリデーションの出力を提供します。フォームの入力が変更されると、デバウンスされた「Precognitive」バリデーションリクエストがLaravelアプリケーションに送信されます。デバウンスのタイムアウトは、フォームのsetValidationTimeout関数を呼び出すことで設定できます。
1form.setValidationTimeout(3000);
バリデーションリクエストが進行中の場合、フォームのvalidatingプロパティはtrueになります。
1<template x-if="form.validating">2 <div>Validating...</div>3</template>
バリデーションリクエスト中またはフォーム送信中に返されたバリデーションエラーは、フォームのerrorsオブジェクトに自動的に入力されます。
1<template x-if="form.invalid('email')">2 <div x-text="form.errors.email"></div>3</template>
フォームにエラーがあるかどうかは、フォームのhasErrorsプロパティを使用して判断できます。
1<template x-if="form.hasErrors">2 <div><!-- ... --></div>3</template>
また、入力がバリデーションに合格したか失敗したかを判断するには、フォームのvalid関数とinvalid関数にそれぞれの入力の名前を渡します。
1<template x-if="form.valid('email')">2 <span>✅</span>3</template>4 5<template x-if="form.invalid('email')">6 <span>❌</span>7</template>
フォームの入力は、変更され、バリデーションレスポンスが受信された後にのみ、有効または無効として表示されます。
これまで見てきたように、入力のchangeイベントにフックし、ユーザーが操作するにつれて個々の入力を検証できます。しかし、ユーザーがまだ操作していない入力を検証する必要がある場合もあります。これは「ウィザード」を構築する際によくあることで、次のステップに進む前に、ユーザーが操作したかどうかに関わらず、表示されているすべての入力を検証したい場合です。
Precognitionでこれを実行するには、validateメソッドを呼び出し、検証したいフィールド名をonly設定キーに渡す必要があります。バリデーションの結果は、onSuccessまたはonValidationErrorコールバックで処理できます。
1<button2 type="button"3 @click="form.validate({4 only: ['name', 'email', 'phone'],5 onSuccess: (response) => nextStep(),6 onValidationError: (response) => /* ... */,7 })"8>Next Step</button>
フォーム送信リクエストが進行中であるかどうかは、フォームのprocessingプロパティを調べることで判断できます。
1<button :disabled="form.processing">2 Submit3</button>
古いフォームデータの再入力
上記で説明したユーザー作成の例では、Precognitionを使用してライブバリデーションを実行しています。しかし、フォームを送信するためには従来のサーバーサイドのフォーム送信を実行しています。そのため、フォームにはサーバーサイドのフォーム送信から返された「古い」入力とバリデーションエラーが入力されるべきです。
1<form x-data="{2 form: $form('post', '/register', {3 name: '{{ old('name') }}',4 email: '{{ old('email') }}',5 }).setErrors({{ Js::from($errors->messages()) }}),6}">
あるいは、XHR経由でフォームを送信したい場合は、フォームのsubmit関数を使用できます。これはAxiosリクエストのPromiseを返します。
1<form 2 x-data="{ 3 form: $form('post', '/register', { 4 name: '', 5 email: '', 6 }), 7 submit() { 8 this.form.submit() 9 .then(response => {10 form.reset();11 12 alert('User created.')13 })14 .catch(error => {15 alert('An error occurred.');16 });17 },18 }"19 @submit.prevent="submit"20>
Axiosの設定
Precognitionバリデーションライブラリは、Axios HTTPクライアントを使用して、アプリケーションのバックエンドにリクエストを送信します。便宜上、アプリケーションで必要な場合はAxiosインスタンスをカスタマイズできます。たとえば、laravel-precognition-vueライブラリを使用している場合、アプリケーションのresources/js/app.jsファイルで、送信される各リクエストに追加のリクエストヘッダを追加できます。
1import { client } from 'laravel-precognition-vue';2 3client.axios().defaults.headers.common['Authorization'] = authToken;
または、アプリケーション用に設定済みのAxiosインスタンスが既にある場合は、Precognitionにそのインスタンスを使用するように指示できます。
1import Axios from 'axios';2import { client } from 'laravel-precognition-vue';3 4window.axios = Axios.create()5window.axios.defaults.headers.common['Authorization'] = authToken;6 7client.use(window.axios)
Inertia対応のPrecognitionライブラリは、設定されたAxiosインスタンスをバリデーションリクエストにのみ使用します。フォームの送信は常にInertiaによって送信されます。
バリデーションルールのカスタマイズ
リクエストのisPrecognitiveメソッドを使用することで、Precognitiveリクエスト中に実行されるバリデーションルールをカスタマイズすることが可能です。
たとえば、ユーザー作成フォームでは、パスワードが「漏洩していない」ことを最終的なフォーム送信時にのみ検証したい場合があります。Precognitiveバリデーションリクエストでは、パスワードが必須であり、最低8文字であることを単純に検証します。isPrecognitiveメソッドを使用することで、フォームリクエストで定義されたルールをカスタマイズできます。
1<?php 2 3namespace App\Http\Requests; 4 5use Illuminate\Foundation\Http\FormRequest; 6use Illuminate\Validation\Rules\Password; 7 8class StoreUserRequest extends FormRequest 9{10 /**11 * Get the validation rules that apply to the request.12 *13 * @return array14 */15 protected function rules()16 {17 return [18 'password' => [19 'required',20 $this->isPrecognitive()21 ? Password::min(8)22 : Password::min(8)->uncompromised(),23 ],24 // ...25 ];26 }27}
ファイルアップロードの処理
デフォルトでは、Laravel PrecognitionはPrecognitiveバリデーションリクエスト中にファイルをアップロードまたは検証しません。これにより、大きなファイルが不必要に複数回アップロードされることがなくなります。
この動作のため、アプリケーションが対応するフォームリクエストのバリデーションルールをカスタマイズして、フィールドが完全なフォーム送信時にのみ必須であることを指定するようにしてください。
1/** 2 * Get the validation rules that apply to the request. 3 * 4 * @return array 5 */ 6protected function rules() 7{ 8 return [ 9 'avatar' => [10 ...$this->isPrecognitive() ? [] : ['required'],11 'image',12 'mimes:jpg,png',13 'dimensions:ratio=3/2',14 ],15 // ...16 ];17}
すべてのバリデーションリクエストにファイルを含めたい場合は、クライアント側のフォームインスタンスでvalidateFiles関数を呼び出すことができます。
1form.validateFiles();
副作用の管理
ルートにHandlePrecognitiveRequestsミドルウェアを追加する際は、Precognitiveリクエスト中にスキップすべき副作用が他のミドルウェアにないかを検討する必要があります。
たとえば、各ユーザーのアプリケーションとの「インタラクション」の総数をインクリメントするミドルウェアがあるかもしれませんが、Precognitiveリクエストをインタラクションとしてカウントしたくない場合があります。これを実現するために、インタラクション数をインクリメントする前に、リクエストのisPrecognitiveメソッドをチェックできます。
1<?php 2 3namespace App\Http\Middleware; 4 5use App\Facades\Interaction; 6use Closure; 7use Illuminate\Http\Request; 8 9class InteractionMiddleware10{11 /**12 * Handle an incoming request.13 */14 public function handle(Request $request, Closure $next): mixed15 {16 if (! $request->isPrecognitive()) {17 Interaction::incrementFor($request->user());18 }19 20 return $next($request);21 }22}
テスト
テストでPrecognitiveリクエストを行いたい場合、LaravelのTestCaseにはwithPrecognitionヘルパーが含まれており、これはPrecognitionリクエストヘッダを追加します。
さらに、Precognitiveリクエストが成功したこと、例えばバリデーションエラーを返さなかったことをアサートしたい場合は、レスポンスでassertSuccessfulPrecognitionメソッドを使用できます。
1it('validates registration form with precognition', function () { 2 $response = $this->withPrecognition() 3 ->post('/register', [ 4 'name' => 'Taylor Otwell', 5 ]); 6 7 $response->assertSuccessfulPrecognition(); 8 9 expect(User::count())->toBe(0);10});
1public function test_it_validates_registration_form_with_precognition() 2{ 3 $response = $this->withPrecognition() 4 ->post('/register', [ 5 'name' => 'Taylor Otwell', 6 ]); 7 8 $response->assertSuccessfulPrecognition(); 9 $this->assertSame(0, User::count());10}