Laravel 13.40 เพิ่ม strict: true ใน $request->integer()

Laravel 13.40 เพิ่ม strict: true ให้ integer(), float(), boolean() และ string() ค่าที่แปลงไม่ได้จะได้ค่า default แทน 0 หรือ error เหมาะกับ filter และค่าเสริมในหน้ารายการ ส่วนข้อมูลที่ผู้ใช้ต้องกรอกให้ถูกยังต้องใช้ validation

เผยแพร่
เวลาอ่าน
3 นาที
สรุปจาก
Paul Redmond · laravel-news.com
ในโพสต์นี้ 9 หัวข้อ
  1. ค่าผิดกลายเป็น 0 เพราะ helper เดิมแค่ cast
  2. ใส่ strict: true แล้วค่าที่แปลงไม่ได้จะได้ค่า default
  3. แต่ละ method รับค่าอะไรบ้าง
  4. เลขผู้เสียภาษีกับเบอร์โทรต้องรับเป็น string
  5. ราคาที่พิมพ์ comma มาจะได้ค่า default
  6. strict เช็คแค่ชนิดข้อมูล ส่วนช่วงของค่าต้องเช็คเอง
  7. ค่า default เป็น closure หรือ null ก็ได้
  8. ใช้กับข้อมูลนอก request ได้ด้วย
  9. ข้อมูลที่ผู้ใช้ต้องกรอกให้ถูกยังต้องใช้ validation

ค่าผิดกลายเป็น 0 เพราะ helper เดิมแค่ cast

$request->integer() ใช้ cast ของ PHP ตรง ๆ และใช้ค่า default เฉพาะตอนที่ไม่มี key นั้นใน request พอมีใครแก้ URL เล่น หรือบอทยิงค่าแปลก ๆ เข้ามา เราจึงได้ค่าที่ไม่ได้ตั้งใจ

// /orders?per_page=abc123
$request->integer('per_page', 25); // 0

// /settings?notify=maybe
$request->boolean('notify', true); // false

// /customers?name[]=chris
$request->string('name'); // ErrorException: Array to string conversion

ตัวอย่างที่สองเห็นผลชัดที่สุด ผู้ใช้ส่ง maybe มาโดยยังไม่ได้เลือกอะไร แต่การแจ้งเตือนกลับปิดไปเลย ส่วนตัวอย่างที่สามกลายเป็น error 500 จาก URL ที่ใครก็พิมพ์ได้

ใส่ strict: true แล้วค่าที่แปลงไม่ได้จะได้ค่า default

$perPage = $request->integer('per_page', 25, strict: true);
$notify = $request->boolean('notify', true, strict: true);
$price = $request->float('max_price', 1000.0, strict: true);
$sort = $request->string('sort', 'created_at', strict: true);

