Kotchasan Framework Documentation

Kotchasan Framework Documentation

คู่มือสถาปัตยกรรม MVC ของ Kotchasan

TH 07 Feb 2026 02:18

คู่มือสถาปัตยกรรม MVC ของ Kotchasan

บทนำ

Kotchasan Framework ใช้รูปแบบสถาปัตยกรรม MVC (Model-View-Controller) ที่ช่วยแยกการทำงานของแอปพลิเคชันออกเป็น 3 ส่วนหลัก ทำให้โค้ดมีการจัดระเบียบที่ดี ง่ายต่อการบำรุงรักษา และสามารถขยายได้

สารบัญ

  1. ภาพรวม MVC Pattern
  2. โครงสร้างไดเรกทอรี
  3. Model Layer
  4. View Layer
  5. Controller Layer
  6. การทำงานร่วมกันของ MVC
  7. ตัวอย่างการสร้างโมดูล
  8. Best Practices

ภาพรวม MVC Pattern

Model (โมเดล)

  • จัดการข้อมูลและตรรกะทางธุรกิจ
  • เชื่อมต่อกับฐานข้อมูล
  • ตรวจสอบความถูกต้องของข้อมูล
  • ประมวลผลข้อมูลก่อนส่งให้ Controller

View (วิว)

  • จัดการการแสดงผลและ UI
  • สร้าง HTML, JSON หรือรูปแบบการแสดงผลอื่นๆ
  • รับข้อมูลจาก Controller และแสดงผล
  • ไม่มีตรรกะทางธุรกิจ

Controller (คอนโทรลเลอร์)

  • จัดการ HTTP Request และ Response
  • เชื่อมต่อระหว่าง Model และ View
  • ตัดสินใจว่าจะใช้ Model และ View ไหน
  • จัดการ routing และ authentication

โครงสร้างไดเรกทอรี

modules/
└── mymodule/
    ├── controllers/
    │   ├── index.php          # Controller หลัก (namespace MyModule\Index)
    │   ├── api.php            # API Controller (namespace MyModule\Api)
    │   ├── user.php           # User Controller (namespace MyModule\User)
    │   └── admin.php          # Admin Controller (namespace MyModule\Admin)
    ├── models/
    │   ├── index.php          # Index Model (namespace MyModule\Index)
    │   ├── user.php           # User Model (namespace MyModule\User)
    │   └── validation.php     # Validation Model (namespace MyModule\Validation)
    ├── views/
    │   ├── index.php          # Index View (namespace MyModule\Index)
    │   ├── user.php           # User View (namespace MyModule\User)
    │   └── admin.php          # Admin View (namespace MyModule\Admin)
    ├── template/
    │   └── email.html         # Template อีเมล
    ├── script.js              # JavaScript
    └── style.css              # CSS

Model Layer

การสร้าง Model

namespace MyModule\User;

use Kotchasan\Model;
use Kotchasan\Database;

/**
 * User Model
 * File: modules/mymodule/models/user.php
 */
class Model extends \Kotchasan\Model
{
    /**
     * ตารางที่ใช้งาน
     */
    protected $table = 'users';

    /**
     * การเชื่อมต่อฐานข้อมูล (ถ้าไม่ระบุจะใช้ default)
     */
    protected $conn = 'default';

    /**
     * ดึงข้อมูลผู้ใช้ทั้งหมด
     */
    public function getAllUsers()
    {
        return $this->db->select()
            ->from($this->table)
            ->where('status', '=', 'active')
            ->orderBy('created_at', 'DESC')
            ->fetchAll();
    }

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

