ทำโปรเจกต์ Laravel ยังไงก็ต้อง validate ข้อมูลที่ผู้ใช้กรอกเข้ามา Laravel ให้เราทำได้หลายแบบ บทความนี้จะไล่ดูสามแบบ คือ validate ใน Controller, ใน Form Request และด้วย Rule ที่เราเขียนเอง
validate ใน Controller ตรง ๆ
ใน Controller เราเรียก validate() บน Request ได้เลย
สมมติผู้ใช้กำลังเพิ่มบทความ (Article) ในฟอร์มมีชื่อ เนื้อหา และวันที่เผยแพร่ เราเขียน validation ใน Controller ได้หลายท่า ตามนี้
// ระบุ validation rules เป็น string คั่นด้วย |
public function store(Request $request)
{
$request->validate([
'name' => 'required|string',
'body' => 'required|string',
'publish_at' => 'required|date_format:Y-m-d H:i:s',
]);
// ...
}
// ระบุ validation rules เป็น array
public function store(Request $request)
{
$request->validate([
'name' => ['required', 'string'],
'body' => ['required', 'string'],
'publish_at' => ['required', 'date_format:Y-m-d H:i:s'],
]);
// ...
}
// ใช้ helper request() แทนการ inject $request
public function store()
{
request()->validate([
// validation rules...
]);
// ...
}
ค่าที่ validate() คืนมามีแค่ข้อมูลที่ผ่านการ validate แล้ว
public function store(Request $request)
{
$validatedData = $request->validate([
'name' => ['required', 'string'],
'body' => ['required', 'string'],
'publish_at' => ['required', 'date_format:Y-m-d H:i:s'],
]);
// ['name' => ..., 'body' => ..., 'publish_at' => ...]
$this->articleService->store($validatedData);
}
หรือจะหยิบ field ที่ validate แล้วออกจาก request เองก็ได้
public function store(Request $request)
{
$request->validate([
'name' => ['required', 'string'],
'body' => ['required', 'string'],
'publish_at' => ['required', 'date_format:Y-m-d H:i:s'],
]);
// เรารู้ว่า field ที่ผ่าน validation มีแค่ name, body และ publish_at
$this->articleService->store($request->only('name', 'body', 'publish_at'));
}
rule ทั้งหมดที่ Laravel มีให้ ดูได้ใน Laravel documentation
ย้าย validation ไปไว้ใน Form Request
พอ validation เริ่มยาวหรือซับซ้อน ย้ายออกจาก Controller ไปไว้ในคลาส Form Request ดีกว่า
สร้าง Form Request ด้วย Artisan
php artisan make:request StoreArticleRequest
Laravel จะสร้างคลาสไว้ใน app/Http/Requests
class StoreArticleRequest extends FormRequest
{
public function authorize(): bool
{
return false;
}
public function rules(): array
{
return [
//
];
}
}
ในคลาสมีสองเมธอดให้แล้ว คือ authorize() กับ rules()
authorize()
authorize() เอาไว้กันไม่ให้โค้ดใน Controller ได้ทำงาน ถ้าเมธอดนี้คืน false Laravel จะโยน AuthorizationException แล้วตอบ 403 Forbidden กลับไป
เช่น เช็กว่าผู้ใช้มีสิทธิ์สร้างบทความไหม
public function authorize(): bool
{
return $this->user()->can('create', Article::class);
}
rules()
ใน rules() เราคืน array ของ field กับ rule แบบเดียวกับที่เขียนใน Controller
public function rules(): array
{
return [
'name' => ['required', 'string'],
'body' => ['required', 'string'],
'publish_at' => ['required', 'date_format:Y-m-d H:i:s'],
];
}
ถ้า rules() ต้องใช้ dependency อะไร type-hint เป็นพารามิเตอร์ได้เลย
แล้วใน Controller ก็แค่ type-hint Form Request ตัวนี้ในเมธอด
public function store(StoreArticleRequest $request)
{
// ผ่าน authorize และ validation แล้ว
}
ถ้า validation ไม่ผ่าน Laravel จะ redirect ผู้ใช้กลับพร้อม flash error ไว้ใน session ส่วน request ที่อยากได้ JSON จะได้ 422 Unprocessable Content กลับไป
ดึงข้อมูลที่ validate แล้วได้เหมือนเดิม
public function store(StoreArticleRequest $request)
{
$validatedData = $request->validated();
// บันทึกลงฐานข้อมูล
// ...
}
เขียน Rule ของเราเอง
ถ้า rule ที่อยากได้ Laravel ไม่มีให้ เราสร้างคลาส Rule เองได้ด้วย Artisan
php artisan make:rule IsValidStateInUSA
ได้คลาสใหม่ใน app/Rules
class IsValidStateInUSA implements ValidationRule
{
public function validate(string $attribute, mixed $value, Closure $fail): void
{
//
}
}
โค้ดตรวจค่าเขียนไว้ในเมธอด validate() ถ้าค่าไม่ผ่านก็เรียก $fail() พร้อมข้อความ error
ลองตรวจว่าค่าที่ส่งมาเป็นรหัสรัฐของสหรัฐฯ จริงไหม โค้ดจะออกมาประมาณนี้
class IsValidStateInUSA implements ValidationRule
{
public function validate(string $attribute, mixed $value, Closure $fail): void
{
if (! in_array(strtoupper($value), ['AL', 'AK', 'AZ' /* , ... */])) {
$fail('This is not a valid state code in the USA.');
}
}
}
เวลาใช้ ก็ new คลาสนั้นใส่ลงใน array rule ของ field ที่ต้องการ
public function store(Request $request)
{
$request->validate([
'state' => ['required', new IsValidStateInUSA],
]);
// ...
}
แสดง error ให้ผู้ใช้เห็นยังไง
พอ validation ไม่ผ่าน Laravel redirect กลับให้เอง และทุก view มีตัวแปร $errors ให้ใช้อยู่แล้ว
ลองสร้าง Blade view ชื่อ messages.blade.php ไว้แสดง error แล้ว include ใน layout
@if ($errors->any())
<div class="alert alert-danger" role="alert">
<ul>
@foreach ($errors->all() as $error)
<li>{{ $error }}</li>
@endforeach
</ul>
</div>
@endif
โค้ดนี้แสดง error ทุกตัวที่อยู่ใน session ถ้าอยากแสดงทีละ field ใช้ directive @error
@error('title')
<div class="alert alert-danger">{{ $message }}</div>
@enderror
ใน @error เราใช้ตัวแปร $message ได้เลย
เปลี่ยนข้อความ error เป็นของเราเอง
ถ้าใช้ validate() ให้ส่ง array ของข้อความเป็นพารามิเตอร์ตัวที่สอง Laravel จะใช้ข้อความนี้แทนของเดิม
public function store(Request $request)
{
$request->validate([
'name' => 'required|string',
'body' => 'required|string',
'publish_at' => 'required|date_format:Y-m-d H:i:s',
], [
'name.required' => 'กรุณาระบุชื่อบทความ',
'body.required' => 'กรุณาใส่เนื้อหาบทความ',
'publish_at.date_format' => 'รูปแบบวันที่เผยแพร่ไม่ถูกต้อง',
]);
// ...
}
ฝั่ง Form Request ก็เพิ่มเมธอด messages() แล้วคืนข้อความของแต่ละ field และ rule แบบเดียวกัน
public function messages(): array
{
return [
'name.required' => 'กรุณาระบุชื่อบทความ',
'body.required' => 'กรุณาใส่เนื้อหาบทความ',
'publish_at.date_format' => 'รูปแบบวันที่เผยแพร่ไม่ถูกต้อง',
];
}
อ่านเรื่อง validation ต่อได้ที่ไหน
validation ใน Laravel ยังมีอีกเยอะ อ่านต่อได้ที่ Laravel documentation ถ้าอยากเห็นตัวอย่าง Rule ที่ตรวจเลขบัตรประชาชนไทย ลองอ่านต่อที่ ตรวจเลขบัตรประชาชน 13 หลักใน Laravel ทำยังไง ได้ครับ






