Kotchasan Framework Documentation

Kotchasan Framework Documentation

คู่มือการพัฒนาเว็บเพจพื้นฐานด้วย Kotchasan

TH 07 Feb 2026 02:20

คู่มือการพัฒนาเว็บเพจพื้นฐานด้วย Kotchasan

บทนำ

คู่มือนี้จะแนะนำการพัฒนาเว็บเพจพื้นฐานด้วย Kotchasan Framework ตั้งแต่การสร้าง CRUD operations, การจัดการผู้ใช้, การทำ authentication, การจัดการฟอร์ม, การตรวจสอบข้อมูล, และการอัปโหลดไฟล์

สารบัญ

  1. การเตรียมโปรเจกต์
  2. การสร้าง CRUD Operations
  3. ระบบ Authentication
  4. การจัดการฟอร์ม
  5. การตรวจสอบข้อมูล
  6. การอัปโหลดไฟล์
  7. ตัวอย่างเว็บเพจสมบูรณ์
  8. Best Practices

การเตรียมโปรเจกต์

1. โครงสร้างโปรเจกต์

myproject/
├── modules/
│   └── blog/
│       ├── controllers/
│       ├── models/
│       ├── views/
│       └── template/
├── settings/
│   ├── config.php
│   └── database.php
├── datas/
│   ├── cache/
│   ├── logs/
│   └── uploads/
└── index.php

2. การกำหนดค่าฐานข้อมูล

// settings/database.php
return [
    'default' => [
        'driver' => 'mysql',
        'host' => 'localhost',
        'database' => 'myblog',
        'username' => 'root',
        'password' => '',
        'charset' => 'utf8mb4',
        'collation' => 'utf8mb4_unicode_ci',
        'prefix' => 'blog_'
    ]
];

3. การสร้างตาราง

-- ตารางผู้ใช้
CREATE TABLE blog_users (
    id INT AUTO_INCREMENT PRIMARY KEY,
    name VARCHAR(100) NOT NULL,
    email VARCHAR(100) UNIQUE NOT NULL,
    password VARCHAR(255) NOT NULL,
    role ENUM('admin', 'user') DEFAULT 'user',
    status ENUM('active', 'inactive') DEFAULT 'active',
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);

-- ตารางโพสต์
CREATE TABLE blog_posts (
    id INT AUTO_INCREMENT PRIMARY KEY,
   user_id INT NOT NULL,
    title VARCHAR(200) NOT NULL,
    content TEXT NOT NULL,
    status ENUM('draft', 'published') DEFAULT 'draft',
    views INT DEFAULT 0,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    FOREIGN KEY (user_id) REFERENCES blog_users(id) ON DELETE CASCADE
);