    /**
     * สร้างผู้ใช้ใหม่
     */
    public function createUser($data)
    {
        // ตรวจสอบข้อมูล
        $this->validateUserData($data);

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

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

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

    /**
     * อัปเดตข้อมูลผู้ใช้
     */
    public function updateUser($id, $data)
    {
        $data['updated_at'] = date('Y-m-d H:i:s');

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

    /**
     * ลบผู้ใช้
     */
    public function deleteUser($id)
    {
        return $this->db->delete($this->table)
            ->where('id', '=', $id)
            ->execute();
    }

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

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

        // ตรวจสอบอีเมลซ้ำ
        $existing = $this->db->select('id')
            ->from($this->table)
            ->where('email', '=', $data['email'])
            ->first();

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

การใช้งาน Model ขั้นสูง

namespace MyModule\Post;

use Kotchasan\Model;

class Model extends \Kotchasan\Model
{
    /**
     * ดึงโพสต์พร้อมข้อมูลผู้เขียน
     */
    public function getPostsWithAuthor($limit = 10)
    {
        return $this->db->select('p.', 'u.name as author_name', 'u.avatar')
            ->from('posts p')
            ->leftJoin('users u', 'u.id', 'p.user_id')
            ->where('p.status', '=', 'published')
            ->orderBy('p.created_at', 'DESC')
            ->limit($limit)
            ->fetchAll();
    }

    /**
     * ค้นหาโพสต์
     */
    public function searchPosts($keyword, $category = null)
    {
        $query = $this->db->select()
            ->from('posts')
            ->where('status', '=', 'published');

        if (!empty($keyword)) {
            $query->where('title', 'LIKE', "%{$keyword}%")
                  ->orWhere('content', 'LIKE', "%{$keyword}%");
        }

        if ($category) {
            $query->where('category_id', '=', $category);
        }

        return $query->orderBy('created_at', 'DESC')->fetchAll();
    }

    /**
     * สถิติโพสต์
     */
    public function getPostStats()
    {
        return [
            'total' => $this->db->select('COUNT() as count')
                ->from('posts')
                ->first()['count'],
            'published' => $this->db->select('COUNT() as count')
                ->from('posts')
                ->where('status', '=', 'published')
                ->first()['count'],
            'draft' => $this->db->select('COUNT() as count')
                ->from('posts')
                ->where('status', '=', 'draft')
                ->first()['count']
        ];
    }
}

View Layer

การสร้าง View

namespace MyModule\User;

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

/**
 * User View
 * File: modules/mymodule/views/user.php
 */
class View extends \Gcms\View
{
    /**
     * แสดงรายการผู้ใช้
     */
    public function render(Request $request)
    {
        // ดึงข้อมูลจาก Model
        $model = new \MyModule\User\Model();
        $users = $model->getAllUsers();

        // สร้าง HTML Table
        $table = Html::create('table', [
            'class' => 'data fullwidth'
        ]);

        // Header
        $thead = $table->add('thead');
        $tr = $thead->add('tr');
        $tr->add('th', [], 'ID');
        $tr->add('th', [], 'ชื่อ');
        $tr->add('th', [], 'อีเมล');
        $tr->add('th', [], 'สถานะ');
        $tr->add('th', [], 'จัดการ');

        // Body
        $tbody = $table->add('tbody');
        foreach ($users as $user) {
            $tr = $tbody->add('tr');
            $tr->add('td', [], $user['id']);
            $tr->add('td', [], $user['name']);
            $tr->add('td', [], $user['email']);
            $tr->add('td', [], $user['status']);

            // ปุ่มจัดการ
            $actions = $tr->add('td');
            $actions->add('a', [
                'href' => 'index.php?module=mymodule&page=user&action=edit&id=' . $user['id'],
                'class' => 'button green'
            ], 'แก้ไข');
            $actions->add('a', [
                'href' => 'index.php?module=mymodule&page=user&action=delete&id=' . $user['id'],
                'class' => 'button red',
                'onclick' => 'return confirm("ต้องการลบหรือไม่?")'
            ], 'ลบ');
        }

        return $table->render();
    }

    /**
     * ฟอร์มเพิ่ม/แก้ไขผู้ใช้
     */
    public function form(Request $request)
    {
        $id = $request->request('id')->toInt();
        $user = [];

        if ($id > 0) {
            $model = new \MyModule\User\Model();
            $user = $model->getUserById($id);
        }

        // สร้างฟอร์ม
        $form = Html::create('form', [
            'id' => 'user_form',
            'class' => 'setup_frm',
            'action' => 'index.php/mymodule/model/user/save',
            'onsubmit' => 'doFormSubmit',
            'ajax' => true,
            'token' => true
        ]);

        // ฟิลด์ซ่อน ID
        if ($id > 0) {
            $form->add('input', [
                'type' => 'hidden',
                'name' => 'id',
                'value' => $id
            ]);
        }

        // ชื่อ
        $fieldset = $form->add('fieldset');
        $fieldset->add('legend', [], 'ข้อมูลผู้ใช้');

        $groups = $fieldset->add('div', ['class' => 'item']);
        $groups->add('label', [], 'ชื่อ');
        $groups->add('input', [
            'type' => 'text',
            'name' => 'name',
            'value' => isset($user['name']) ? $user['name'] : '',
            'required' => true
        ]);

        // อีเมล
        $groups = $fieldset->add('div', ['class' => 'item']);
        $groups->add('label', [], 'อีเมล');
        $groups->add('input', [
            'type' => 'email',
            'name' => 'email',
            'value' => isset($user['email']) ? $user['email'] : '',
            'required' => true
        ]);

        // รหัสผ่าน (เฉพาะเพิ่มใหม่)
        if ($id == 0) {
            $groups = $fieldset->add('div', ['class' => 'item']);
            $groups->add('label', [], 'รหัสผ่าน');
            $groups->add('input', [
                'type' => 'password',
                'name' => 'password',
                'required' => true
            ]);
        }

        // ปุ่มบันทึก
        $fieldset = $form->add('fieldset');
        $fieldset->add('input', [
            'class' => 'button save large',
            'type' => 'submit',
            'value' => 'บันทึก'
        ]);

        return $form->render();
    }
}

การสร้าง JSON Response

namespace MyModule\Api;

use Kotchasan\Http\Request;
use Kotchasan\Http\Response;

class View extends \Kotchasan\View
{
    /**
     * ส่งข้อมูล JSON
     */
    public function json(Request $request)
    {
        $model = new \MyModule\User\Model();
        $users = $model->getAllUsers();

        // สร้าง Response JSON
        $response = new Response();
        $response = $response->withHeader('Content-Type', 'application/json');

        $data = [
            'success' => true,
            'data' => $users,
            'total' => count($users)
        ];

        $response->getBody()->write(json_encode($data, JSON_UNESCAPED_UNICODE));

        return $response;
    }
}

Controller Layer

การสร้าง Controller

namespace MyModule\User;

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

/**
 * User Controller
 * File: modules/mymodule/controllers/user.php
 */
class Controller extends \Kotchasan\Controller
{
    /**
     * แสดงรายการผู้ใช้
     */
    public function render(Request $request)
    {
        // ตรวจสอบสิทธิ์
        $login = Login::isMember();
        if (!$login || !Login::checkPermission($login, 'can_manage_user')) {
            return new \Kotchasan\Http\NotFound();
        }

        // เรียก View
        $view = new \MyModule\User\View();
        return $view->render($request);
    }

    /**
     * แสดงฟอร์ม
     */
    public function form(Request $request)
    {
        $login = Login::isMember();
        if (!$login || !Login::checkPermission($login, 'can_manage_user')) {
            return new \Kotchasan\Http\NotFound();
        }

        $view = new \MyModule\User\View();
        return $view->form($request);
    }
}

API Controller

namespace MyModule\Api;

use Kotchasan\ApiController;
use Kotchasan\Http\Request;

/**
 * API Controller
 * File: modules/mymodule/controllers/api.php
 */
class Controller extends ApiController
{
    /**
     * GET /api/users
     */
    public function index(Request $request)
    {
        try {
            $model = new \MyModule\User\Model();
            $users = $model->getAllUsers();

            return $this->successResponse($users, 'ดึงข้อมูลสำเร็จ');

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

    /**
     * POST /api/users
     */
    public function store(Request $request)
    {
        try {
            $data = [
                'name' => $request->post('name')->toString(),
                'email' => $request->post('email')->toString(),
                'password' => $request->post('password')->toString()
            ];

            $model = new \MyModule\User\Model();
            $userId = $model->createUser($data);

            return $this->successResponse(['id' => $userId], 'สร้างผู้ใช้สำเร็จ', 201);

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

    /**
     * PUT /api/users/{id}
     */
    public function update(Request $request)
    {
        try {
            $id = $request->request('id')->toInt();
            $data = [
                'name' => $request->post('name')->toString(),
                'email' => $request->post('email')->toString()
            ];

            $model = new \MyModule\User\Model();
            $model->updateUser($id, $data);

            return $this->successResponse([], 'อัปเดตสำเร็จ');

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

    /**
     * DELETE /api/users/{id}
     */
    public function destroy(Request $request)
    {
        try {
            $id = $request->request('id')->toInt();

            $model = new \MyModule\User\Model();
            $model->deleteUser($id);

            return $this->successResponse([], 'ลบสำเร็จ');

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

การทำงานร่วมกันของ MVC

Flow การทำงาน

  1. Request เข้ามาที่ Router
  2. Router เรียก Controller ที่เหมาะสม
  3. Controller ตรวจสอบสิทธิ์และเรียก Model
  4. Model ประมวลผลข้อมูลและส่งกลับ
  5. Controller ส่งข้อมูลให้ View
  6. View สร้าง HTML/JSON และส่งกลับ
  7. Response ถูกส่งกลับไปยัง Client

ตัวอย่างการทำงานแบบสมบูรณ์

// 1. Router (index.php)
$request = \Kotchasan\Http\Request::createFromGlobals();
$router = new \Kotchasan\Http\Router();

$router->get('/users', '\MyModule\User\Controller@render');
$router->post('/users/save', '\MyModule\User\Model@save');

// 2. Controller รับ Request
class Controller extends \Kotchasan\Controller
{
    public function render(Request $request)
    {
        // 3. เรียก Model
        $model = new Model();
        $users = $model->getAllUsers();

        // 4. ส่งข้อมูลให้ View
        $view = new View();
        return $view->render($users);
    }
}

// 5. Model ประมวลผลข้อมูล
class Model extends \Kotchasan\Model
{
    public function getAllUsers()
    {
        return $this->db->select('*')
            ->from('users')
            ->where('status', '=', 'active')
            ->fetchAll();
    }
}

// 6. View สร้าง HTML
class View extends \Kotchasan\View
{
    public function render($users)
    {
        $html = '<table>';
        foreach ($users as $user) {
            $html .= '<tr><td>' . $user['name'] . '</td></tr>';
        }
        $html .= '</table>';

        return $html;
    }
}

ตัวอย่างการสร้างโมดูล

1. สร้างโครงสร้างไดเรกทอรี

mkdir -p modules/blog/{controllers,models,views,template}

2. สร้าง Model

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

use Kotchasan\Model;

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

    /**
     * ดึงโพสต์พร้อมข้อมูลผู้เขียน
     */
    public function getPostsWithAuthor($limit = 10, $offset = 0)
    {
        return $this->db->select('p.', 'u.name as author')
            ->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
     */
    public function getPostById($id)
    {
        return $this->db->select()
            ->from($this->table)
            ->where('id', '=', $id)
            ->where('status', '=', 'published')
            ->first();
    }

    /**
     * สร้างโพสต์ใหม่
     */
    public function createPost($data)
    {
        $data['created_at'] = date('Y-m-d H:i:s');
        $data['status'] = 'draft';

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

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

3. สร้าง View

// modules/blog/views/index.php
namespace Blog\Index;

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

class View extends \Gcms\View
{
    public function render(Request $request)
    {
        $model = new \Blog\Post\Model();
        $posts = $model->getAllPosts();

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

        foreach ($posts as $post) {
            $article = $container->add('article', ['class' => 'blog-post']);

            $article->add('h2', [], $post['title']);
            $article->add('p', ['class' => 'meta'],
                'โดย ' . $post['author'] . ' เมื่อ ' . $post['created_at']
            );
            $article->add('div', ['class' => 'content'],
                substr($post['content'], 0, 200) . '...'
            );
            $article->add('a', [
                'href' => 'index.php?module=blog&page=post&id=' . $post['id'],
                'class' => 'read-more'
            ], 'อ่านต่อ');
        }

        return $container->render();
    }
}

4. สร้าง Controller

// modules/blog/controllers/index.php
namespace Blog\Index;

use Kotchasan\Http\Request;

class Controller extends \Kotchasan\Controller
{
    public function render(Request $request)
    {
        $view = new View();
        return $view->render($request);
    }
}

Best Practices

1. การตั้งชื่อ

// ดี - ชื่อที่มีความหมาย
class UserController extends \Kotchasan\Controller
{
    public function getUserProfile(Request $request) { }
    public function updateUserSettings(Request $request) { }
}

// ไม่ดี - ชื่อที่ไม่ชัดเจน
class Controller extends \Kotchasan\Controller
{
    public function action1(Request $request) { }
    public function doSomething(Request $request) { }
}

2. การแยกความรับผิดชอบ

// Model - จัดการข้อมูลเท่านั้น
class UserModel extends \Kotchasan\Model
{
    public function findUserByEmail($email)
    {
        return $this->db->select('*')
            ->from('users')
            ->where('email', '=', $email)
            ->first();
    }
}

// Controller - จัดการ logic และ flow
class UserController extends \Kotchasan\Controller
{
    public function login(Request $request)
    {
        $email = $request->post('email')->toString();
        $password = $request->post('password')->toString();

        $model = new UserModel();
        $user = $model->findUserByEmail($email);

        if ($user && password_verify($password, $user['password'])) {
            // Login สำเร็จ
            $view = new DashboardView();
            return $view->render($user);
        } else {
            // Login ไม่สำเร็จ
            $view = new LoginView();
            return $view->renderWithError('อีเมลหรือรหัสผ่านไม่ถูกต้อง');
        }
    }
}

// View - แสดงผลเท่านั้น
class LoginView extends \Kotchasan\View
{
    public function renderWithError($message)
    {
        $form = Html::create('form');
        $form->add('div', ['class' => 'error'], $message);
        // ... สร้างฟอร์ม login
        return $form->render();
    }
}

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

class UserController extends \Kotchasan\Controller
{
    public function createUser(Request $request)
    {
        try {
            $model = new UserModel();
            $userId = $model->createUser($request->getParsedBody());

            return $this->successResponse(['id' => $userId]);

        } catch (\InvalidArgumentException $e) {
            return $this->errorResponse($e->getMessage(), 400);
        } catch (\Exception $e) {
            error_log($e->getMessage());
            return $this->errorResponse('เกิดข้อผิดพลาดในระบบ', 500);
        }
    }
}

4. การใช้ Dependency Injection

class UserController extends \Kotchasan\Controller
{
    private $userModel;
    private $emailService;

    public function __construct(UserModel $userModel, EmailService $emailService)
    {
        $this->userModel = $userModel;
        $this->emailService = $emailService;
    }

    public function registerUser(Request $request)
    {
        $userData = $request->getParsedBody();
        $userId = $this->userModel->createUser($userData);

        // ส่งอีเมลยืนยัน
        $this->emailService->sendWelcomeEmail($userData['email']);

        return $this->successResponse(['id' => $userId]);
    }
}

5. การทดสอบ

// การทดสอบ Model
class UserModelTest extends \PHPUnit\Framework\TestCase
{
    public function testCreateUser()
    {
        $model = new UserModel();
        $userData = [
            'name' => 'Test User',
            'email' => 'test@example.com',
            'password' => 'password123'
        ];

        $userId = $model->createUser($userData);
        $this->assertGreaterThan(0, $userId);

        $user = $model->getUserById($userId);
        $this->assertEquals('Test User', $user['name']);
    }
}

// การทดสอบ Controller
class UserControllerTest extends \PHPUnit\Framework\TestCase
{
    public function testLoginSuccess()
    {
        $request = $this->createMockRequest([
            'email' => 'test@example.com',
            'password' => 'password123'
        ]);

        $controller = new UserController();
        $response = $controller->login($request);

        $this->assertEquals(200, $response->getStatusCode());
    }
}

สรุป

การใช้รูปแบบ MVC ใน Kotchasan Framework ช่วยให้:

  1. โค้ดมีการจัดระเบียบที่ดี - แยกความรับผิดชอบชัดเจน
  2. ง่ายต่อการบำรุงรักษา - แก้ไขส่วนใดส่วนหนึ่งโดยไม่กระทบส่วนอื่น
  3. สามารถขยายได้ - เพิ่มฟีเจอร์ใหม่ได้ง่าย
  4. ทดสอบได้ - แต่ละส่วนสามารถทดสอบแยกได้
  5. ทำงานเป็นทีมได้ดี - นักพัฒนาหลายคนทำงานพร้อมกันได้

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