Form Builder ไม่ใช่สิ่งจำเป็นสำหรับ Laravel Framework

Form Builder ได้ถูกถอดออกจาก Core ของ Laravel Framework ตั้งแต่เวอร์ชั่น 5 เป็นต้นมา หากนักพัฒนาต้องการใช้การ Form Builder จะต้องติดตั้ง Package laravelcollective/html เพิ่มเติมเอง บทความนี้จึงขอเสนอทางเลือกในการโค้ด Laravel Framework แบบไม่ใช้งาน Form Builder โดยการเปลี่ยนมาใช้ <form> แทนครับ

ตัวอย่างโค้ดของ Laravel Form Builder

ก่อนอื่นเรามาดูตัวอย่างโค้ดของการใช้งาน Form Builder กันก่อนครับ

Blade

{!! Form::open([ 'url' => '/registers', 'method' => 'post' ]) !!}

    {!! Form::input('text', 'first_name', null, [ 'class' => 'input' ]) !!}
    {!! Form::input('text', 'last_name', null, [ 'class' => 'input' ]) !!}

    {!! Form::submit('Save', [ 'class' => 'button is-primary' ]) !!}
    {!! Form::reset('Cancel', [ 'class' => 'button is-danger' ]) !!}

{!! Form::close() !!}

ข้อดีของ Laravel Form Builder

  • คำสั่ง เขียน Form มีกลิ่นอายในแบบ Laravel Syntax
  • คำสั่ง Form::open() ใส่ csrf Token ให้โดยอัตโนมัติ
  • คำสั่ง Form::select() ไม่จำเป็นต้องเขียน Loop เอง
  • คำสั่ง Form Element ต่างๆ Restore ค่าเดิมให้อัตโนมัติกรณีที่ไม่ผ่านการ Validation
  • คำสั่ง Form::model() ช่วยเพิ่มความสะดวกในการใช้ Form ร่วมกับ Eloquent

ข้อเสียของ Laravel Form Builder

  • ถูกถอดออกจาก Core ของ Laravel Framework ตั้งแต่ Version 5 ถ้าจะใช้ต้องติดตั้งเพิ่มเติม
  • Form Builder ถูก Maintain โดย 3rd Party ไม่ใช่ Community ของ Laravel
  • ต้องเรียนรู้ Syntax เพิ่มเติม
  • ไม่รองรับ Emmet
  • กรณี Web Application มี UI ของ HTML ที่ซับซ้อน ต้องใช้ Syntax ของ Form Builder ผสมกับ HTML Element ทำให้ดูไม่สวยงาม
  • ไม่สามารถขอความช่วยเหลือจากนักพัฒนาที่ไม่ได้ใช้ Form Builder

HTML Form

ก่อนที่จะเขียน <form> ร่วมกับ Laravel Framework เรามาลองเขียน <form> แบบพื้นฐานกันดูก่อน

เตรียมไฟล์สำหรับเขียนโปรแกรม

Path

|-register.html
|-submit.html
|-submit.php...

เพิ่ม Attribute action เพื่อกำหนดว่า Form จะถูก Submit ไปที่ไฟล์ไหน ซึ่งสามารถกำหนดเป็นไฟล์ HTML ก็ยังได้

ไฟล์ register.html

<form action="/submit.html">
    <input type="text" name="first_name">
    <input type="text" name="last_name">

    <input type="submit" value="Save">
</form>

Start PHP Built-in Web Server ที่ Root Path ของโปรเจค

พิมพ์คำสั่งที่ Terminal

php -S localhost:8000

Browser

http://localhost:8000/register.html

html form

การ Submit Form

html form submit using get method

ไฟล์ register.html

<h3>ได้รับข้อมูลเรียบร้อยแล้ว</h3>

html form submit response

การ Submit Form ด้วย Default Method (GET) จะส่งผลให้เกิดเป็น Query String ตามรูปตัวอย่าง ในการทำงานจริงเราจะไม่เลือกใช้ GET Method สำหรับการ Submit Form เนื่องจากไม่มีความปลอดภัย