-- ตารางหมวดหมู่
CREATE TABLE blog_categories (
    id INT AUTO_INCREMENT PRIMARY KEY,
    name VARCHAR(100) NOT NULL,
    slug VARCHAR(100) UNIQUE NOT NULL,
    description TEXT,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

-- ตารางความสัมพันธ์โพสต์-หมวดหมู่
CREATE TABLE blog_post_categories (
    post_id INT NOT NULL,
    category_id INT NOT NULL,
    PRIMARY KEY (post_id, category_id),
    FOREIGN KEY (post_id) REFERENCES blog_posts(id) ON DELETE CASCADE,
    FOREIGN KEY (category_id) REFERENCES blog_categories(id) ON DELETE CASCADE
);

การสร้าง CRUD Operations

1. Model สำหรับ CRUD

// modules/blog/models/post.php
namespace Blog\Post;

use Kotchasan\Model;

class Model extends \Kotchasan\Model
{
    protected $table = 'posts';

    /**
     * ดึงโพสต์ทั้งหมด (Read)
     */
    public function getAllPosts($limit = 10, $offset = 0)
    {
        return $this->db->select('p.*', 'u.name as author_name')
            ->from($this->table . ' p')
            ->leftJoin('users u', 'u.id', 'p.user_id')
            ->where('p.status', '=', 'published')
            ->orderBy('p.created_at', 'DESC')
            ->limit($limit, $offset)
            ->fetchAll();
    }

    /**
     * ดึงโพสต์ตาม ID (Read)
     */
    public function getPostById($id)
    {
        $post = $this->db->select('p.**', 'u.name as author_name')
            ->from($this->table . ' p')
            ->leftJoin('users u', 'u.id', 'p.user_id')
            ->where('p.id', '=', $id)
            ->first();

        if ($post) {
            // เพิ่มจำนวนการดู
            $this->db->update($this->table)
                ->set(['views' => 'views + 1'])
                ->where('id', '=', $id)
                ->execute();
        }

        return $post;
    }

    /**
     * สร้างโพสต์ใหม่ (Create)
     */
    public function createPost($data)
    {
        // ตรวจสอบข้อมูล
        $this->validatePostData($data);

        $data['created_at'] = date('Y-m-d H:i:s');
        $data['status'] = $data['status'] ?? 'draft';

        $this->db->insert($this->table)
            ->values($data)
            ->execute();

        return $this->db->lastInsertId();
    }

    /**
     * อัปเดตโพสต์ (Update)
     */
    public function updatePost($id, $data)
    {
        $this->validatePostData($data);

        $data['updated_at'] = date('Y-m-d H:i:s');

        return $this->db->update($this->table)
            ->set($data)
            ->where('id', '=', $id)
            ->execute();
    }

    /**
     * ลบโพสต์ (Delete)
     */
    public function deletePost($id)
    {
        return $this->db->delete($this->table)
            ->where('id', '=', $id)
            ->execute();
    }

    /**
     * ตรวจสอบข้อมูลโพสต์
     */
    private function validatePostData($data)
    {
        if (empty($data['title'])) {
            throw new \Exception('กรุณากรอกหัวข้อ');
        }

        if (empty($data['content'])) {
            throw new \Exception('กรุณากรอกเนื้อหา');
        }

        if (empty($data['user_id'])) {
            throw new \Exception('ไม่พบข้อมูลผู้เขียน');
        }
    }

    /**
     * ค้นหาโพสต์
     */
    public function searchPosts($keyword)
    {
        return $this->db->select('p.*', 'u.name as author_name')
            ->from($this->table . ' p')
            ->leftJoin('users u', 'u.id', 'p.user_id')
            ->where('p.status', '=', 'published')
            ->where('p.title', 'LIKE', "%{$keyword}%")
            ->orWhere('p.content', 'LIKE', "%{$keyword}%")
            ->orderBy('p.created_at', 'DESC')
            ->fetchAll();
    }
}

2. Controller สำหรับ CRUD

// modules/blog/controllers/post.php
namespace Blog\Post;

use Gcms\Login;
use Kotchasan\Http\Request;

class Controller extends \Kotchasan\Controller
{
    /**
     * แสดงรายการโพสต์
     */
    public function render(Request $request)
    {
        $model = new Model();
        $page = $request->request('page')->toInt() ?: 1;
        $limit = 10;
        $offset = ($page - 1)  $limit;

        $posts = $model->getAllPosts($limit, $offset);

        $view = new View();
        return $view->render($posts, $page);
    }

    /**
     * แสดงโพสต์เดียว
     */
    public function view(Request $request)
    {
        $id = $request->request('id')->toInt();

        if ($id <= 0) {
            return new \Kotchasan\Http\NotFound();
        }

        $model = new Model();
        $post = $model->getPostById($id);

        if (!$post) {
            return new \Kotchasan\Http\NotFound();
        }

        $view = new View();
        return $view->viewPost($post);
    }

    /**
     * แสดงฟอร์มสร้าง/แก้ไขโพสต์
     */
    public function form(Request $request)
    {
        // ตรวจสอบการ login
        $login = Login::isMember();
        if (!$login) {
            return new \Kotchasan\Http\NotFound();
        }

        $id = $request->request('id')->toInt();
        $post = [];

        if ($id > 0) {
            $model = new Model();
            $post = $model->getPostById($id);

            // ตรวจสอบสิทธิ์แก้ไข
            if (!$post || ($post['user_id'] != $login['id'] && $login['role'] != 'admin')) {
                return new \Kotchasan\Http\NotFound();
            }
        }

        $view = new View();
        return $view->form($post, $login);
    }
}

3. View สำหรับ CRUD

// modules/blog/views/post.php
namespace Blog\Post;

use Kotchasan\Html;
use Kotchasan\Http\Request;

class View extends \Gcms\View
{
    /**
     * แสดงรายการโพสต์
     */
    public function render($posts, $currentPage)
    {
        $container = Html::create('div', ['class' => 'blog-container']);

        // หัวข้อ
        $container->add('h1', [], 'บล็อกของเรา');

        // ฟอร์มค้นหา
        $searchForm = $container->add('form', [
            'method' => 'GET',
            'class' => 'search-form'
        ]);
        $searchForm->add('input', [
            'type' => 'text',
            'name' => 'search',
            'placeholder' => 'ค้นหาโพสต์...',
            'value' => $_GET['search'] ?? ''
        ]);
        $searchForm->add('button', ['type' => 'submit'], 'ค้นหา');

        // รายการโพสต์
        if (empty($posts)) {
            $container->add('p', [], 'ไม่พบโพสต์');
        } else {
            foreach ($posts as $post) {
                $article = $container->add('article', ['class' => 'post-item']);

                $article->add('h2')->add('a', [
                    'href' => 'index.php?module=blog&page=post&action=view&id=' . $post['id']
                ], $post['title']);

                $meta = $article->add('div', ['class' => 'post-meta']);
                $meta->add('span', [], 'โดย ' . $post['author_name']);
                $meta->add('span', [], ' | ' . date('d/m/Y H:i', strtotime($post['created_at'])));
                $meta->add('span', [], ' | ดู ' . number_format($post['views']) . ' ครั้ง');

                $content = substr(strip_tags($post['content']), 0, 200);
                $article->add('p', [], $content . '...');

                $article->add('a', [
                    'href' => 'index.php?module=blog&page=post&action=view&id=' . $post['id'],
                    'class' => 'read-more'
                ], 'อ่านต่อ');
            }
        }

        // Pagination
        $this->renderPagination($container, $currentPage);

        return $container->render();
    }

    /**
     * แสดงโพสต์เดียว
     */
    public function viewPost($post)
    {
        $container = Html::create('div', ['class' => 'post-container']);

        $container->add('h1', [], $post['title']);

        $meta = $container->add('div', ['class' => 'post-meta']);
        $meta->add('span', [], 'โดย ' . $post['author_name']);
        $meta->add('span', [], ' | ' . date('d/m/Y H:i', strtotime($post['created_at'])));
        $meta->add('span', [], ' | ดู ' . number_format($post['views']) . ' ครั้ง');

        $container->add('div', ['class' => 'post-content'], $post['content']);

        // ปุ่มกลับ
        $container->add('a', [
            'href' => 'index.php?module=blog&page=post',
            'class' => 'back-button'
        ], '← กลับไปรายการโพสต์');

        return $container->render();
    }
}

ระบบ Authentication

1. User Model

// modules/blog/models/user.php
namespace Blog\User;

use Kotchasan\Model;
use Kotchasan\Password;

class Model extends \Kotchasan\Model
{
    protected $table = 'users';

    /**
     * ตรวจสอบการ login
     */
    public function authenticate($email, $password)
    {
        $user = $this->db->select('*')
            ->from($this->table)
            ->where('email', '=', $email)
            ->where('status', '=', 'active')
            ->first();

        if ($user && password_verify($password, $user['password'])) {
            // อัปเดตเวลา login ล่าสุด
            $this->db->update($this->table)
                ->set(['last_login' => date('Y-m-d H:i:s')])
                ->where('id', '=', $user['id'])
                ->execute();

            return $user;
        }

        return false;
    }

    /**
     * สร้างผู้ใช้ใหม่
     */
    public function register($data)
    {
        // ตรวจสอบอีเมลซ้ำ
        $existing = $this->db->select('id')
            ->from($this->table)
            ->where('email', '=', $data['email'])
            ->first();

        if ($existing) {
            throw new \Exception('อีเมลนี้มีผู้ใช้แล้ว');
        }

        // เข้ารหัสรหัสผ่าน
        $data['password'] = password_hash($data['password'], PASSWORD_DEFAULT);
        $data['created_at'] = date('Y-m-d H:i:s');
        $data['role'] = 'user';
        $data['status'] = 'active';

        $this->db->insert($this->table)
            ->values($data)
            ->execute();

        return $this->db->lastInsertId();
    }

    /**
     * ดึงข้อมูลผู้ใช้ตาม ID
     */
    public function getUserById($id)
    {
        return $this->db->select('*')
            ->from($this->table)
            ->where('id', '=', $id)
            ->where('status', '=', 'active')
            ->first();
    }

    /**
     * อัปเดตโปรไฟล์
     */
    public function updateProfile($id, $data)
    {
        // ไม่อนุญาตให้แก้ไขอีเมลและรหัสผ่านที่นี่
        unset($data['email'], $data['password'], $data['role']);

        $data['updated_at'] = date('Y-m-d H:i:s');

        return $this->db->update($this->table)
            ->set($data)
            ->where('id', '=', $id)
            ->execute();
    }
}

2. Login Controller

// modules/blog/controllers/login.php
namespace Blog\Login;

use Kotchasan\Http\Request;
use Kotchasan\Session;

class Controller extends \Kotchasan\Controller
{
    /**
     * แสดงฟอร์ม login
     */
    public function render(Request $request)
    {
        // ถ้า login แล้วให้ redirect
        if (Session::get('user_id')) {
            header('Location: index.php?module=blog&page=dashboard');
            exit;
        }

        $view = new View();
        return $view->loginForm();
    }

    /**
     * ประมวลผล login
     */
    public function authenticate(Request $request)
    {
        if ($request->initSession() && $request->isSafe()) {
            try {
                $email = $request->post('email')->toString();
                $password = $request->post('password')->toString();

                if (empty($email) || empty($password)) {
                    throw new \Exception('กรุณากรอกอีเมลและรหัสผ่าน');
                }

                $model = new \Blog\User\Model();
                $user = $model->authenticate($email, $password);

                if ($user) {
                    // บันทึก session
                    Session::set('user_id', $user['id']);
                    Session::set('user_name', $user['name']);
                    Session::set('user_role', $user['role']);

                    return [
                        'alert' => 'เข้าสู่ระบบสำเร็จ',
                        'location' => 'index.php?module=blog&page=dashboard'
                    ];
                } else {
                    throw new \Exception('อีเมลหรือรหัสผ่านไม่ถูกต้อง');
                }

            } catch (\Exception $e) {
                return [
                    'alert' => $e->getMessage(),
                    'input' => 'email'
                ];
            }
        }

        return [
            'alert' => 'การเข้าสู่ระบบไม่ถูกต้อง'
        ];
    }

    /**
     * ออกจากระบบ
     */
    public function logout(Request $request)
    {
        Session::destroy();
        header('Location: index.php?module=blog&page=login');
        exit;
    }
}

3. Register Controller

// modules/blog/controllers/register.php
namespace Blog\Register;

use Kotchasan\Http\Request;

class Controller extends \Kotchasan\Controller
{
    /**
     * แสดงฟอร์มสมัครสมาชิก
     */
    public function render(Request $request)
    {
        $view = new View();
        return $view->registerForm();
    }

    /**
     * ประมวลผลการสมัครสมาชิก
     */
    public function submit(Request $request)
    {
        if ($request->initSession() && $request->isSafe()) {
            try {
                $data = [
                    'name' => $request->post('name')->toString(),
                    'email' => $request->post('email')->toString(),
                    'password' => $request->post('password')->toString(),
                ];

                $confirmPassword = $request->post('confirm_password')->toString();

                // ตรวจสอบข้อมูล
                if (empty($data['name'])) {
                    throw new \Exception('กรุณากรอกชื่อ');
                }

                if (empty($data['email']) || !filter_var($data['email'], FILTER_VALIDATE_EMAIL)) {
                    throw new \Exception('กรุณากรอกอีเมลที่ถูกต้อง');
                }

                if (strlen($data['password']) < 6) {
                    throw new \Exception('รหัสผ่านต้องมีอย่างน้อย 6 ตัวอักษร');
                }

                if ($data['password'] !== $confirmPassword) {
                    throw new \Exception('รหัสผ่านไม่ตรงกัน');
                }

                $model = new \Blog\User\Model();
                $userId = $model->register($data);

                return [
                    'alert' => 'สมัครสมาชิกสำเร็จ กรุณาเข้าสู่ระบบ',
                    'location' => 'index.php?module=blog&page=login'
                ];

            } catch (\Exception $e) {
                return [
                    'alert' => $e->getMessage(),
                    'input' => 'name'
                ];
            }
        }

        return [
            'alert' => 'การสมัครสมาชิกไม่ถูกต้อง'
        ];
    }
}

การจัดการฟอร์ม

1. ฟอร์ม Login

// modules/blog/views/login.php
namespace Blog\Login;

use Kotchasan\Html;

class View extends \Gcms\View
{
    public function loginForm()
    {
        $container = Html::create('div', ['class' => 'login-container']);

        $container->add('h2', [], 'เข้าสู่ระบบ');

        $form = $container->add('form', [
            'id' => 'login_form',
            'class' => 'login-form',
            'action' => 'index.php/blog/login/authenticate',
            'onsubmit' => 'doFormSubmit',
            'ajax' => true,
            'token' => true
        ]);

        // อีเมล
        $group = $form->add('div', ['class' => 'form-group']);
        $group->add('label', [], 'อีเมล');
        $group->add('input', [
            'type' => 'email',
            'name' => 'email',
            'required' => true,
            'placeholder' => 'กรอกอีเมล'
        ]);

        // รหัสผ่าน
        $group = $form->add('div', ['class' => 'form-group']);
        $group->add('label', [], 'รหัสผ่าน');
        $group->add('input', [
            'type' => 'password',
            'name' => 'password',
            'required' => true,
            'placeholder' => 'กรอกรหัสผ่าน'
        ]);

        // ปุ่ม submit
        $form->add('button', [
            'type' => 'submit',
            'class' => 'btn btn-primary'
        ], 'เข้าสู่ระบบ');

        // ลิงก์สมัครสมาชิก
        $container->add('p')->add('a', [
            'href' => 'index.php?module=blog&page=register'
        ], 'ยังไม่มีบัญชี? สมัครสมาชิก');

        return $container->render();
    }
}

2. ฟอร์มสร้าง/แก้ไขโพสต์

// modules/blog/views/post.php (เพิ่มเมธอด)
public function form($post, $user)
{
    $isEdit = !empty($post);

    $container = Html::create('div', ['class' => 'post-form-container']);

    $container->add('h2', [], $isEdit ? 'แก้ไขโพสต์' : 'สร้างโพสต์ใหม่');

    $form = $container->add('form', [
        'id' => 'post_form',
        'class' => 'post-form',
        'action' => 'index.php/blog/post/save',
        'onsubmit' => 'doFormSubmit',
        'ajax' => true,
        'token' => true
    ]);

    // ID (สำหรับแก้ไข)
    if ($isEdit) {
        $form->add('input', [
            'type' => 'hidden',
            'name' => 'id',
            'value' => $post['id']
        ]);
    }

    // หัวข้อ
    $group = $form->add('div', ['class' => 'form-group']);
    $group->add('label', [], 'หัวข้อ ');
    $group->add('input', [
        'type' => 'text',
        'name' => 'title',
        'value' => $post['title'] ?? '',
        'required' => true,
        'placeholder' => 'กรอกหัวข้อโพสต์'
    ]);

    // เนื้อหา
    $group = $form->add('div', ['class' => 'form-group']);
    $group->add('label', [], 'เนื้อหา ');
    $group->add('textarea', [
        'name' => 'content',
        'required' => true,
        'rows' => 10,
        'placeholder' => 'กรอกเนื้อหาโพสต์'
    ], $post['content'] ?? '');

    // สถานะ
    $group = $form->add('div', ['class' => 'form-group']);
    $group->add('label', [], 'สถานะ');
    $select = $group->add('select', ['name' => 'status']);
    $select->add('option', [
        'value' => 'draft',
        'selected' => ($post['status'] ?? 'draft') === 'draft'
    ], 'แบบร่าง');
    $select->add('option', [
        'value' => 'published',
        'selected' => ($post['status'] ?? '') === 'published'
    ], 'เผยแพร่');

    // ปุ่ม submit
    $buttonGroup = $form->add('div', ['class' => 'button-group']);
    $buttonGroup->add('button', [
        'type' => 'submit',
        'class' => 'btn btn-primary'
    ], $isEdit ? 'อัปเดต' : 'บันทึก');

    $buttonGroup->add('a', [
        'href' => 'index.php?module=blog&page=dashboard',
        'class' => 'btn btn-secondary'
    ], 'ยกเลิก');

    return $container->render();
}
}

