ข้ามไปยังเนื้อหา

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 ดังนี้

  1. อันดับแรก กำหนด Template ที่ต้องการก่อนเลย
  2. จากนั้นจะไปโหลดเมนู
  3. ถัดมาเป็นการโหลดโมดูลหรือหน้าที่ต้องการ
  4. ขั้นตอนต่อมา เป็นการโหลด View สำหรับ Template หลักหรือ index.html
  5. จากนั้นจึงเตรียมข้อมูลสำหรับแทนที่ลงใน Template หลัก ได้แก่ เมนูที่โหลดไว้แล้ว ข้อความไตเติล และ ส่วนเนื้อหา (ถ้ามีข้อมูลที่จะต้องใส่ลงใน Template หลักเพิ่มเติม ก็สามารถใส่ได้ที่นี่)
  6. สุดท้าย สั่งประมวลผล 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&amp;id=1',
                    ),
                ),
            ),
            'about' => array(
                'text' => 'เกี่ยวกับเรา',
                'url' => 'index.php?module=about',
                'submenus' => array(
                    array(
                        'text' => 'แก้ไข',
                        'url' => 'index.php?module=write&amp;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 เท่านั้น

  1. $request->isReferer() ใช้สำหรับตรวจสอบว่ามีการเรียกไฟล์นี้จากภายในโดเมนเดียวกันหรือไม่ ถ้าใช่จะคืนค่า true และไปทำคำสั่งภายใน If ถ้าไม่ใช่ จะแสดง File Not Found!
  2. ถัดมา ตัวแปร $save (array) จะรับค่าจากการ Submit ด้วยเมธอด $request->post()
  3. บรรทัดต่อมาเป็นการเรียกใช้คลาส \App\Db
  4. และเป็นการตรวจสอบรายการที่แก้ไข จาก ID ของข้อมูลที่ส่งมาจากฟอร์ม
  5. ถ้าพบข้อมูล ก็จะทำการบันทึกลงฐานข้อมูล ที่คำสั่ง edit() ของฐานข้อมูล และคืนค่า "บันทึกเรียบร้อย" แต่ถ้าไม่พบข้อมูลจะคืนค่า "ไม่พบรายการที่เลือก"

หมายเหตุ โค้ดตัวอย่างมีการปิดการบันทึกข้อมูลลงฐานข้อมูลไว้ ถ้าต้องการจะบันทึกให้เอา comment ออก และเนื่องจากตัวอย่างนี้มีการใช้งานฐานข้อมุล ก่อนการทดสอบจะต้องมีการสร้างฐานข้อมูล จากไฟล์ database.sql ก่อน และทำการกำหนดค่าฐานข้อมูลให้ถูกต้องที่ /settings/config.php ด้วย

^