CRUD
ตัวอย่างการใช้งานฐานข้อมูล
projects/crud/
โปรเจ็คนี้จะเป็นตัวอย่างที่ค่อนข้างสมบูรณ์ที่สุดนะครับ เพราะจะเป็นการรวมความสามารถทั้งหมดของ Somtum ไว้ในโปรเจ็คเดียว ตั้งแต่การใช้งาน Menu Template ตลอดการใช้งานร่วมกับฐานข้อมูล ศึกษาเพียงโปรเจ็คเดียวได้ครบเลย
<?php
namespace Index\Index;
use Somtum\Http\Request;
use Somtum\Template;
class Controller extends \Somtum\Controller
{
public function index(Request $request)
{
// เริ่มต้นการใช้งาน Template
Template::init('skin/'.self::$cfg->skin);
// โหลดเมนู
$menus = \Somtum\Menu::init(\Index\Menu\Model::getMenus());
// Controller หลัก
$page = createClass('Index\Main\Controller')->execute($request);
// สร้าง View
$view = new \Somtum\View();
// Meta
$view->setMetas(array(
'description' => '<meta name=description content="'.$page->description().'" />',
'keywords' => '<meta name=keywords content="'.$page->keywords().'" />',
));
// แทนที่ลงใน template
$view->setContents(array(
// เมนู
'/{MENUS}/' => $menus->render($page->menu()),
// web title
'/{TITLE}/' => $page->title(),
// โหลดหน้าที่เลือก (html)
'/{CONTENT}/' => $page->detail(),
));
// ส่งออกเป็น HTML
echo $view->renderHTML();
}
}
ผมข้าม index.php มาที่ defaultController เลยนะครับ มีขั้นตอนการทำงานใน \Index\Index\Controller ดังนี้
- อันดับแรก กำหนด Template ที่ต้องการก่อนเลย
- จากนั้นจะไปโหลดเมนู
- ถัดมาเป็นการโหลดโมดูลหรือหน้าที่ต้องการ
- ขั้นตอนต่อมา เป็นการโหลด View สำหรับ Template หลักหรือ index.html
- จากนั้นจึงเตรียมข้อมูลสำหรับแทนที่ลงใน Template หลัก ได้แก่ เมนูที่โหลดไว้แล้ว ข้อความไตเติล และ ส่วนเนื้อหา (ถ้ามีข้อมูลที่จะต้องใส่ลงใน Template หลักเพิ่มเติม ก็สามารถใส่ได้ที่นี่)
- สุดท้าย สั่งประมวลผล Template
<?php
namespace Index\Menu;
class Model
{
public static function getMenus()
{
return array(
'home' => array(
'text' => 'หน้าหลัก',
'url' => 'index.php',
'submenus' => array(
array(
'text' => 'แก้ไข',
'url' => 'index.php?module=write&id=1',
),
),
),
'about' => array(
'text' => 'เกี่ยวกับเรา',
'url' => 'index.php?module=about',
'submenus' => array(
array(
'text' => 'แก้ไข',
'url' => 'index.php?module=write&id=2',
),
),
),
);
}
}
\Index\Menu\Model::getMenus() เป็นคลาสสำหรับเก็บข้อมูลเมนูในรูปแบบแอเรย์ ข้อมูลในเมธอด getMenus อาจมาจากฐานข้อมูล หรือวิธีการอื่นใดก็ได้ เพียงแต่สุดท้ายจะต้องออกมาในรูปแบบที่กำหนดไว้นี้เท่านั้น นอกจากนั้น หากมีกระบวนการ Login เกิดขึ้น และเราต้องการเลือกเมนูตามสถานะสมาชิก เราก็สามารถส่งพารามิเตอร์มาเพื่อจัดการเมนูได้
<?php
namespace Index\Main;
use Somtum\Http\Request;
class Controller extends \Somtum\Controller
{
public function execute(Request $request)
{
// รับค่าจาก $_GET[module] ถ้าไม่มีคืนค่า home
$module = $request->get('module', 'home');
if ($module === 'write') {
// หน้าแก้ไขข้อมูล
return createClass('Index\Write\Controller')->execute($request);
} else {
// หน้าแสดงข้อมูล
$page = \Index\Main\Model::get($module);
if ($page) {
$this->title = $page->title;
$this->description = $page->description;
$this->keywords = $page->keywords;
$this->detail = $page->detail;
$this->menu = $module;
// คืนค่า Controller และข้อมูลจากฐานข้อมูล
return $this;
}
}
// 404 Page Not Found
return createClass('Index\Error\Controller')->execute();
}
}
\Index\Main\Controller::execute() คือเมธอดสำหรับคัดเลือกหน้าเว็บที่ต้องการมาแสดง โดยดูจากพารามิเตอร์ $module ที่รับค่ามาจาก $_GET[module'] ด้วยคำสั่ง $requext->get() ถ้า $module เท่ากับ write จะถูกส่งต่อไปยัง \Index\Write\Controller แต่ถ้าเป็นหน้าเพจ จะถูกส่งต่อไปตรวจสอบกับฐานข้อมูลด้วย \Index\Index\Model::get() เพื่ออ่านข้อมูลหน้าทีเลือก ซึ่งถ้าพบหน้าที่เลือก ก็จะถูกนำไปกำหนดค่าให้กับ property ของ Controller และส่งค่า Controller กลับไปยัง Controller ที่เรียกมา แต่ถ้าไม่พบ จะไปโหลด \Index\Error\Controller มีใช้งานแทน
<?php
namespace Index\Main;
class Model extends \Somtum\Model
{
public static function get($page)
{
// เรียกใช้งาน คลาสภายนอก
$db = new \App\Db();
// SELECT * FROM `gcms_index` WHERE `page`=:page LIMIT 1
return $db->first('index', array('page' => $page));
}
}
คลาส \Index\Main\Model มีเมธอดชื่อ get() สำหรับการอ่านข้อมูลหน้าเว็บที่เลือกจากฐานข้อมูล โดยมีการเรียกใช้ Library ภายนอกจากคลาส \App\Db อยู่ที่ App/Db.php ซึ่งเป็นคลาสสำหรับการเชื่อมต่อกับ Databse แบบ PDO ผมไม่อธิบายวิธีใช้คลาสนี้นะครับ เพราะหากมีคลาสแบบอื่น ก็สามารถนำมาใช้ได้เช่นกัน (ตัวอย่างนี้แสดงให้เห็นว่า หากคลาสปฏิบัติตามกฏ PSR-2 เราจะสามารถนำคลาสมาใชในโปรเจ็คได้ทันที)
เมธอด get ทำหน้าที่อ่านข้อมูลจาก $page ที่เลือก แล้วส่งกลับไปยัง Controller
ในกรณีที่มีการเลือกหน้าแก้ไขข้อมูล คำสั่งจะถูกส่งไปยัง \Index\Write\Controller และ \Index\Write\Controller จะไปทำการโหลดฟอร์มจาก \Index\Write\View อีกที
<?php
namespace Index\Write;
use Somtum\Template;
class View extends \Somtum\View
{
public function render($page)
{
// โหลดฟอร์ม write.html
$template = Template::load('', '', 'write');
// แทนที่ข้อมูลลงใน templlate
$this->setContents(array(
'/{TITLE}/' => $page->title,
'/{KEYWORDS}/' => $page->keywords,
'/{DESCRIPTION}/' => $page->description,
'/{DETAIL}/' => $page->detail,
'/{ID}/' => $page->id,
));
// คืนค่าฟอร์ม HTML
return $this->renderHTML($template);
}
}
ข้ามไปดู \Index\Write\View เลยนะครับ ที่ View จะทำหน้าที่ โหลดฟอร์ม write.hml ซึ่งอยู่ใน Template มาใช้งาน แล้วแทนที่ด้วยข้อมูลที่อ่านมาจาก Model ที่ \Index\Write\Controller เสร็จแล้วแทนที่ข้อมูลลงในฟอร์ม และส่งฟอร์มกลับไปแสดงผล
ข้อสังเกตุ ตัวอย่างนี้แสดงให้เห็นว่า เราจะส่ง Template ที่ต้องการใช้งานให้ View ที่คำสั่ง renderHTML (ถ้าไม่ระบุ จะเป็นการโหลด index.html มาใช้งาน)
<form method="post" action="index.php/Index/Model/Write/submit">
<p><label for="title">Title</label></p>
<p><input type="text" name="title" id="title" size="100" value="{TITLE}"></p>
<p><label for=" keywords">Keywords</label></p>
<p><input type="text" name="keywords" id="keywords" size="100" value="{KEYWORDS}"></p>
<p><label for=" description">Description</label></p>
<p><textarea name="description" id="description" rows="3" cols="100">{DESCRIPTION}</textarea></p>
<p><label for="detail">Detail</label></p>
<p><textarea name="detail" id="detail" rows="3" cols="100">{DETAIL}</textarea></p>
<p><button type="submit">Submit</button><input type="hidden" name="id" value="{ID}"></p>
</form>
กลับไปดูที่ฟอร์มกันก่อน เป็นโค้ด HTML Form ธรรมดา มีการแทนที่ข้อมูลจากฐานข้อมูลใส่ลงในฟอร์ตามตัวแปรต่างๆ ที่กำหนดมาจาก View เช่นเดียวกับการใส่ข้อมูลลงใน Template และใช้ Method POST และมีการส่งค่าเมื่อมีการ Submit ไปที่ action index.php/Index/Model/Write/submit หรือคลาส \Index\Write\Model::submit
<?php
namespace Index\Write;
use Somtum\Http\Request;
class Model extends \Somtum\Model
{
public static function get($id)
{
$db = new \App\Db();
// SELECT * FROM `gcms_index` WHERE `id`=:id LIMIT 1
return $db->first('index', array('id' => $id));
}
public function submit(Request $request)
{
// ตรวจสอบว่าเรียกมาจากไซต์ตัวเองหรือไม่
if ($request->isReferer()) {
// ตรวจสอบว่ามีอะไรส่งมาบ้าง
//var_dump($_POST);
$save = array(
'title' => $request->post('title'),
'keywords' => $request->post('keywords'),
'description' => $request->post('description'),
'detail' => $request->post('detail'),
);
// connect database
$db = new \App\Db();
// ตรวจสอบรายการที่แก้ไข
$search = $db->first('index', array('id' => (int) $request->post('id')));
if ($search) {
// บันทึกการแก้ไข
$db->edit('index', $search->id, $save);
// ตรวจสอบว่ามีอะไรส่งมาบ้าง
//var_dump($save);
// คืนค่า
echo 'บันทึกเรียบร้อย';
} else {
echo 'ไม่พบรายการที่เลือก';
}
} else {
echo 'File Not Found!';
}
}
}
ดูที่โค้ด \Index\Write\Model::submit นะครับ เพราะเมธอด get() จะมีลักษณะคล้ายเมธอดก่อนหน้า ต่างกันแค่ เมธอด get ของคลาสนี้ ค้นหาจาก ID เท่านั้น
- $request->isReferer() ใช้สำหรับตรวจสอบว่ามีการเรียกไฟล์นี้จากภายในโดเมนเดียวกันหรือไม่ ถ้าใช่จะคืนค่า true และไปทำคำสั่งภายใน If ถ้าไม่ใช่ จะแสดง File Not Found!
- ถัดมา ตัวแปร $save (array) จะรับค่าจากการ Submit ด้วยเมธอด $request->post()
- บรรทัดต่อมาเป็นการเรียกใช้คลาส \App\Db
- และเป็นการตรวจสอบรายการที่แก้ไข จาก ID ของข้อมูลที่ส่งมาจากฟอร์ม
- ถ้าพบข้อมูล ก็จะทำการบันทึกลงฐานข้อมูล ที่คำสั่ง edit() ของฐานข้อมูล และคืนค่า "บันทึกเรียบร้อย" แต่ถ้าไม่พบข้อมูลจะคืนค่า "ไม่พบรายการที่เลือก"