はじめに
今回は、Laravel Precognitionを使用したライブバリデーションについてまとめました。
※おことわり※
基本的に学習内容のアウトプットです。
初学者であるため、間違い等あればご指摘いただけますと嬉しいです。
この記事の目的
以下内容のアウトプット
- Laravel Precognitionを使用したバリデーションの実装
この記事の内容
- Precognitionについて
- 前提
- ざっくり
- ルートの作成
- バリデーションの作成
- コントローラの作成
- Precognitionライブラリをインストール
- ビューの作成
- 参考
1. Precognitionについて
One of the primary use cases of Precognition is the ability to provide "live" validation for your frontend JavaScript application without having to duplicate your application's backend validation rules.
バックエンドと同じバリデーションルールを、複製することなくフロントエンドに適用することができます。
リアルタイムで検証が行われ、ライブバリデーションを実現できます。
ライブバリデーションとは
こんな感じで、laravelのFormRequestで実装した検証内容をReactに適用できます。
2. 前提
- Inertia環境のLaravelプロジェクトが構築済み ※1
- Material UIがインストール済み ※2
※1 構築がまだの方は以下から環境構築できます。
※2 以下コマンドを実行し、Material UIと関連するモジュールをインストールします。
npm install @mui/material @mui/icons-material @emotion/react @emotion/styled
3. ざっくり
名前、題名、説明の3点が入力できるフォームを作成します。
フォームの検証にはライブバリデーションを適用し、どのように動くか検証します。
4. ルートの作成
以下2つのルートを作成します。
- フォーム
- フォームの送信先
// フォーム
Route::get('/form', [FormController::class, 'form'])->name('precognition.form');
// フォームの登録
Route::post('/create', [FormController::class, 'create'])->middleware([HandlePrecognitiveRequests::class])->name('precognition.create');
// ^^^^^^^^^^^ Precognitionを有効にする
create()メソッドに対して、middlewareを指定してPrecognitionを有効にします。
5. バリデーションの作成
FormRequestを作成します。
php artisan make:request PrecognitionFormRequest
class PrecognitionFormRequest 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<string, \Illuminate\Contracts\Validation\ValidationRule|array|string>
*/
public function rules(): array
{
return [
'userName' => ['required', 'max:10'],
'title' => ['required', 'max:150'],
'content' => ['required', 'max:1500'],
];
}
public function messages()
{
return [
'userName.required' => '名前を入力してください。',
'userName.max' => '名前は10文字以内で入力をしてください。',
'title.required' => '題名を入力してください。',
'title.max' => '題名は150文字以内で入力をしてください。',
'content.required' => '説明を入力してください。',
'content.max' => '説明は1500文字以内で入力をしてください。',
];
}
}
キー名(userNameなど)はフロントのフィールドに合わせます。
6. コントローラの作成
コントローラを作成し、form()とcreate()メソッドを定義します。
php artisan make:controller FormController
class FormController extends Controller
{
/**
* フォームを表示
* @return Response
*/
public function form(): Response
{
return Inertia::render('Form/FormCreate');
}
/**
* フォームの登録
* @param PrecognitionFormRequest $request
* @return RedirectResponse
*/
public function create(PrecognitionFormRequest $request): RedirectResponse
{
// 登録処理を省略
return to_route('hoge.huga');
}
create()メソッドにPrecognitionFormRequestを指定します。
7. Precognitionライブラリをインストール
laravel-precognition-react-inertiaパッケージをインストールします。
npm install laravel-precognition-react-inertia
この後の実装で、検証機能が強化されたInertiaのフォームヘルパー useForm
を使用します。
8. ビューの作成
Material UIを使用して、フォームを作成します。
import { ChangeEvent } from 'react';
import { useForm } from 'laravel-precognition-react-inertia'; // ※1
import {
Box,
Button,
Card,
CardContent,
FormControl,
Grid,
TextField,
Typography,
} from '@mui/material';
import InputError from '@/Components/InputError';
type FormCreateProps = {
userName: string;
title: string;
content: string;
};
export const FormCreate = () => {
// フォームの初期化
const form = useForm<FormCreateProps>('post', route('precognition.create'), { // ※1
userName: '',
title: '',
content: '',
});
// 送信
const handleSubmit = (e: React.FormEvent<HTMLFormElement>): void => {
e.preventDefault();
form.submit({
preserveScroll: true,
onSuccess: () => {
form.reset();
},
onError: (errors: object) => {
console.error(errors);
},
});
};
// 入力項目のonChangeイベント
const handleChangeUserName = (e: ChangeEvent<HTMLInputElement>): void => {
form.setData('userName', e.target.value);
form.forgetError('userName');
};
const handleChangeTitle = (e: ChangeEvent<HTMLInputElement>): void => {
form.setData('title', e.target.value);
form.forgetError('title');
};
const handleChangeContent = (e: ChangeEvent<HTMLInputElement>): void => {
form.setData('content', e.target.value);
form.forgetError('content');
};
return (
<Box component="form" onSubmit={handleSubmit}> // ※2
<Card variant="outlined" sx={{ mt: 5 }}>
<CardContent>
<Typography
variant="h6"
component="div"
sx={{ mb: 1, fontWeight: 'bold', textAlign: 'center' }}
>
フォーム
</Typography>
<Grid container sx={{ my: 2 }}>
<TextField
label={'名前'}
required={true}
value={form.data.userName}
onChange={handleChangeUserName} // ※3
onBlur={() => form.validate('userName')} // ※4
fullWidth
inputProps={{
style: {
boxShadow: 'none',
},
}}
/>
{form.invalid('userName') && (
<InputError message={form.errors.userName} className="mt-2" />
)} // ※5
</Grid>
<Grid container sx={{ my: 2 }}>
<TextField
label={'題名'}
required={true}
value={form.data.title}
onChange={handleChangeTitle}
onBlur={() => form.validate('title')}
fullWidth
inputProps={{
style: {
boxShadow: 'none',
},
}}
/>
{form.invalid('title') && (
<InputError message={form.errors.title} className="mt-2" />
)}
</Grid>
<Grid container sx={{ my: 2 }}>
<TextField
label={'説明'}
required={true}
value={form.data.content}
onChange={handleChangeContent}
onBlur={() => form.validate('content')}
fullWidth
inputProps={{
style: {
boxShadow: 'none',
},
}}
/>
{form.invalid('content') && (
<InputError message={form.errors.content} className="mt-2" />
)}
</Grid>
<Grid container>
<Button disabled={form.processing} type={'submit'} variant="contained"> // ※6
登録
</Button>
</Grid>
</CardContent>
</Card>
</Box>
);
};
※1 useForm
- reactのuseFormではなく、laravel-precognition-react-inertiaの
useForm
を使用する - useFormを使用して フォームオブジェクト を作成する
- 各フィールドに初期値を指定する
※2 onSubmit
- formタグでフォームを囲う
- onSubmitでデフォルトのフォーム送信を防ぎ、
form.submit
をトリガーする
※3 onChange
- 入力値が変更されたとき、
form.setData
で新しい値をセットする -
form.forgetError
でエラーを手動でクリアする
※4 onBlur
- 対応するフィールドのバリデーションをトリガーする
※5 エラーメッセージ
- 検証エラーかどうか
form.invalid
で判定をして、検証エラーの場合、指定したフィールドのエラーメッセージを表示する
※6 多重クリック対策
-
form.processing
でフォーム送信リクエストが処理中かどうか判定する - 送信中の場合、ボタンが非活性になり、クリックされるのを防ぐ
フォームオブジェクト
-
form.reset:フォームをリセットする
-
form.processing:フォーム送信リクエストが処理中の場合、falseを返す
-
form.data:フォームの値を管理する
console.log(form.data);
-
form.validating:検証リクエストが進行中の場合、trueを返す
-
form.hasErrors:フォームにエラーがある場合、trueを返す
-
form.valid:検証を通過した場合、trueを返す
-
form.invalid:検証エラーの場合、trueを返す
-
form.errors:検証エラーの場合、エラーが格納される
console.log(form.errors);
完成したフォーム
バリデーションの挙動
9. 参考