การตรวจสอบข้อมูล

1. Validation Helper

// modules/blog/models/validator.php
namespace Blog\Validator;

class Model
{
    /**
     * ตรวจสอบอีเมล
     */
    public static function validateEmail($email)
    {
        if (empty($email)) {
            throw new \Exception('กรุณากรอกอีเมล');
        }

        if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
            throw new \Exception('รูปแบบอีเมลไม่ถูกต้อง');
        }

        return true;
    }

    /**
     * ตรวจสอบรหัสผ่าน
     */
    public static function validatePassword($password, $confirmPassword = null)
    {
        if (empty($password)) {
            throw new \Exception('กรุณากรอกรหัสผ่าน');
        }

        if (strlen($password) < 6) {
            throw new \Exception('รหัสผ่านต้องมีอย่างน้อย 6 ตัวอักษร');
        }

        if ($confirmPassword !== null && $password !== $confirmPassword) {
            throw new \Exception('รหัสผ่านไม่ตรงกัน');
        }

        return true;
    }

    /**
     * ตรวจสอบข้อความ
     */
    public static function validateText($text, $fieldName, $minLength = 1, $maxLength = null)
    {
        if (empty($text)) {
            throw new \Exception("กรุณากรอก{$fieldName}");
        }

        $length = mb_strlen($text, 'UTF-8');

        if ($length < $minLength) {
            throw new \Exception("{$fieldName}ต้องมีอย่างน้อย {$minLength} ตัวอักษร");
        }

        if ($maxLength && $length > $maxLength) {
            throw new \Exception("{$fieldName}ต้องไม่เกิน {$maxLength} ตัวอักษร");
        }

        return true;
    }

    /**
     * ตรวจสอบไฟล์อัปโหลด
     */
    public static function validateUploadFile($file, $allowedTypes = [], $maxSize = 2097152)
    {
        if (!isset($file['tmp_name']) || empty($file['tmp_name'])) {
            throw new \Exception('กรุณาเลือกไฟล์');
        }

        if ($file['error'] !== UPLOAD_ERR_OK) {
            throw new \Exception('เกิดข้อผิดพลาดในการอัปโหลดไฟล์');
        }

        if ($file['size'] > $maxSize) {
            $maxSizeMB = $maxSize / 1024 / 1024;
            throw new \Exception("ไฟล์มีขนาดใหญ่เกิน {$maxSizeMB} MB");
        }

        if (!empty($allowedTypes)) {
            $fileType = strtolower(pathinfo($file['name'], PATHINFO_EXTENSION));
            if (!in_array($fileType, $allowedTypes)) {
                throw new \Exception('ประเภทไฟล์ไม่ถูกต้อง อนุญาตเฉพาะ: ' . implode(', ', $allowedTypes));
            }
        }

        return true;
    }
}

