บทความ

Laravel Validation ทำได้กี่แบบ ใช้ตรงไหนบ้าง

validate ข้อมูลใน Laravel ได้ทั้งใน Controller, ใน Form Request และด้วย Rule ที่เขียนเอง แล้วแสดงหรือเปลี่ยนข้อความ error ให้ผู้ใช้เห็น

เผยแพร่
อัปเดต
— ปรับตัวอย่างให้ตรงกับ Laravel 13
เวลาอ่าน
3 นาที
แปลจาก
Anthony Rappa · laravel-news.com

ทำโปรเจกต์ 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 ทำยังไง ได้ครับ

อ่านต่อ

ล่าสุด

ดูทั้งหมด →