จากที่ได้เกริ่นไปในบทความตอนที่แล้ว เนื้อหานี้เราจะ
มาดูตัวอย่างการประยุกต์ใช้งาน การสร้าง RESTful API ใน CI4
โดยอธิบายต่อเนื่องจากตอนที่แล้ว สามารถทบทวนได้ที่
บทความตามลิ้งค์ด้านล่าง
การกำหนด และใช้งาน RESTful API ใน CodeIgniter 4 http://niik.in/1013
https://www.ninenik.com/content.php?arti_id=1013 via @ninenik
เนื้อหานี้ เราจะมีการพูดถึงส่วนต่างๆ ที่เคยแนะนำไปแล้ว ไม่ว่าจะเป็น Model
การใช้งาน Controller การกำหนด Routes การใช้งานร่วมกับฐานข้อมูล เหล่านี้ล้วน
จำเป็นสำหรับการประยุุกต์ใช้งานนี้ หากใครเพิ่งมาอ่านหัวข้อนี้ แนะนำทบทวนเนื้อหา
ที่จำเป็นตามลิ้งค์ด้านล่าง
สิ่งที่จะทำในเนื้อหานี้
เราจะสร้าง RESTful API สำหรับ users โดยสามารถทำการเพิ่ม ลบ แก้ไข แสดง ข้อมูล users ผ่านคำสั่ง
HTTP Request ต่างๆ ได้ โดยจะเริ่มต้นที่รูปแบบอย่างง่าย แล้วปรับการใช้งานเพิ่มเติม เช่นการใช้งานรูปแบบ
เฉพาะสำหรับตอบกลับของ API เพื่อให้ทำงานได้ง่ายและสะดวกขึ้น
ในการทดสอบการเรียกใช้งาน เราจะใช้โปรแกรมที่ชื่อ Insomnia Core สำหรับใช้เป็น REST api client
สามารถดาวน์โหลดมาใช้งานได้ที่ https://insomnia.rest/download/core/
เตรียมส่วนของประยุกต์ใช้งาน
สิ่งแรกที่เราต้องทำคือ ให้เราสร้างตาราง tbl_users ด้วยรุปแบบโครงสร้างดังนี้
CREATE TABLE `tbl_users` ( `id` int(11) NOT NULL, `name` varchar(100) NOT NULL, `username` varchar(100) NOT NULL, `email` varchar(100) NOT NULL, `created_at` datetime NOT NULL DEFAULT current_timestamp(), `updated_at` datetime NOT NULL, `deleted_at` datetime NOT NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8; ALTER TABLE `tbl_users` ADD PRIMARY KEY (`id`); ALTER TABLE `tbl_users` MODIFY `id` int(11) NOT NULL AUTO_INCREMENT;
ฟิลด์หลักๆ ของเราจะอยู่ที่ id name username และ email ส่วนอีก 3 ฟิลด์ที่เหลือ เป็นการกำหนดโดย
ใช้เริ่มแบบตามค่าเริ่มต้นของฟิลด์ใน model สำหรับเก็บวันที่ของการสร้างข้อมูล วันที่อัพเดท และวันที่ลบ
ฟิลด์นี้สัมพันธ์การใช้งานใน model สามารถไปทบทวนได้ที่บทความเกี่ยวกับ model
รู้จักกับ Model และวิธีการใช้งาน Model ใน CodeIgniter 4 http://niik.in/1004
https://www.ninenik.com/content.php?arti_id=1004 via @ninenik
เราจะสร้าง API โดยกำหนด Controllers ไว้ใน app/Controllers/Api/ และกำหนด Model ไว้ใน
app/Models/Api/ โดยจะใช้ชื่อเป็น Users.php และ UsersModel.php ตามลำดับ
ต่อไปให้เรากำหนด Resource Routes สำหรับใช้งาน Users API โดยกำหนดใน Routes.php
app/Config/Routes.php
$routes->resource('api/users', ['controller' =>'Api\Users']);
เนื่องจากปกติ การกำหนด resource route จะใช้ชื่อของ route เป็นชื่อของ controller แต่เนื่องจากว่า
เรากำหนดไว้ในโฟลเดอร์ย่อย api อีกที ดังนั้น ชื่อของ controller เราจะต้องปรับเป็นค่าที่ถูกต้อง นั่นคือใช้
เป็นค่า 'Api\Users' กำหนดใน controller option ลงไป
การกำหนดชื่อ Controller ข้างต้น เราจะกำหนดเฉพาะส่วนที่เพิ่มเติม เพราะ path หลักของ Controllers
จะอยู่ที่ App\Controllers อยู่แล้ว ดังนั้นเราจึงกำหนดแค่ 'Api\Users'
เมื่อเรากำหนด resource routes ไปแล้วข้างตัน ตัว CI4 ก็จะสร้าง routes สำหรับ API ให้เราตามที่อธิบาย
ไปในเนื้อหาตอนที่แล้ว ต่อไป เราต้องจัดการในส่วนของ model และ Controller เพื่อเรียกใช้งาน ตามลำดับดังนี้
กำหนด Model สำหรับ RESTful API
ให้เราสร้างไฟล์ model ชื่อ UsersModel.php ตามรูปแบบคำสั่งดังนี้
app/Models/Api/UsersModel.php
<?php namespace App\Models\Api; // กำหนด namespace use CodeIgniter\Model; // เรียกใช้งาน Model class class UsersModel extends Model { protected $table = 'tbl_users'; protected $primaryKey = 'id'; protected $returnType = 'array'; protected $allowedFields = ['name', 'username', 'email']; protected $useSoftDeletes = true; protected $dateFormat = 'datetime'; // datetime, date, int protected $useTimestamps = true; protected $createdField = 'created_at'; protected $updatedField = 'updated_at'; protected $deletedField = 'deleted_at'; protected $validationRules = []; protected $validationMessages = []; protected $skipValidation = false; }
ข้างต้นเราจะยังไม่ส่วนใจในส่วนของการ validation
สังเกตในส่วนของการกำหนด namespace เรากำหนด api path เพิ่มเข้าไปด้วย
ตอนนี้เราได้ส่วนของ UsersModel class สำหรับใช้งาน
กำหนด Resource Controller สำหรับ RESTful API
ถึงแม้ resource route จะกำหนดรูปแบบ method ที่รองรับให้เราเรียบร้อยแล้วได้แก่ new create index
show edit update และ delete แต่ที่เราจะใช้งาน จะมีแค่การเพิ่ม ลบ แก้ไข และแสดงค่า เท่านั้น ดังนั้นเราจะ
กำหนด method ให้กับ Resource Controller เฉพาะที่จะใช้งาน คือ index create show update และ delete
เราจะได้ไฟล์ Users.php ดังนี้
app/Controllers/Api/Users.php
<?php namespace App\Controllers\Api; use CodeIgniter\RESTful\ResourceController; class Users extends ResourceController { protected $modelName = 'App\Models\Api\UsersModel'; protected $format = 'json'; public function index(){ } public function create(){ } public function show($id = null){ } public function update($id = null){ } public function delete($id = null){ } }
ใน ResourceController เรากำหนด property ชื่อ $modelName ให้มีค่าเท่ากับ Model class ที่เราจะเรียก
ใช้งาน สังเกตว่า จะแตกต่างจากรูปแบบที่เรียกใช้งานใน Controller ปกติ การกำหนดข้างต้น เราจะสามารถเรียก
ใช้งาน model ใน RecourceController ผ่านคำสั่ง $this->model
สำหรับ property ที่ชื่อ $format เป็นการกำหนดรุปแบบของ ข้อมูล API ที่จะตอบกลับไปยัง Client ว่าจะใช้เป็น
รูปแบบไหน โดยทั่วไป มักใช้เป็น json หรือถ้าเราต้องการใช้เป็นแบบ xml ก็สามารถกำหนดค่าเข้าไปได้
การกำหนด parameter สำหรับ show() update() และ delete() method เราจะต้องกำหนดตามรูปแบบตัวอย่าง
ด้านบน คือ ให้มีค่าเริ่มต้นเป็น null เราจะกำหนดในรูปแบบ delete($id) ไม่ได้
ที่นี้เราปรับโค้ดใหม่ เพื่อจำลองการทำงาน ซึ่งปกติแล้ว การเพิ่ม แก้ไข เราจะต้องส่งข้อมูลมาด้วย แต่เราจะกำหนด
เป็นแบบฟิกค่าไปก่อน เพื่อดูการทำงาน จะได้ไฟล์ Users.php ปรับใหม่เป็นดังนี้
app/Controllers/Api/Users.php
<?php namespace App\Controllers\Api; use CodeIgniter\RESTful\ResourceController; class Users extends ResourceController { protected $modelName = 'App\Models\Api\UsersModel'; protected $format = 'json'; public function index() { $response = $this->model->findAll(); return $this->respond($response); } public function create(){ if($this->model->save([ 'name' => 'new name test '.date("H:i:s"), 'username' => 'new username test '.date("H:i:s"), 'email' => 'new email test '.date("H:i:s"), ])){ $response = [ 'status' => 200, 'message' => 'Data Created' ]; } return $this->respond($response); } public function show($id = null){ if(!empty($id)){ $response = $this->model->find($id); } return $this->respond($response); } public function update($id = null){ if($this->model->save([ 'id' => $id, 'name' => 'edit name test '.date("H:i:s"), 'username' => 'edit username test '.date("H:i:s"), 'email' => 'edit email test '.date("H:i:s"), ])){ $response = [ 'status' => 200, 'message' => 'Data Updated' ]; } return $this->respond($response); } public function delete($id = null){ if(!empty($id)){ if($this->model->delete($id)){ $response = [ 'status' => 200, 'message' => 'Data Deleted' ]; } } return $this->respond($response); } }
ใน index() เราใช้คำสั่ง findAll() ของ model เพื่อแสดงข้อมูลทั้งหมดของตาราง tbl_users ใน create()
เราใช้คำสั่ง save() สำหรับบันทึกหรือสร้างข้อมูลใหม่ เราสามารถใช้คำสั่ง insert() แทนได้ถ้าต้องการ
เมื่อทำการเพิ่มข้อมูลตัวอย่างสมมติสำเร็จ ก็ให้คืนค่ากลับมาเป็นข้อมูล ที่มีค่า status กับค่า message ที่ต้องการ
แสดงหรือใช้งาน ใน show() เราใช้คำสั่ง find() โดยส่งค่า id ที่เป็น primary key ไปดึงข้อมูลเฉพาะที่ตรงกับ id
ที่ส่งเข้ามา มาแสดง ใน update() เราใช้คำสั่ง save() เพิ่มจำลองการบันทึกข้อมูลที่แก้ไข และคืนค่าเป็นสถานะ
การทำรายการ คล้ายการเพิ่มข้อมูล เราสามารถใช้คำสัง update($id,$data) แทน save() ได้ การใช้คำสั่่ง save()
กรณีแก้ไข เราจะต้องส่งค่า id ทีเป็น primary key ที่เป็นการระบุว่า กำลังจะแก้ไขรายการไหน เข้าไปด้วยเสมอ
และสุดท้าย ใน delete() เราใช้คำสั่ง delete() ของ model เพิ่มลบรายการข้อมูล และคืนค่าสถานะการลบกลับไป
และเนื่องจากใน model เรากำหนดค่า $useSoftDeletes = true การลบข้อมูลจึงเป็นเพียงการลบชั่วคราวเท่านั้น
ข้อมูลจะไม่ถูกลบไปจากตารางจริง เกี่ยวกับส่วนนี้ดูเพิ่มเติมเกี่ยวการใช้งาน model
เท่านี้ ก็ถือได้ว่าเป็นอันเสร็จเรียบร้อย สำหรับการสร้าง Users API เบื้องต้นใน CI4
ต่อไปเราจะทดสอบเรียกใช้งานผ่านโปรแกรม Insomnia Core โดย routes ที่เราจะใช้งานจะมีดังนี้
get https://www.mysslweb.com/api/users เรียก index() get https://www.mysslweb.com/api/users/:segment เรียก show() post https://www.mysslweb.com/api/users เรียก create() put https://www.mysslweb.com/api/users/:segment เรียก update() delete https://www.mysslweb.com/api/users/:segment เรียก delete()
ค่าด้านหน้าคือ HTTP Method ที่เราจะสามารถส่งไปเรียกใช้งานได้ ต่อมาเป็นส่วนของ API endpoint URL ที่ต้อง
ระบุใช้งาน ส่วนคำอธิบายด้านหลัง คือ controller method ต่างๆ ที่ถูกเรียกใช้งาน
เรามาเริ่มต้นที่ การเพิ่มข้อมูล จะเพิ่มไปสัก 2 รายการ ดังนี้
ต่อด้วยแสดงข้อมูลทั้งหมด
ต่อด้วยแสดงข้อมูลเฉพาะรายการที่ id = 2
ต่อด้วยการแก้ไขรายการที่มี id = 2
ต่อด้วยแสดงรายการที่มี id = 2 อีกครั้งหลังแก้ไข
ต่อด้วย ลบรายการที่มี id = 2
เราลองกำหนด HTTP Method ที่ไม่ตรงกับที่เรากำหนด เช่นใช้เป็น POST สำหรับการแสดงข้อมูล id = 2
จะเห็นว่า จะเกิด error ขึ้น เนื่องจากไม่สัมพันธ์กับรูปแบบที่เรากำหนดเอาไว้ เพราะการแสดงข้อมูลที่มี id = 2
ต้องเรียกผ่าน GET method เท่านั้น
การใช้งาน HTTP Response สำหรับ API
ตัวอย่างการใช้งานในหัวข้อที่ผ่านมา เราสามารถกำหนดรูปแบบข้อมูล ที่ response มายัง Client ตามต้องการ
การใช้งานข้างต้น ยังถือว่าเป็นรูปแบบที่ยังไม่สมบูรณ์นัก เพราะในการจัดการกับ API ต้องการมีการตรวจสอบค่าต่างๆ
ไม่เว้นแต่กรณีที่ ไม่พบข้อมูล หรือการทำงานเกิดข้อผิดพลาด เราจะส่งกลับข้อมูลในรูปแบบใด จะมาดูที่เนื้อหานี้กัน
สำหรับ CI4 จะมี Class ที่สามารถกำหนดรุปแบบการ Response สำหรับ API โดยเฉพาะชื่อว่า ResponseTrait
มาดูว่า ResponseTrait รองรับการคืนค่า HTTP status code แบบใดบ้าง ดังนี้
// รองรับการกำหนด response method ทั่วไป $this->respond($data, 200); // รองรับการกำหนด failure response $this->fail($errors, 400); // รองรับการกำหนด created response $this->respondCreated($data); // รองรับการกำหนด successfully deleted $this->respondDeleted($data); // Command executed by no response required $this->respondNoContent(); // รองรับการกำหนด Client isn't authorized ไม่ได้รับสิทธิ์เข้าถึง API $this->failUnauthorized(); // รองรับการกำหนด Forbidden action ไม่สามารถใช้งานส่วนที่กำหนดได้ $this->failForbidden(); // รองรับการกำหนด Resource Not Found ไม่พบข้อมูล $this->failNotFound(); // รองรับการกำหนด Data did not validate ข้อมูลไม่ผ่านการตรวจสอบ $this->failValidationError(); // รองรับการกำหนด Resource already exists มีข้อมูลแล้วในระบบ $this->failResourceExists(); // รองรับการกำหนด Resource previously deleted ข้อมูลถูกลบไปก่อนหน้า $this->failResourceGone(); // รองรับการกำหนด Client made too many requests การจำกัดจำนวนครั้งการเข้าถึง API $this->failTooManyRequests(); // รองรับการกำหนด Internal Server Error เกิด error ฝั่ง server $this->failServerError();
เรามาลองปรับใช้งาน ResponseTrait กับ API ของเรา และปรับการส่งค่าข้อมูล สำหรับการเพิ่ม และการแก้ไข
ใหม่ดังนี้
app/Controllers/Api/Users.php
<?php namespace App\Controllers\Api; use CodeIgniter\RESTful\ResourceController; use CodeIgniter\API\ResponseTrait; // เรียกใช้ ResponseTrait class class Users extends ResourceController { use ResponseTrait; // .ใช้ ResponseTrait protected $modelName = 'App\Models\Api\UsersModel'; protected $format = 'json'; public function index() { $response = $this->model->findAll(); return $this->respond($response); } public function create(){ $input = $this->request->getPost(); $data = [ 'name' => $input['name'], 'username' => $input['username'], 'email' => $input['email'], ]; $userID = $this->model->insert($data); // คืนค่า primary key return $this->respondCreated(['id' => $userID]); } public function show($id = null){ $response = $this->model->find($id); if($response){ return $this->respond($response); }else{ return $this->failNotFound('No Data Found with id '.$id); } } public function update($id = null){ $input = $this->request->getRawInput(); $data = [ 'name' => $input['name'], 'username' => $input['username'], 'email' => $input['email'], ]; $user = $this->model->find($id); if($user){ $this->model->update($id, $data); return $this->respond(['id' => $id]); }else{ return $this->failNotFound('No Data Found with id '.$id); } } public function delete($id = null){ if(!empty($id)){ $user = $this->model->find($id); if($user){ $this->model->delete($id); return $this->respondDeleted(['id' => $id]); }else{ return $this->failNotFound('No Data Found with id '.$id); } } } }
เราจะทดสอบเพิ่มข้อมูลใหม่ 2 รายการ เนื่องจาก เราจะส่งข้อมูลเข้าไปด้วย
การเพิ่มข้อมูลข้างต้น เราส่งข้อมูลแบบ POST ในรูปแบบฟอร์ม กำหนด Content-Type ให้มีค่าเท่ากับ
application/x-www-form-urlencoded เมื่อสร้างข้อมูล สังเกตส่วนของ status code ที่เป็น 201 ซึ่งตัว
ResponseTrait จัดการค่านี้ให้จากคำสั่ง respondCreated()
เราลองแสดงข้อมูลที่ไม่มีอยู่ในระบบ สมมติกำหนด id เป็น 3
สังเกต status ที่ได้จากคำสั่ง failNotFound()
ต่อไปทดสอบแก้ไขข้อมูล โดยแก้ไขรายการที่ id = 2
ข้อมูลหลังจากแก้ไข
ต่อไปเราจะลองมาปรับเงื่อนไขเพิ่มเติม โดยการเพิ่มการตรวจสอบความถูกต้องของข้อมูลใน การในเพิ่ม และ
การแก้ไข ซึ่งหากไม่ผ่านเงื่อนไข ก็จะมี error เราจะเอาค่า error นั้น แสดงตอน response กลับมา
ให้เรากำหนดค่าให้กับ $validationRules เบื้องต้นดังนี้
protected $validationRules = [ 'name' => 'required', 'username' => 'required', 'email' => 'required|valid_email', ];
การตรวจสอบความถูกต้องหรือ Validated ข้อมูล จะทำไปพร้อมกับคำสั่ง insert() update() หรือ save()
ดังนั้น เราต้องกำหนดเงื่อนไขเพื่อตรวจสอบ การทำงานของคำสั่งเหล่านี้ด้วย ดังนี้
app/Controllers/Api/Users.php (เฉพาะคำสั่ง insert() และ update()
public function create(){ $input = $this->request->getPost(); $data = [ 'name' => $input['name'], 'username' => $input['username'], 'email' => $input['email'], ]; if($userID = $this->model->insert($data)){ // ตรวจสอบการ validated ไปด้วย return $this->respondCreated(['id' => $userID]); }else{ return $this->failValidationError(implode("",$this->model->errors())); } } public function update($id = null){ $input = $this->request->getRawInput(); $data = [ 'name' => $input['name'], 'username' => $input['username'], 'email' => $input['email'], ]; $user = $this->model->find($id); if($user){ if($this->model->update($id, $data)){ // ตรวจสอบการ validated ไปด้วย return $this->respond(['id' => $id]); }else{ return $this->failValidationError(implode("",$this->model->errors())); } }else{ return $this->failNotFound('No Data Found with id '.$id); } }
เมื่อเรามีการตรวจสอบความถูกต้องของข้อมูล เราจะต้องกำหนดเงื่อนไขการทำคำสั่ง insert() และ update()
โดยใช้ if() ถ้าไม่ผ่านการตรวจสอบ ก็ให้ return คำสั่ง $this->failValidationError() โดยมีข้อมูล error ที่ไม่
ผ่านการตรวจสอบจากคำสั่ง $this->model->errors() แต่เนื่องจาก ต้องให้ข้อมูลในฟังก์ชั่น failValidationError(string)
เป็น string เราก็ใช้คำสั่ง implode() ข้อมูล array ของ error กลับออกไปเป็นข้อมูลแทน
ทดสอบการเพิ่มข้อมูลใหม่ โดยเราไม่กรอก name
ทดสอบอีกครั้งกรอกข้อมูลให้ครบ แต่ให้กรอก email ในรูปแบบที่ไม่ถูกต้อง ก็จะได้เป็น
จากผลลัพธ์ที่ได้ เราจะได้ข้อมูล error ที่เกิดจากการไม่ผ่านเงื่อนไขการตรวจสอบความถูกต้องของข้อมูลไปใช้งาน
นอกจากนั้น เราจะสังเกตเห็นสถานะ status code จะเป็น 400 Bad Request
สำหรับแนวทาง และตัวอย่างการใช้งาน RESTful API ใน CI4 เบื้องต้น จะมีประมาณนี้
เราจะได้ดูการใช้งาน RESTful API เพิ่มเติม กรณี API นั้นๆ มีการจำกัดสิทธิ์การใช้งาน เช่น ต้องล็อกอิน หรือใช้
token ถึงจะสามารถเรียกใช้งานได้ เหล่านี้เป็นต้น จะได้นำเสนอในบทความต่อไป รอติดตาม