argument นี้เข้ามาใน Laravel 13.40 (PR #62254 ของ Chris Morrell) โปรเจกต์ที่ยังอยู่รุ่นเก่ากว่านั้นต้องอัปเดตก่อน ไม่อย่างนั้น PHP จะโยน error ว่าไม่รู้จัก named parameter นี้

แต่ละ method รับค่าอะไรบ้าง

Method ค่าที่ผ่าน ค่าที่ได้ default แทน
integer() 50, +12 12.5, 007, 12abc, เลขที่เกินขนาด int, true จาก JSON
float() 49.99, 12, 1e3, .5 1,000, abc, INF, true จาก JSON
boolean() 1, true, on, yes เป็น true และ 0, false, off, no เป็น false maybe, 2, ค่าว่าง
string() ข้อความและตัวเลข array, true/false, null

boolean() ไม่สนตัวพิมพ์เล็กใหญ่หรือช่องว่างรอบคำ ?notify=ON จึงเป็น true เหมือนกัน

ค่าว่างอย่าง ?sort= ก็ได้ default ในโหมด strict เพราะ middleware ConvertEmptyStringsToNull ที่ Laravel ใส่มาให้เปลี่ยนเป็น null ก่อนแล้ว ส่วนแบบไม่ strict จะได้ข้อความว่าง เพราะถือว่ามี key นั้นอยู่

เลขผู้เสียภาษีกับเบอร์โทรต้องรับเป็น string

งานในไทยมีเลขหลายชุดที่หน้าตาเป็นตัวเลขแต่ขึ้นต้นด้วย 0 อย่างเลขผู้เสียภาษีของนิติบุคคลและเบอร์โทร ถ้ารับเลขพวกนี้ด้วย integer() จะได้ค่าผิดทั้งสองโหมด

// ?tax_id=0105500000000
$request->integer('tax_id');                  // 105500000000 เลข 0 ตัวหน้าหายไป
$request->integer('tax_id', 0, strict: true); // 0 เพราะ strict ไม่รับเลขที่มี 0 นำหน้า
$request->string('tax_id')->value();          // "0105500000000"

เลขชุดนี้เป็นรหัส ไม่ได้เอาไปคำนวณ จึงรับเป็น string แล้วค่อยตรวจรูปแบบด้วย validation ถ้าอยากต่อยอด ลองอ่านวิธีดึงชื่อบริษัทจากเลขผู้เสียภาษีได้ครับ

ราคาที่พิมพ์ comma มาจะได้ค่า default

ช่องกรองราคาที่ให้พิมพ์เองมักได้ค่าอย่าง 1,000 มาด้วย cast แบบเดิมจะอ่านแค่ก่อนถึง comma แล้วได้ 1.0 หน้ารายการจึงกรองเหลือแต่ของราคาไม่เกินหนึ่งบาท

โหมด strict จะคืนค่า default แทน ซึ่งปลอดภัยกว่า แต่ผู้ใช้ก็ยังไม่ได้ผลตามที่พิมพ์ ถ้าช่องนี้ให้พิมพ์เอง ให้ตัด comma ออกก่อนในหน้าเว็บ หรือใน prepareForValidation() ของ Form Request

strict เช็คแค่ชนิดข้อมูล ส่วนช่วงของค่าต้องเช็คเอง

?per_page=1000000 กับ ?per_page=-1 เป็น integer ที่ถูกต้องทั้งคู่ จึงผ่าน strict ไปได้ ถ้าเอาค่านี้ไปแบ่งหน้าโดยตรง ใครก็สั่งให้ query ดึงข้อมูลทีเดียวหลายแสนแถวได้ ให้จำกัดช่วงไว้ด้วย Number::clamp()

use Illuminate\Support\Number;

$perPage = Number::clamp($request->integer('per_page', 25, strict: true), 1, 100);

ค่า default เป็น closure หรือ null ก็ได้

closure เหมาะกับค่า default ที่ต้องไปดึงมาก่อน ส่วน null ใช้แยก "ไม่ได้ส่งค่าที่ใช้ได้มา" ออกจาก "ส่ง 0 มาจริง ๆ" ซึ่งแบบไม่ strict จะได้ 0 ทั้งคู่

$branchId = $request->integer('branch_id', fn () => $request->user()->branch_id, strict: true);

$limit = $request->integer('limit', null, strict: true);

if ($limit === null) {
    // ไม่มี limit ที่ใช้ได้ใน request
}

เมื่อค่าที่ส่งมาผิด Laravel จะคืนค่า default ตามที่เราเขียนโดยไม่ cast ให้ จึงควรตั้ง default ให้ตรงชนิดกับ method นั้น ส่วน docblock ของ integer() กับ float() ยังบอกว่าคืนค่า int และ float เครื่องมืออย่าง PHPStan หรือ Larastan จึงอาจเตือนตรงที่ใช้ null

ใช้กับข้อมูลนอก request ได้ด้วย

strict อยู่ใน trait InteractsWithData จึงมีให้ทุกที่ที่ใช้ trait นี้

  • $request->safe() ที่คืน ValidatedInput
  • Fluent
  • Uri::of($url)->query() ตอนอ่าน query string ของ URL ที่ระบบอื่นส่งมา
  • $attributes ใน Blade component
  • $this->input() ในคำสั่ง Artisan

ข้อมูลที่ผู้ใช้ต้องกรอกให้ถูกยังต้องใช้ validation

จุดอ่อนของ strict คือไม่มีใครรู้ว่าค่าที่ส่งมาผิด ระบบแค่ใช้ค่า default แล้วทำงานต่อ กับจำนวนแถวต่อหน้าไม่เป็นไร แต่ถ้าลูกค้าพิมพ์จำนวนสินค้าเป็น abc แล้วระบบสั่งของให้ 25 ชิ้น เรื่องจะใหญ่กว่า error หนึ่งบรรทัดมาก

ค่าแบบไหน ใช้อะไร
ขนาดหน้า ทิศทางการเรียง ปุ่มเปิดปิดการแสดงผล filter ในหน้ารายการ strict: true
ค่าที่ต้องมี หรือผู้ใช้ควรได้รู้ว่ากรอกผิด เช่น จำนวนสินค้า ราคา เลขผู้เสียภาษี validation

ตัวอย่างเพิ่มเติมของแต่ละ method อ่านต่อได้ที่บทความของ Paul Redmondครับ

อ่านต่อ

ล่าสุด

ดูทั้งหมด →