การอัปโหลดไฟล์

1. Upload Controller

// modules/blog/controllers/upload.php
namespace Blog\Upload;

use Kotchasan\Http\Request;
use Kotchasan\File;

class Controller extends \Kotchasan\Controller
{
    /**
     * อัปโหลดรูปภาพ
     */
    public function image(Request $request)
    {
        if ($request->initSession() && $request->isSafe()) {
            try {
                $file = $request->getUploadedFiles()['image'] ?? null;

                if (!$file) {
                    throw new \Exception('ไม่พบไฟล์ที่อัปโหลด');
                }

                // ตรวจสอบไฟล์
                \Blog\Validator\Model::validateUploadFile([
                    'tmp_name' => $file->getStream()->getMetadata('uri'),
                    'name' => $file->getClientFilename(),
                    'size' => $file->getSize(),
                    'error' => $file->getError()
                ], ['jpg', 'jpeg', 'png', 'gif'], 5242880); // 5MB

                // สร้างชื่อไฟล์ใหม่
                $extension = strtolower(pathinfo($file->getClientFilename(), PATHINFO_EXTENSION));
                $filename = uniqid() . '.' . $extension;
                $uploadPath = ROOT_PATH . '/datas/uploads/images/';

                // สร้างโฟลเดอร์ถ้าไม่มี
                if (!is_dir($uploadPath)) {
                    mkdir($uploadPath, 0755, true);
                }

                // ย้ายไฟล์
                $file->moveTo($uploadPath . $filename);

                // ปรับขนาดรูปภาพ (ถ้าต้องการ)
                $this->resizeImage($uploadPath . $filename, 800, 600);

                return [
                    'alert' => 'อัปโหลดสำเร็จ',
                    'filename' => $filename,
                    'url' => 'datas/uploads/images/' . $filename
                ];

            } catch (\Exception $e) {
                return [
                    'alert' => $e->getMessage()
                ];
            }
        }

        return [
            'alert' => 'การอัปโหลดไม่ถูกต้อง'
        ];
    }

