โปรเจ็คตัวอย่างเว็บไซต์ร้านค้าออนไลน์ที่ใช้ Javascript API ในการแสดงสินค้า ตอนที่ 2
ตอนที่สอง ผมจะอธิบายในส่วนของการสร้าง REST API ด้วยคชสาร ซึ่งสามารถสร้างได้ง่ายมาก โดยที่โค้ดในส่วนนี้จะอยู่ในโปรเจ็คระบบบัญชีออนไลน์ นะครับ
ไฟล์แรก คือ api.php เป็นไฟล์หลักสำหรับการเรียก API ครับ สามารถใช้ชื่ออื่นก็ได้ (ตัวอย่างนี้มีการใช้ index.php เป็นหน้าเว็บหลักไปแล้ว) จุดที่สำคัญในไฟล์นี้คือมีการกำหนดค่า Router ไปที่ \Gcms\Router และ Controller ไปที่ \Index\Api\Controller
ไฟล์ที่สอง Gcms/Router.php หรือคลาส \Gcms\Router
ใจความสำคัญของคลาสนี้คือมีการกำหนด $rules สำหรับกฏของ Router ในการแยกหน้า ตัวอย่างเช่น
ไฟล์ที่สาม modules/index/controllers/api.php หรือ \Index\Api\Controller ซึ่งเป็น Controller หลักของ API
คลาสนี้มีหน้าที่ในการเลือก Model ที่ต้องการจาก Request ที่ส่งมาจาก Router (ตัวแปร $request) และทำการส่งกลับข้อมูลรูปแบบ JSON ผ่าน Response
เมธอดที่ต้องการส่งมาจาก Router ที่พารามิเตอร์ get('action') หรือเทียบเท่า $_GET['action']
นำเมธอดไปตรวจสอบว่ามีหรือไม่ในคลาส Index\Api\Model
ถ้ามีก็จะเรียกใช้เมธอด คืนค่าผลลัพท์ที่ตัวแปร $result
ในกรณีที่ไม่พบเมธอด ตัวแปร $result จะถูกกำนดค่า error ไปแทน
และสุดท้ายคืนค่าผลลัพท์เป็น JSON โดยใช้คลาส Response
ไฟล์สุดท้าย modules/index/models/api.php หรือคลาส \Index\Api\Model ซึ่งเป็น Model ที่ Controller เรียกใช้ (ผมจะอธิบายทีละเมธอดเลยนะครับ)
เมธอดแรก คืนค่าหมวดหมู่ทั้งหมด หรือเทียบเท่า SQL Query เป็น
เมธอด products คืนค่ารายการสินค้าที่เกี่ยวข้องตามพารามิเตอร์ที่ส่งมา เมธอดนี้จะทำหน้าที่หลายอย่าง เช่น query ข้อมูลสินค้าที่ต้องการตามหมวดหมู่ รวมถึง query ข้อมูลที่มาจากการค้นหา และส่งกลับข้อมูลสำหรับการแบ่งหน้าการแสดงผลด้วย
เมธอดถัดมา เมธอด serach จะไปเรียกใช้เมธอด products อีกที
เมธอดสุดท้าย อ่านค่าสินค้าที่เลือกจาก ID ของสินค้า
จะได้ SQL ประมาณนี้
ผลลัพท์ของ Query ทั้งหมด จะส่งกลับเป็นแอเรย์ กลับไปที่ Controller เพื่อใช้ในการส่งค่ากลับต่อไป
ตัวอย่างด้านบนเป็นผลลัพท์ของ API ในรูปแบบ JSON ที่ส่งกลับ
เพิ่มเติม สำหรับการใช้งาน API ที่อาจมีปัญหาการร้องขอข้อมูลจากเว็บไซต์ต่าง URL กัน (API อยู่โดเมนหนึ่ง และ เว็บไซต์ที่เรียกใช้อยู่อีกโดเมนหนึ่ง) ให้กำหนดค่า Apache ให้ยอมรับการร้องขอข้อมูลโดยการกำหนดที่ไฟล์ .htaccess
สำหรับโปรแกรมที่ผมใช้สำหรับทดสอบการทำงานของ API ผมใช้ตัวนี้เลยครับ https://install.advancedrestclient.com (ตัวที่เป็น extension ของ Chrome ก็มี)
อ่านต่อตอนสุดท้ายคลิกลิงค์ด้านล่างเลยครับ
ไฟล์แรก คือ api.php เป็นไฟล์หลักสำหรับการเรียก API ครับ สามารถใช้ชื่ออื่นก็ได้ (ตัวอย่างนี้มีการใช้ index.php เป็นหน้าเว็บหลักไปแล้ว) จุดที่สำคัญในไฟล์นี้คือมีการกำหนดค่า Router ไปที่ \Gcms\Router และ Controller ไปที่ \Index\Api\Controller
<?php
/**
* api.php
* หน้าเพจสำหรับให้ API เรียกมา
*
* @author Goragod Wiriya <admin@goragod.com>
* @link http://www.kotchasan.com/
* @copyright 2016 Goragod.com
* @license http://www.kotchasan.com/license/
*/
// load Kotchasan
include 'load.php';
// Initial Kotchasan Framework
$app = Kotchasan::createWebApplication('Gcms\Config');
$app->defaultRouter = 'Gcms\Router';
$app->defaultController = 'Index\Api\Controller';
$app->run();
ไฟล์ที่สอง Gcms/Router.php หรือคลาส \Gcms\Router
<?php
/**
* @filesource Gcms/Login.php
* @link http://www.kotchasan.com/
* @copyright 2016 Goragod.com
* @license http://www.kotchasan.com/license/
*/
namespace Gcms;
/**
* Router Class สำหรับ GCMS
*
* @author Goragod Wiriya <admin@goragod.com>
*
* @since 1.0
*/
class Router extends \Kotchasan\Router
{
/**
* กฏของ Router สำหรับการแยกหน้าเว็บไซต์
*
* @var array
*/
protected $rules = array(
// api.php/products/<category_id>/<page>
'/api\.php\/(products)\/([0-9]+)\/([0-9]+)/i' => array('action', 'category_id', 'page'),
// api.php/products/<page>
'/api\.php\/(products)\/([0-9]+)/i' => array('action', 'page'),
// api.php/search/<q>/<page>
'/api\.php\/(search)\/([^\/]+|$)(\/([0-9]+))?/i' => array('action', 'q', '', 'page'),
// api.php/<action>/<id>
'/api\.php\/([a-z]+)(\/([0-9]+))?/i' => array('action', '', 'id'),
);
}
ใจความสำคัญของคลาสนี้คือมีการกำหนด $rules สำหรับกฏของ Router ในการแยกหน้า ตัวอย่างเช่น
'/api\.php\/(products)\/([0-9]+)\/([0-9]+)/i' => array('action', 'category_id', 'page'),
- (products) จะถูก map ไปที่ action ได้ action=products
- ([0-9]+) จะถูก map ไปที่ category_id ได้ category_id=xxx
- ([0-9]+) จะถูก map ไปที่ page ได้ page=xxx
ไฟล์ที่สาม modules/index/controllers/api.php หรือ \Index\Api\Controller ซึ่งเป็น Controller หลักของ API
<?php
/**
* @filesource modules/index/controllers/api.php
* @link http://www.kotchasan.com/
* @copyright 2016 Goragod.com
* @license http://www.kotchasan.com/license/
*/
namespace Index\Api;
use \Kotchasan\Http\Request;
/**
* Controller สำหรับโหลดข้อมูลด้วย Ajax
*
* @author Goragod Wiriya <admin@goragod.com>
*
* @since 1.0
*/
class Controller extends \Kotchasan\Controller
{
/**
* มาจากการเรียกด้วย Ajax
*
* @param Request $request
*/
public function index(Request $request)
{
$action = $request->get('action')->filter('a-z');
if (method_exists('Index\Api\Model', $action)) {
$result = \Index\Api\Model::$action($request);
}
if (!isset($result)) {
$result = array(
'error' => array(
'code' => 400,
'message' => 'bad request'
)
);
}
// Response
$response = new \Kotchasan\Http\Response;
$response->withHeaders(array(
'Content-type' => 'application/json; charset=UTF-8'
))
->withContent(json_encode($result))
->send();
}
}
คลาสนี้มีหน้าที่ในการเลือก Model ที่ต้องการจาก Request ที่ส่งมาจาก Router (ตัวแปร $request) และทำการส่งกลับข้อมูลรูปแบบ JSON ผ่าน Response
$action = $request->get('action')->filter('a-z');
เมธอดที่ต้องการส่งมาจาก Router ที่พารามิเตอร์ get('action') หรือเทียบเท่า $_GET['action']
if (method_exists('Index\Api\Model', $action)) {
นำเมธอดไปตรวจสอบว่ามีหรือไม่ในคลาส Index\Api\Model
$result = \Index\Api\Model::$action($request);
ถ้ามีก็จะเรียกใช้เมธอด คืนค่าผลลัพท์ที่ตัวแปร $result
ในกรณีที่ไม่พบเมธอด ตัวแปร $result จะถูกกำนดค่า error ไปแทน
และสุดท้ายคืนค่าผลลัพท์เป็น JSON โดยใช้คลาส Response
// Response
$response = new \Kotchasan\Http\Response;
$response->withHeaders(array(
'Content-type' => 'application/json; charset=UTF-8'
))
->withContent(json_encode($result))
->send();
ไฟล์สุดท้าย modules/index/models/api.php หรือคลาส \Index\Api\Model ซึ่งเป็น Model ที่ Controller เรียกใช้ (ผมจะอธิบายทีละเมธอดเลยนะครับ)
/**
* คืนค่ารายการหมวดหมู่ทั้งหมด
*
* @param Request $request
* @return array
*/
public static function categories(Request $request)
{
$query = static::create()->db()->createQuery()
->select('category_id', 'topic')
->from('category')
->where(array('type', 0))
->order('category_id')
->toArray()
->cacheOn();
$result = array();
foreach ($query->execute() as $item) {
$result[] = array(
'category_id' => $item['category_id'],
'topic' => $item['topic'],
);
}
return $result;
}
เมธอดแรก คืนค่าหมวดหมู่ทั้งหมด หรือเทียบเท่า SQL Query เป็น
SELECT category_id, topic FROM category WHERE type=0 ORDER BY category_id
เมธอด products คืนค่ารายการสินค้าที่เกี่ยวข้องตามพารามิเตอร์ที่ส่งมา เมธอดนี้จะทำหน้าที่หลายอย่าง เช่น query ข้อมูลสินค้าที่ต้องการตามหมวดหมู่ รวมถึง query ข้อมูลที่มาจากการค้นหา และส่งกลับข้อมูลสำหรับการแบ่งหน้าการแสดงผลด้วย
/**
* คืนค่ารายการสินค้า ถ้ามีการระบุ id มา หมายถึงสินค้าในหมวดที่เลือก
*
* @param Request $request
* @return array
*/
public static function products(Request $request)
{
// ค่าที่ส่งมา
$q = $request->get('q')->topic();
$category_id = $request->get('category_id')->toInt();
$page = $request->get('page')->toInt();
$list_per_page = $request->get('limit', 30)->toInt();
// ตัวแปรสำหรับส่งค่ากลับ
$result = array();
// Model
$model = new static;
$query = $model->db()->createQuery()->from('product');
if ($category_id > 0) {
$query->where(array('category_id', $category_id));
$result['category_id'] = $category_id;
}
$where = array();
if ($q != '') {
foreach (explode(' ', $q) as $item) {
$where[] = array('product_no', 'LIKE', "%$item%");
$where[] = array('topic', 'LIKE', "%$item%");
$where[] = array('description', 'LIKE', "%$item%");
}
}
if (!empty($where)) {
$query->andWhere(\Kotchasan\Database\Sql::WHERE($where, 'OR'));
$result['q'] = $q;
}
// จำนวน
$result['total'] = $query->cacheOn()->count();
$result['totalpage'] = ceil($result['total'] / $list_per_page);
$result['page'] = max(1, ($page > $result['totalpage'] ? $result['totalpage'] : $page));
$result['start'] = $list_per_page * ($result['page'] - 1);
// query
$result['items'] = $query->order('id')
->select()
->order('topic', 'product_no')
->limit($list_per_page, $result['start'])
->cacheOn()
->toArray()
->execute();
// คืนค่า
return $result;
}
เมธอดถัดมา เมธอด serach จะไปเรียกใช้เมธอด products อีกที
/**
* ค้นหารายการสินค้า จาก product_no topic description
*
* @param Request $request
* @return array
*/
public static function search(Request $request)
{
return self::products($request);
}
เมธอดสุดท้าย อ่านค่าสินค้าที่เลือกจาก ID ของสินค้า
/**
* คืนค่ารายละเอียดของสินค้าที่ id
*
* @param Request $request
* @return array
*/
public static function product(Request $request)
{
return static::create()->db()->createQuery()
->from('product')
->where(array('id', $request->get('id')->toInt()))
->cacheOn()
->toArray()
->first();
}
จะได้ SQL ประมาณนี้
SELECT * FROM product WHERE id=? LIMIT 1
ผลลัพท์ของ Query ทั้งหมด จะส่งกลับเป็นแอเรย์ กลับไปที่ Controller เพื่อใช้ในการส่งค่ากลับต่อไป
{
"id": 1,
"product_no": "0111900300104",
"topic": "Welness Smart Chek เครื่องตรวจวัดน้ำตาลกลูโคสในเลือด",
"description": "ตรวจวัดระดับน้ำตาลได้รวดเร็วแม่นยำ ด้วยวิธีการง่ายๆแค่ 4 ขั้นตอน",
"price": "1990.00",
"url": "http://www.tvdirect.tv/welness-smart-check",
"image": "http://cdns.cdntvdirect.com/media/catalog/product/s/m/smart-chek_1_2.jpg",
"last_update": 0,
"vat": "0.00",
"unit": "",
"category_id": 29,
"count_stock": 0
}
ตัวอย่างด้านบนเป็นผลลัพท์ของ API ในรูปแบบ JSON ที่ส่งกลับ
เพิ่มเติม สำหรับการใช้งาน API ที่อาจมีปัญหาการร้องขอข้อมูลจากเว็บไซต์ต่าง URL กัน (API อยู่โดเมนหนึ่ง และ เว็บไซต์ที่เรียกใช้อยู่อีกโดเมนหนึ่ง) ให้กำหนดค่า Apache ให้ยอมรับการร้องขอข้อมูลโดยการกำหนดที่ไฟล์ .htaccess
RewriteEngine On
# Cross domain access
Header add Access-Control-Allow-Origin "*"
Header add Access-Control-Allow-Headers "origin, x-requested-with, content-type"
Header add Access-Control-Allow-Methods "PUT, GET, POST, DELETE, OPTIONS"
สำหรับโปรแกรมที่ผมใช้สำหรับทดสอบการทำงานของ API ผมใช้ตัวนี้เลยครับ https://install.advancedrestclient.com (ตัวที่เป็น extension ของ Chrome ก็มี)
อ่านต่อตอนสุดท้ายคลิกลิงค์ด้านล่างเลยครับ