Laravel Precognition: Validation Real-Time Không Cần Lặp Lại Rules
Form validation là đoạn code bị duplicate nhiều nhất trong lập trình web. Bạn viết rules trong Laravel, rồi viết lại bằng JavaScript. Khi rules thay đổi, phải update cả hai — hoặc quên và tạo ra bugs.
Laravel Precognition giải quyết bằng cách cho frontend hỏi backend để validate từng field real-time, tái sử dụng rules từ Form Request sẵn có.
Cơ Chế Hoạt Động
User gõ email → Frontend gửi partial request → Backend validate CHỈ field đó
→ Trả kết quả validation
→ Frontend hiển thị lỗi ngay lập tức
Không submit form. Không reload trang. Không duplicate rules.
Cài Đặt
Backend
composer require laravel/precognition
Frontend (chọn stack)
# Vue
npm install laravel-precognition-vue
# React
npm install laravel-precognition-react
# Alpine.js
npm install laravel-precognition-alpine
Thiết Lập Backend
1. Thêm Middleware
// routes/web.php
Route::post('/register', [RegistrationController::class, 'store'])
->middleware('precognitive');
Middleware precognitive nói với Laravel: "Nếu đây là precognitive request, validate và trả về ngay — không chạy logic controller."
2. Dùng Form Request (Bạn Chắc Đã Có Rồi)
// app/Http/Requests/RegisterRequest.php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Validation\Rules\Password;
class RegisterRequest extends FormRequest
{
public function rules(): array
{
return [
'name' => ['required', 'string', 'max:255'],
'email' => ['required', 'email', 'unique:users,email'],
'username' => ['required', 'string', 'min:3', 'max:30', 'unique:users,username', 'alpha_dash'],
'password' => ['required', 'confirmed', Password::min(8)->mixedCase()->numbers()],
];
}
public function messages(): array
{
return [
'username.unique' => 'Username này đã được sử dụng.',
'email.unique' => 'Đã có tài khoản với email này.',
];
}
}
3. Dùng Trong Controller
class RegistrationController extends Controller
{
public function store(RegisterRequest $request)
{
// Code này CHỈ chạy khi submit thật, không phải precognitive request
$user = User::create($request->validated());
Auth::login($user);
return redirect('/dashboard');
}
}
Xong phần backend. Form Request hiện tại hoạt động nguyên trạng.
Frontend: Vue 3
<script setup>
import { useForm } from 'laravel-precognition-vue';
const form = useForm('post', '/register', {
name: '',
email: '',
username: '',
password: '',
password_confirmation: '',
});
const submit = () => form.submit({
onSuccess: () => window.location.href = '/dashboard',
});
</script>
<template>
<form @submit.prevent="submit">
<div>
<label>Email</label>
<input v-model="form.email" @change="form.validate('email')" type="email" />
<span v-if="form.invalid('email')" class="text-red-500">{{ form.errors.email }}</span>
<span v-if="form.valid('email')" class="text-green-500">✓ Khả dụng</span>
</div>
<div>
<label>Username</label>
<input v-model="form.username" @change="form.validate('username')" />
<span v-if="form.invalid('username')" class="text-red-500">{{ form.errors.username }}</span>
</div>
<div>
<label>Password</label>
<input v-model="form.password" @change="form.validate('password')" type="password" />
<span v-if="form.invalid('password')" class="text-red-500">{{ form.errors.password }}</span>
</div>
<button :disabled="form.processing" type="submit">Đăng Ký</button>
</form>
</template>
Điểm chính:
@change="form.validate('email')"— trigger validation cho field đó khi user rời inputform.invalid('email')— kiểm tra field có lỗi khôngform.valid('email')— kiểm tra field đã validated và passed- Form tự động debounce
Frontend: Alpine.js
Cho ứng dụng Blade-first:
<form
x-data="{
form: $form('post', '/register', {
name: '', email: '', username: '',
password: '', password_confirmation: '',
})
}"
@submit.prevent="form.submit()"
>
<div>
<label>Email</label>
<input x-model="form.email" @change="form.validate('email')" type="email">
<template x-if="form.invalid('email')">
<p x-text="form.errors.email" class="text-red-500 text-sm"></p>
</template>
<template x-if="form.valid('email')">
<p class="text-green-500 text-sm">✓ Khả dụng</p>
</template>
</div>
<button :disabled="form.processing" type="submit">Đăng Ký</button>
</form>
Xử Lý Side Effects
Đôi khi validation có side effects cần skip khi precognitive:
class StoreOrderRequest extends FormRequest
{
protected function prepareForValidation(): void
{
if ($this->isPrecognitive()) {
return; // Skip khi precognitive validation
}
$this->merge(['discount' => $this->calculateDiscount()]);
}
}
File Uploads
Precognition hỗ trợ validate file:
public function rules(): array
{
return [
'avatar' => ['required', 'image', 'max:2048', 'dimensions:min_width=100,min_height=100'],
];
}
File được gửi lên backend để validate — size, dimensions, MIME type — tất cả check server-side.
Performance
Mỗi lần validate field là một HTTP request. Tối ưu:
- Dùng
@changethay vì@inputcho rules nặng (nhưunique) - Debounce phù hợp — 500ms khi gõ, 0ms cho dropdowns
- Nhóm validations liên quan:
form.validate(['password', 'password_confirmation']);
Testing
public function test_precognitive_validation_for_email(): void
{
User::factory()->create(['email' => 'taken@example.com']);
$this->post('/register', ['email' => 'taken@example.com'], [
'Precognition' => 'true',
'Precognition-Validate-Only' => 'email',
])->assertStatus(422)
->assertJsonValidationErrors('email');
}
Kết Luận
Laravel Precognition cho bạn real-time form validation với zero rule duplication. Form Requests là single source of truth.
Khi nào nên dùng:
- Form đăng ký/signup với uniqueness checks
- Multi-step forms cần validate từng bước
- Form phức tạp với conditional rules
Khi nào không nên dùng:
- Form đơn giản chỉ cần check client-side (required, min length)
- Form hiếm khi fail validation
- Form traffic cao mà server load là concern