    /**
     * ปรับขนาดรูปภาพ
     */
    private function resizeImage($imagePath, $maxWidth, $maxHeight)
    {
        $imageInfo = getimagesize($imagePath);
        if (!$imageInfo) {
            return false;
        }

        $originalWidth = $imageInfo[0];
        $originalHeight = $imageInfo[1];
        $imageType = $imageInfo[2];

        // คำนวณขนาดใหม่
        $ratio = min($maxWidth / $originalWidth, $maxHeight / $originalHeight);
        if ($ratio >= 1) {
            return true; // ไม่ต้องปรับขนาด
        }

        $newWidth = intval($originalWidth  $ratio);
        $newHeight = intval($originalHeight  $ratio);

        // สร้างรูปภาพใหม่
        $newImage = imagecreatetruecolor($newWidth, $newHeight);

        switch ($imageType) {
            case IMAGETYPE_JPEG:
                $sourceImage = imagecreatefromjpeg($imagePath);
                break;
            case IMAGETYPE_PNG:
                $sourceImage = imagecreatefrompng($imagePath);
                imagealphablending($newImage, false);
                imagesavealpha($newImage, true);
                break;
            case IMAGETYPE_GIF:
                $sourceImage = imagecreatefromgif($imagePath);
                break;
            default:
                return false;
        }

        // ปรับขนาด
        imagecopyresampled($newImage, $sourceImage, 0, 0, 0, 0, $newWidth, $newHeight, $originalWidth, $originalHeight);

        // บันทึกรูปภาพ
        switch ($imageType) {
            case IMAGETYPE_JPEG:
                imagejpeg($newImage, $imagePath, 85);
                break;
            case IMAGETYPE_PNG:
                imagepng($newImage, $imagePath);
                break;
            case IMAGETYPE_GIF:
                imagegif($newImage, $imagePath);
                break;
        }

        // ล้างหน่วยความจำ
        imagedestroy($sourceImage);
        imagedestroy($newImage);

        return true;
    }
}

