Kotchasan PHP Framework

[ตอนที่ 5] เวิร์คช้อป CMS อย่างง่าย เก็บข้อมูลลงฐานข้อมูล

ในบทนี้ผมจะเขียนถึงการสร้างโมดูลสมุดเยี่ยม (Guest Book) เนื่องจากผมมองว่าโมดูลสมุดเยี่ยมนี่แหละที่สามารถอธิบายความสามารถของคชสารได้หลายอย่าง ในแบบที่ไม่ซับซ้อนนัก ซึ่งถ้าเข้าใจคอนเซ็ปต์ของสมุดเยี่ยมก็สามารถพัฒนาต่อเป็นบอร์ดหรือบทความได้ไม่ยากเพราะต่างก็มีอะไรหลายๆอย่างคล้ายๆกัน เช่น
  • สมุดเยี่ยมจะต้องมีฟอร์มสำหรับเขียนสมุดเยี่ยม ซึ่งจะมีลักษณะคล้ายๆกับการตั้งกระทู้ หรือการแสดงความคิดเห็นของบอร์ด
  • สมุดเยี่ยมจะต้องมีการแสดงเนื้อหาที่เขียนในลักษณะของลิสต์รายการ คล้ายๆกับการแสดงรายการบทความ หรือการแสดงรายการความคิดเห็น
  • สมุดเยี่ยมจะต้องมีการแบ่งหน้าการแสดงผล ซึ่งทั้งบอร์ดหรือบทความก็ต้องมีเช่นกัน
คงจะพอมองภาพออกและนำไปประยุกต์ต่อกันได้นะครับ

เริ่มต้นกันที่ส่วนแรก เมื่อมีการเรียกสมุดเยี่ยม จะเป็นการเรียกไปยัง Guestbook\Index\Controller ซึ่งเป็นคอนโทรลเลอร์หลักของ Guest Book นั่นเอง
namespace Guestbook\Index;

use \Kotchasan\Http\Request;

class Controller extends \Kotchasan\Controller
{

  public function init(Request $request, $module)
  {
    // query ข้อมูล
    $index = \Guestbook\Index\Model::get($request);
    // คืนค่าข้อมูลโมดูล
    return (object)array(
        'module' => $module,
        'title' => 'สมุดเยี่ยม',
        'detail' => \Guestbook\Index\View::render($request, $index)
    );
  }
}

เนื่องจากจริงๆแล้ว สมุดเยี่ยมจะมีเพียงหน้าเดียวแสดงรายการต่อๆกันไปเท่านั้น ดังนั้นมันจึงมีโค้ดที่ไม่ซับซ้อน
// query ข้อมูล
$index = \Guestbook\Index\Model::get($request);

บรรทัดนี้จะเป็นการอ่านข้อมูลออกมาจากฐานข้อมูล
'detail' => \Guestbook\Index\View::render($request, $index)

และข้อมูลที่ได้จะถุกส่งไปแสดงผลด้วย View และส่งผลลัพท์จาก View ไปใส่ตัวแปร detail เพื่อส่งกลับไปแสดงผล

ลองไปดูโค้ด \Guestbook\Index\Model::get()
namespace Guestbook\Index;

use \Kotchasan\Http\Request;

class Model extends \Kotchasan\Model
{

  public static function get(Request $request)
  {
    $model = new static;
    $query = $model->db()->createQuery()->from('guestbook');
    // เปิดใช้งาน cache ถ้าไม่มี $_GET[visited]
    if (!$request->get('visited')->exists()) {
      $query->cacheOn();
    }
    // จำนวน
    $index = (object)array(
        'list_per_page' => 10,
        'total' => $query->count()
    );
    // ข้อมูลแบ่งหน้า
    $index->page = $request->get('page')->toInt();
    $index->totalpage = ceil($index->total / $index->list_per_page);
    $index->page = max(1, ($index->page > $index->totalpage ? $index->totalpage : $index->page));
    $index->start = $index->list_per_page * ($index->page - 1);
    // query
    $query->select()->order('create_date DESC')->limit($index->list_per_page, $index->start);
    // เปิดใช้งาน cache ถ้าไม่มี $_GET[visited]
    if (!$request->get('visited')->exists()) {
      $query->cacheOn();
    }
    $index->items = $query->execute();
    // คืนค่า
    return $index;
  }
}