การ Submit Form ด้วย POST Method

ไฟล์ register.html

<form action="/submit.html" method="POST">
    ...
</form>

html form submit using post method

การ Submit Form ด้วย POST Method ข้อมูลจะถูก Encode (เปลี่ยนแปลงรูปร่างหน้าตาเพื่อความสะดวกในการ Transportation) และ ถูกแนบไปกับ Request เราสามารถตรวจสอบ Request และ Response โดยใช้ Chrome DevTools

HTML Form & PHP Script

การ Submit Form ไปยังไฟล์ HTML นั้นสามารถทำได้ แต่ก็นำมาใช้ประโยชน์อะไรไม่ได้ เราจะลองเปลี่ยนเป็นการ Submit Form ไปยังไฟล์ PHP แทนครับ

ไฟล์ register.html

<form action="/submit.php" method="POST">
    ...
</form>

ไฟล์ submit.php

<?php

var_dump($_POST);

Result

array (size=2)
  'first_name' => string 'John' (length=4)
  'last_name' => string 'Doh' (length=3)

เตรียมไฟล์สำหรับ Laravel Project

เรามาลอง Setup Laravel Project แบบง่ายๆ โดยมีไฟล์ที่จะนำมาใช้ตามบทความ ตามโครงสร้างด้านล่าง

Path

+-app
|   +-Http
|   |   +-Controllers
|   |   |   +-RegisterController
+-resources
|   +-views
|   |   +-registers
|   |   |   |-create.blade.php
|   |   |   |-edit.blade.php
|   +-routes
|   |   |-web.php

Route & Controller Setup

พิมพ์คำสั่งที่ Terminal

สร้าง Controller และ Route ในแบบ ReSTful

php artisan make:controller RegisterController --resource

ไฟล์ routes/web.php

Route::resource('registers', 'RegisterController');

ไฟล์ app/Http/RegisterController.php

class RegisterController extends Controller
{
    public function index()
    {
        return 'Hello World';
    }
}

พิมพ์คำสั่งที่ Terminal

php artisan serv

Browser

http://localhost:8000

ใช้งาน Form ใน Laravel Framework

จากโค้ดตัวอย่างในหัวข้อ HTML Form เราจะเห็นว่าสาระสำคัญในเรื่องการ Submit Form ก็คือ Attribute action ของ <form> ซึ่งเป็นตัวกำหนดว่า Form จะถูก Submit ไปที่ไฟล์ไหนเพื่อประมวลผล ดังนั้นเมื่อนำมาใช้กับ Laravel Framework เราก็เพียงแค่ระบุ URI ตามที่ต้องการ

ไฟล์ resources/views/registers/create.blade.php

<form action="/registers" method="POST">
    <input type="text" name="first_name">
    <input type="text" name="last_name">

    <input type="submit" value="Save">
</form>

ไฟล์ App/Http/Controllers/RegisterController.php

class RegisterController extends Controller
{
    public function create()
    {
        return view('registers.create');
    }

    public function store(Request $request)
    {
        return '<h3>Thanks</h3>';
    }
}

Oops !!! เกิดปัญหาหลังจากทดลอง Submit Form

CSRF Protection

Laravel Framework มีระบบป้องการการถูกโจมตีในแบบ csrf เราจึงต้องส่งค่า csrf Token แนบไปกับการ Submit Form เพื่อให้ Request สามารถผ่านด่านอรหันต์นี้เข้าไปได้ แต่โชคดีที่ Laravel Framework มี Helper Function ไว้ให้เราเรียกใช้ โดยเราจะแก้ไขโค้ดตามตัวอย่างด้านล่าง

ไฟล์ creates/views/registers/create.blade.php

::: v-pre

<form action="/registers" method="POST">
    {{ csrf_field() }}
</form>

:::

Refresh หน้าเว็บและลองแสดง Page Source