2. ฟอร์มอัปโหลดไฟล์

// modules/blog/views/upload.php
namespace Blog\Upload;

use Kotchasan\Html;

class View extends \Gcms\View
{
    public function imageUploadForm()
    {
        $container = Html::create('div', ['class' => 'upload-container']);

        $container->add('h3', [], 'อัปโหลดรูปภาพ');

        $form = $container->add('form', [
            'id' => 'upload_form',
            'class' => 'upload-form',
            'action' => 'index.php/blog/upload/image',
            'method' => 'POST',
            'enctype' => 'multipart/form-data',
            'onsubmit' => 'doFormSubmit',
            'ajax' => true,
            'token' => true
        ]);

        // ไฟล์อัปโหลด
        $group = $form->add('div', ['class' => 'form-group']);
        $group->add('label', [], 'เลือกรูปภาพ');
        $group->add('input', [
            'type' => 'file',
            'name' => 'image',
            'accept' => 'image/*',
            'required' => true
        ]);

        // คำแนะนำ
        $group->add('small', ['class' => 'help-text'],
            'รองรับไฟล์ JPG, PNG, GIF ขนาดไม่เกิน 5MB');

        // ปุ่มอัปโหลด
        $form->add('button', [
            'type' => 'submit',
            'class' => 'btn btn-primary'
        ], 'อัปโหลด');

        // พื้นที่แสดงผลลัพธ์
        $container->add('div', [
            'id' => 'upload_result',
            'class' => 'upload-result'
        ]);

        return $container->render();
    }
}