อธิบายโค้ดทีละส่วนนะครับ
$model = new static;
$query = $model->db()->createQuery()->from('guestbook');

เป็นการเรียกการใช้งาน QueryBuilder เรียกตาราง guestbook
// เปิดใช้งาน cache ถ้าไม่มี $_GET[visited]
if (!$request->get('visited')->exists()) {
  $query->cacheOn();
}

โค้ดชุดนี้บอกว่า ถ้ามีตัวแปร visited อยู่ใน URL ด้วยห้ามโหลดข้อมูลจากแคช เช่น เมื่อมีการลบข้อมูลหรือมีการเขียนสมุดเยี่ยม เพื่อให้สมุดเยี่ยมทำการโหลดข้อมูลล่าสุดมาแสดงผล (ถ้าไม่มี สามารถใช้งานแคชได้ เช่นการเรียกหน้าเพจตามปกติ)
// จำนวน
$ndex = (object)array(
    'list_per_page' => 10,
    'total' => $query->count()
);

นับจำนวน ข้อความในสมุดเยี่ยมทั้งหมด ด้วย $query->count() และกำหนดค่ารายการแสดงผลสมุดเยี่ยมต่อหนึ่งหน้า list_per_page ไว้ที่ 10 รายการต่อหน้า ค่าเหล่านี้จะใช้ส่งกลับด้วย เลยเก็บค่าไว้ที่ object $index
// คำนวณข้อมูลสำหรับใช้ในการแบ่งหน้า
// หน้าที่ต้องการ
$index->page = $request->get('page')->toInt();
// จำนวนหน้าทั้งหมด
$index->totalpage = ceil($index->total / $index->list_per_page);
// คำนวณหน้าที่กำลังแสดงผล เริ่มต้นที่หน้า 1 และไม่เกินจำนวนหน้าทั้งหมด
$index->page = max(1, ($index->page > $index->totalpage ? $index->totalpage : $index->page));
// รายการเริ่มต้นที่ต้องการ
$index->start = $index->list_per_page * ($index->page - 1);

โค้ดด้านบนใช้คำนวณการแสดงผลรายการตามหน้าที่เลือก คำอธิบายอยู่ในโค้ดแล้ว
// query
$query->select()->order('create_date DESC')->limit($index->list_per_page, $index->start);

กำหนดข้อมูลที่ได้ใส่ลงใน Query
  • select() ไม่มีพารามิเตอร์ หมายถึง SELECT *
  • order() หมายถึง ORDER BY create_date DESC
  • limit() เช่น LIMIT 1,10
ส่วน form() เราเรียกแล้วตั้งแต่ query ก่อนหน้า ไม่ต้องเรียกซ้ำอีก สังเกตุนะครับ ว่าการใช้ QueryBuilder เราไม่จำเป็นต้องเรียงลำดับคำสั่งให้ถูกต้องเหมือนการเขียนคำสั่ง SQL ซึ่งเมื่อสั่งประมวลผลในขั้นนตอนสุดท้าย ตัว QueryBuilder จะทำการแปลคำสั่งให้เอง
// เปิดใช้งาน cache ถ้าไม่มี $_GET[visited]
if (!$request->get('visited')->exists()) {
  $query->cacheOn();
}
$index->items = $query->execute();

ส่วนบล๊อกสุดทายก็จะมีการตรวจสอบตัวแปร visited เพื่อเปิดใช้งานแคช และทำการ Query ผลลัพท์ที่ต้องการออกมาในบรรทัดสุดท้ายด้วย $query->execute(); ก่อนที่จะทำการคืนค่าตัวแปร $index ที่เก็บผลลัพท์ทั้งหมดไว้กลับไป