Page Source

<form action="/registers" method="POST">
    <input type="hidden" name="_token" value="UeNq68NZdMAszrhDdS2QJ9zqX...">
</form>

csrf_field() ได้สร้าง <input type="henden">

แก้ไขโค้ดเพิ่มเติมตามตัวอย่างด้านล่าง เพื่อ Retrieve ค่าจาก Form

ไฟล์ App/Http/Controllers/RegisterController.php

class RegisterController extends Controller
{
    public function store(Request $request)
    {
        $inputs = request()->all();
        // $inputs = request()->except(_token);

        dd($inputs);
    }
}

Refresh เว็บ, ป้อนข้อมูลใหม่ และลอง Submit Form

Result

array:3 [▼
  "_token" => "UeNq68NZdMAszrhDdS2QJ9zq..."
  "first_name" => "John"
  "last_name" => "Doh"
]

action() Helper Function

หากเราไม่คุ้นเคยกับการเรียกใช้ URL Path ตรงๆ เราสามารถใช้ Helper Function action() เพื่อระบุการเข้าถึง Method ใน Controller ซึ่งง่ายกว่าในการทำความเข้าใจว่า Form จะถูก Submit ไปที่ใดกันแน่

ไฟล์ resources/views/registers/create.blade.php

::: v-pre

<form action="{{ action('RegisterController@store') }}" method="POST">
    ...
</form>

:::

ReSTful Method

การใช้ <form> เพื่อสร้าง ReSTful Request บางคนมักจะเข้าใจผิดว่าเราสามารถทำได้โดยการแก้ไขค่า Attribute method เหมือนในตัวอย่างด้านล่าง

<form action="..." method="PUT">
    ...
</form>

แต่ในความเป็นจริง PUT และ DELETE Method ไม่มีตัวตนอยู่จริงใน HTML Form แต่มีตัวตนใน XMLHttpRequest (AJAX) ดังนั้นการจะประยุกต์ใช้งาน HTTP Method เช่น PUT และ DELETE จึงเป็นได้แค่ส่วนขยายของ POST Method เท่านั้น ซึ่่งเป็นเรื่องของแต่ละ Framework ที่จะกำหนดวิธีการขึ้นมาเอง ซึ่งการกำหนด PUT และ DELETE Method ใน Laravel Framework จะใช้ Helper Function ชื่อ method_field() เป็นตัวกำหนด ตามตัวอย่างโค้ดด้านล่าง

PUT Method

ไฟล์ creates/views/registers/edit.blade.php

::: v-pre

<form action="{{ action('RegisterController@update', [ 'id' => 1001 ]) }}" method="POST">
    {{ csrf_field() }}

    {{ method_field('PUT') }}

    ...
</form>

:::

RegisterController@update นั้นจะต้องแนบ Argument ซึ่งเป็น ID ของ Record ที่จะถูกแก้ไขไปด้วย เราจึงจำเป็นจะต้องมี Argument เพิ่มเติมให้กับ action() ด้วย

Page Source

<form action="http://localhost:8000/registers/1001" method="POST">
    <input type="hidden" name="_token" value="UeNq68NZdMAszrhDdS2QJ9zqXQ1G02nav4e3sW6P">
    <input type="hidden" name="_method" value="PUT">

    ...
</form>

method_feld() ได้สร้าง <input type="henden">

DELETE Method

ไฟล์ creates/views/registers/edit.blade.php

::: v-pre

<form action="{{ action('RegisterController@destroy', [ 'id' => 1001 ]) }}" method="POST">
    {{ csrf_field() }}

    {{ method_field('DELETE') }}

    ...
</form>

:::

Page Source

<form action="http://localhost:8000/registers/1001" method="POST">
    <input type="hidden" name="_token" value="UeNq68NZdMAszrhDdS2QJ9zqXQ1G02nav4e3sW6P">
    <input type="hidden" name="_method" value="DELETE">

    ...
</form>

つづく