ตัวอย่างเว็บเพจสมบูรณ์

1. Dashboard

// modules/blog/controllers/dashboard.php
namespace Blog\Dashboard;

use Gcms\Login;
use Kotchasan\Http\Request;

class Controller extends \Kotchasan\Controller
{
    public function render(Request $request)
    {
        // ตรวจสอบการ login
        $login = Login::isMember();
        if (!$login) {
            header('Location: index.php?module=blog&page=login');
            exit;
        }

        // ดึงสถิติ
        $postModel = new \Blog\Post\Model();
        $userPosts = $postModel->getUserPosts($login['id']);
        $stats = $postModel->getUserStats($login['id']);

        $view = new View();
        return $view->render($login, $userPosts, $stats);
    }
}

2. Dashboard View

// modules/blog/views/dashboard.php
namespace Blog\Dashboard;

use Kotchasan\Html;

class View extends \Gcms\View
{
    public function render($user, $posts, $stats)
    {
        $container = Html::create('div', ['class' => 'dashboard-container']);

        // Header
        $header = $container->add('div', ['class' => 'dashboard-header']);
        $header->add('h1', [], 'แดชบอร์ด');
        $header->add('p', [], 'ยินดีต้อนรับ, ' . $user['name']);

        // สถิติ
        $statsRow = $container->add('div', ['class' => 'stats-row']);

        $statCard = $statsRow->add('div', ['class' => 'stat-card']);
        $statCard->add('h3', [], $stats['total_posts']);
        $statCard->add('p', [], 'โพสต์ทั้งหมด');

        $statCard = $statsRow->add('div', ['class' => 'stat-card']);
        $statCard->add('h3', [], $stats['published_posts']);
        $statCard->add('p', [], 'โพสต์ที่เผยแพร่');

        $statCard = $statsRow->add('div', ['class' => 'stat-card']);
        $statCard->add('h3', [], number_format($stats['total_views']));
        $statCard->add('p', [], 'การดูทั้งหมด');

        // เมนูด่วน
        $quickMenu = $container->add('div', ['class' => 'quick-menu']);
        $quickMenu->add('h2', [], 'เมนูด่วน');

        $menuRow = $quickMenu->add('div', ['class' => 'menu-row']);
        $menuRow->add('a', [
            'href' => 'index.php?module=blog&page=post&action=form',
            'class' => 'menu-item'
        ], '+ สร้างโพสต์ใหม่');

        $menuRow->add('a', [
            'href' => 'index.php?module=blog&page=post',
            'class' => 'menu-item'
        ], 'ดูโพสต์ทั้งหมด');

        $menuRow->add('a', [
            'href' => 'index.php?module=blog&page=profile',
            'class' => 'menu-item'
        ], 'แก้ไขโปรไฟล์');

        // โพสต์ล่าสุด
        $recentPosts = $container->add('div', ['class' => 'recent-posts']);
        $recentPosts->add('h2', [], 'โพสต์ล่าสุดของคุณ');

        if (empty($posts)) {
            $recentPosts->add('p', [], 'ยังไม่มีโพสต์');
        } else {
            $table = $recentPosts->add('table', ['class' => 'data-table']);

            // Header
            $thead = $table->add('thead');
            $tr = $thead->add('tr');
            $tr->add('th', [], 'หัวข้อ');
            $tr->add('th', [], 'สถานะ');
            $tr->add('th', [], 'การดู');
            $tr->add('th', [], 'วันที่สร้าง');
            $tr->add('th', [], 'จัดการ');

            // Body
            $tbody = $table->add('tbody');
            foreach ($posts as $post) {
                $tr = $tbody->add('tr');
                $tr->add('td', [], $post['title']);
                $tr->add('td', [], $post['status'] === 'published' ? 'เผยแพร่' : 'แบบร่าง');
                $tr->add('td', [], number_format($post['views']));
                $tr->add('td', [], date('d/m/Y', strtotime($post['created_at'])));

                $actions = $tr->add('td');
                $actions->add('a', [
                    'href' => 'index.php?module=blog&page=post&action=form&id=' . $post['id'],
                    'class' => 'btn btn-sm btn-primary'
                ], 'แก้ไข');

                $actions->add('a', [
                    'href' => 'index.php?module=blog&page=post&action=view&id=' . $post['id'],
                    'class' => 'btn btn-sm btn-secondary'
                ], 'ดู');
            }
        }

        return $container->render();
    }
}

Best Practices

1. ความปลอดภัย

// ใช้ CSRF Token เสมอ
$form = Html::create('form', [
    'token' => true, // เปิดใช้งาน CSRF protection
    'ajax' => true
]);

// ตรวจสอบ session และ token
if ($request->initSession() && $request->isSafe()) {
    // ประมวลผลข้อมูล
}

// Escape ข้อมูลก่อนแสดงผล
echo htmlspecialchars($userInput, ENT_QUOTES, 'UTF-8');

// ใช้ Prepared Statements
$users = $db->select('*')
    ->from('users')
    ->where('email', '=', $email) // ปลอดภัยจาก SQL Injection
    ->execute();

2. การจัดการ Error

try {
    // โค้ดที่อาจเกิด error
    $result = $model->createPost($data);

    return [
        'alert' => 'บันทึกสำเร็จ',
        'location' => 'index.php?module=blog&page=dashboard'
    ];

} catch (\Exception $e) {
    // Log error สำหรับ developer
    error_log('Post creation error: ' . $e->getMessage());

    // แสดงข้อความที่เหมาะสมกับผู้ใช้
    return [
        'alert' => 'เกิดข้อผิดพลาด: ' . $e->getMessage(),
        'input' => 'title' // focus ไปที่ field ที่มีปัญหา
    ];
}

3. การ Validate ข้อมูล

class ExampleClass {
    <?php
// Validate ทั้งฝั่ง Client และ Server
// Client-side (JavaScript)
document.getElementById('email').addEventListener('blur', function() {
    const email = this.value;
    const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;

    if (!emailRegex.test(email)) {
        this.setCustomValidity('รูปแบบอีเมลไม่ถูกต้อง');
    } else {
        this.setCustomValidity('');
    }
});

// Server-side (PHP)
public function validateEmail($email)
{
    if (empty($email)) {
        throw new \Exception('กรุณากรอกอีเมล');
    }

    if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
        throw new \Exception('รูปแบบอีเมลไม่ถูกต้อง');
    }

    return true;
}
}

4. การจัดการ Session

// ตรวจสอบ login
function requireLogin()
{
    if (!Session::get('user_id')) {
        header('Location: index.php?module=blog&page=login');
        exit;
    }

    return [
        'id' => Session::get('user_id'),
        'name' => Session::get('user_name'),
        'role' => Session::get('user_role')
    ];
}

// ใช้งาน
$user = requireLogin();

5. การ Optimize Performance

// ใช้ Pagination
public function getAllPosts($page = 1, $limit = 10)
{
    $offset = ($page - 1)  $limit;

    return $this->db->select()
        ->from('posts')
        ->where('status', '=', 'published')
        ->orderBy('created_at', 'DESC')
        ->limit($limit, $offset)
        ->fetchAll();
}

// ใช้ Cache สำหรับข้อมูลที่ไม่เปลี่ยนบ่อย
public function getCategories()
{
    $cacheKey = 'blog_categories';
    $categories = Cache::get($cacheKey);

    if ($categories === null) {
        $categories = $this->db->select('*')
            ->from('categories')
            ->orderBy('name')
            ->fetchAll();

        Cache::set($cacheKey, $categories, 3600); // cache 1 ชั่วโมง
    }

    return $categories;
}

// Lazy Loading สำหรับรูปภาพ
<img src="placeholder.jpg" data-src="actual-image.jpg" class="lazy-load" alt="...">
}

6. การจัดระเบียบโค้ด

// แยก Business Logic ออกจาก Controller
class PostController extends \Kotchasan\Controller
{
    private $postService;

    public function __construct()
    {
        $this->postService = new PostService();
    }

    public function create(Request $request)
    {
        try {
            $data = $request->getParsedBody();
            $post = $this->postService->createPost($data);

            return $this->successResponse($post);
        } catch (\Exception $e) {
            return $this->errorResponse($e->getMessage());
        }
    }
}

// Service Class
class PostService
{
    private $postModel;
    private $validator;

    public function __construct()
    {
        $this->postModel = new PostModel();
        $this->validator = new PostValidator();
    }

    public function createPost($data)
    {
        $this->validator->validate($data);
        return $this->postModel->create($data);
    }
}

สรุป

การพัฒนาเว็บเพจด้วย Kotchasan Framework ให้ความสำคัญกับ:

  1. ความปลอดภัย - ใช้ CSRF Token, Prepared Statements, Input Validation
  2. การจัดระเบียบ - ใช้ MVC Pattern, แยก Business Logic
  3. ประสิทธิภาพ - ใช้ Cache, Pagination, Optimization
  4. ความสะดวก - Form Helper, Validation Helper, Upload Helper
  5. การบำรุงรักษา - Error Handling, Logging, Testing

การปฏิบัติตาม Best Practices เหล่านี้จะช่วยให้เว็บเพจมีคุณภาพสูง ปลอดภัย และใช้งานได้อย่างมีประสิทธิภาพ