Production-Ready CodeIgniter 4 + Docker พร้อม CI/CD ใน 10 นาที

โดย CyberMAN



🐳 Docker + CI/CD + CodeIgniter 4

Production-Ready CodeIgniter 4 + Docker
พร้อม CI/CD ใน 10 นาที

ตั้งค่าสภาพแวดล้อม PHP ระดับ Production ด้วย Docker, Nginx, MySQL, Redis และ GitHub Actions ในขั้นตอนเดียว

🕐 อ่าน 8 นาที⚡ PHP-FPM 8.2🐳 Docker Compose🔁 GitHub Actions

ปัญหาที่นักพัฒนา PHP ทุกคนเจอ

ทุกครั้งที่เริ่มโปรเจกต์ CodeIgniter 4 ใหม่ ก่อนที่จะเขียน business logic สักบรรทัดเดียว เราต้องเสียเวลาหลายชั่วโมงกับการ setup: ทำให้ Docker ทำงานได้, ตั้งค่า Nginx, เชื่อม PHP-FPM, ติดตั้ง MySQL กับ Redis, ทำให้ PHPUnit รันได้ใน container และสุดท้ายก็ต้องนั่งหาว่าทำไม CI pipeline ถึง fail ตั้งแต่ push ครั้งแรก

เวลาที่ใช้ setup คือ "เวลาสูญเปล่า" ไม่ได้ช่วย ship feature และก็ต้องทำซ้ำในทุกโปรเจกต์

💡 บทความนี้จะพาคุณตั้งแต่ศูนย์ไปถึง environment ที่ tested และพร้อม production จริงๆ ด้วย CodeIgniter 4 + Docker stack ภายใน 10 นาที พร้อม GitHub Actions CI/CD ที่รัน PHPUnit, PHPStan และ Rector อัตโนมัติ

Stack ที่ใช้ในโปรเจกต์นี้

stack ทั้งหมดถูกออกแบบมาให้ครอบคลุมตั้งแต่ development ไปจนถึง production โดยไม่ต้องเปลี่ยน config อะไรมาก

ComponentVersionหน้าที่
PHP-FPM8.2FastCGI runtime สำหรับรัน PHP
CodeIgniter4.7MVC framework + Shield authentication
Nginx1.28-alpineReverse proxy และ serve static files
MySQL8.4Primary database
Redis7-alpineCache, sessions และ queue backend
SupervisorsystemPID-1 process manager ใน production
PHPStanlevel 6Static analysis ตรวจ code ก่อน deploy
PHPUnit10.5Test suite พร้อม pcov driver

โครงสร้างไฟล์และ Docker Compose

โปรเจกต์แบ่งเป็นสองโหมดชัดเจน: Dockerfile.dev สำหรับ development (มี pcov + git support) และ Dockerfile สำหรับ production (ใช้ Supervisor เป็น PID-1)

docker-compose.yml
version: '3.9'

services:
  app:
    build:
      context: .
      dockerfile: Dockerfile.dev
    volumes:
      - .:/var/www/html
    depends_on:
      - db
      - redis
    environment:
      CI_ENVIRONMENT: development
      DB_HOST:     db
      REDIS_HOST:  redis

  nginx:
    image: nginx:1.28-alpine
    ports:
      - "8080:80"
    volumes:
      - ./nginx/default.conf:/etc/nginx/conf.d/default.conf
    depends_on:
      - app

  db:
    image: mysql:8.4
    environment:
      MYSQL_ROOT_PASSWORD: secret
      MYSQL_DATABASE:      ci4app
    volumes:
      - db_data:/var/lib/mysql

  redis:
    image: redis:7-alpine

volumes:
  db_data:

รัน Stack ทั้งหมดด้วยคำสั่งเดียว

ทุกอย่างถูก wrap ไว้ใน Makefile คำสั่ง make setup จะ build images, start containers, รอ MySQL + Redis พร้อม, รัน migrations, seed database และ hit healthcheck endpoint อัตโนมัติ

terminal
# Build, start, migrate, seed — ทุกอย่างในคำสั่งเดียว
make setup

# ตรวจสอบว่า stack ทำงานปกติ
curl http://localhost:8080/health

ถ้า stack ทำงานถูกต้อง จะได้ response JSON ดังนี้:

healthcheck response
{
  "status":    "ok",
  "database":  "ok",
  "redis":     "ok",
  "timestamp": "2026-06-26T15:00:00+00:00"
}
✅ เห็น JSON นี้ = สำเร็จ!หมายความว่า PHP-FPM, Nginx, MySQL, Redis ทำงานครบ, migrations รันเรียบร้อย และ healthcheck ผ่านทั้งหมด

3 การตัดสินใจสำคัญที่ไม่ค่อยมีคนพูดถึง

1. ใช้ Supervisor เป็น PID-1 แทน php-fpm โดยตรง

ถ้ารัน php-fpm เป็น PID-1 ตรงๆ จะเกิดปัญหา zombie processes สะสมใน container ที่รันนานๆ Supervisor จัดการ signal forwarding ได้ถูกต้อง, reap child processes และ restart service ที่ crash อัตโนมัติ

Dockerfile (production)
FROM php:8.2-fpm-alpine

RUN apk add --no-cache supervisor

# คัดลอก config ของ supervisor
COPY supervisord.conf /etc/supervisor/conf.d/app.conf

# Healthcheck via FastCGI
HEALTHCHECK --interval=10s --timeout=3s \
  CMD cgi-fcgi -bind -connect 127.0.0.1:9000 || exit 1

CMD ["/usr/bin/supervisord", "-c", "/etc/supervisor/supervisord.conf"]

2. SSH Key ใน Docker Container — ทำให้ถูกต้องและปลอดภัย

ถ้า app ต้องใช้ git clone จาก private repo หรือ deploy ผ่าน SSH ภายใน container การจัดการ SSH key ต้องทำอย่างระมัดระวัง ห้าม hardcode key ใน Dockerfile เด็ดขาด ให้ใช้ Docker build argument แทน

⚠️ ข้อควรระวังอย่า COPY ~/.ssh/id_rsa /root/.ssh/id_rsa ตรงๆ ใน Dockerfile เพราะ key จะถูก bake เข้าไปใน image layer และถ้าใครดึง image ไปก็จะได้ private key ของคุณไปด้วย
Dockerfile (SSH key ปลอดภัย)
# รับ SSH key ผ่าน build argument
ARG ssh_prv_key
ARG ssh_pub_key

RUN mkdir -p /root/.ssh && \
    echo "$ssh_prv_key" > /root/.ssh/id_rsa && \
    echo "$ssh_pub_key" > /root/.ssh/id_rsa.pub && \
    chmod 600 /root/.ssh/id_rsa && \
    chmod 600 /root/.ssh/id_rsa.pub

# ลบ key หลัง build เสร็จ (ใช้ --squash)
terminal — build command
docker build \
  --build-arg ssh_prv_key="$(cat ~/.ssh/id_rsa)" \
  --build-arg ssh_pub_key="$(cat ~/.ssh/id_rsa.pub)" \
  --squash \
  -t myapp .

3. สองสภาพแวดล้อม ไฟล์เดียว .env

ใช้ .env แยก config ระหว่าง dev และ production อย่าเขียน credential ลงใน docker-compose.yml โดยตรง ใช้ variable substitution แทน

.env
CI_ENVIRONMENT=development
DB_HOST=db
DB_PORT=3306
DB_DATABASE=ci4app
DB_USERNAME=ci4user
DB_PASSWORD=secret
REDIS_HOST=redis
REDIS_PORT=6379

GitHub Actions CI/CD Pipeline

ทุก push และ pull request จะ trigger workflow อัตโนมัติ รัน PHPUnit, PHPStan และ Rector เพื่อให้มั่นใจว่า code ที่จะ merge เข้า main ผ่าน quality gate ทุกข้อ

.github/workflows/ci.yml
name: CI Pipeline

on:
  push:
    branches: [main, develop]
  pull_request:

jobs:
  test:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v4

      - name: Build & Start Stack
        run: make setup

      - name: PHPStan — Static Analysis
        run: docker compose exec app vendor/bin/phpstan analyse --level=6

      - name: PHPUnit — Run Tests
        run: docker compose exec app vendor/bin/phpunit --coverage-text

      - name: Rector — Code Quality Check
        run: docker compose exec app vendor/bin/rector process --dry-run
🔁 Flow ทำงานอย่างไร
  1. Push code → GitHub Actions เริ่มทำงาน
  2. make setup build Docker stack ทั้งหมด
  3. PHPStan วิเคราะห์ type safety ระดับ 6
  4. PHPUnit รัน test suite พร้อม code coverage
  5. Rector ตรวจ code style ตาม PHP 8.2 standards
  6. ผ่านทุก step → merge ได้, fail → block merge ทันที

Docker Best Practices ที่ต้องรู้

1

ใช้ Alpine image เสมอ

เลือก php:8.2-fpm-alpine และ nginx:alpine แทน full Debian image ขนาดเล็กกว่า 5–10 เท่า attack surface น้อยกว่า และ pull เร็วกว่ามาก

2

ห้ามใส่ secret ใน image layer

ใช้ Docker build argument หรือ Docker secrets สำหรับ sensitive data ทุกชนิด ไม่ว่าจะเป็น API keys, SSH keys หรือ database passwords ถ้า key เข้าไปใน layer แล้วจะดึงออกยากมาก

3

ใช้ Healthcheck ทุก service

Docker รู้ว่า container "start" แต่ไม่รู้ว่า service พร้อมรับ request แล้ว Healthcheck บอก Docker Compose ให้รอ service จริงๆ พร้อมก่อนจึงเริ่ม service ถัดไป

4

แยก Dockerfile ระหว่าง dev และ production

Dockerfile.dev มี pcov, xdebug, git, tools ต่างๆ ส่วน Dockerfile production ต้องเบาที่สุด ไม่ต้องการ dev dependency ใดๆ ทั้งนั้น


สรุป: ทำไมต้อง Setup แบบนี้?

การ setup CI4 + Docker stack แบบนี้ให้ประโยชน์ชัดเจนสามด้าน:

  • ความเร็ว — ไม่ต้องเสียเวลา setup ซ้ำทุกโปรเจกต์ make setup คำสั่งเดียวพร้อมพัฒนา
  • ความสม่ำเสมอ — ทุก developer ใน team รันบน environment เดียวกันทุกครั้ง ไม่มีปัญหา "works on my machine"
  • คุณภาพ — CI/CD pipeline บังคับให้ code ผ่าน test, static analysis และ code quality check ก่อน merge ทุกครั้ง

สำหรับมือใหม่ที่เริ่มต้นกับ CodeIgniter 4 การมี stack แบบนี้ตั้งแต่ต้นจะช่วยให้โฟกัสกับการเขียน business logic ได้เลย แทนที่จะมานั่ง debug environment ปัญหา

🚀 ลองทำตามเลยวันนี้!





VibePHP: PHP Engine แห่งอนาคต ที่ใช้ AI แทน Interpreter

โดย CyberMAN



PHP Code Mania · 2026

VibePHP: PHP Engine แห่งอนาคต
ที่ใช้ AI แทน Interpreter

ไม่มี Parser ไม่มี Compiler ไม่มีแม้แต่ฐานข้อมูล — แค่ Vibe และ AI ที่คิดเอาเองว่าโค้ดของคุณควรทำอะไร

📅 มิถุนายน 2026⏱ อ่าน 7 นาที🎯 Beginner–Intermediate
PHPVibePHPAI RuntimeLaravelCodeIgniter 4Web Development

บทนำ: PHP ยุค "Vibe-Driven Development"

ปี 2026 วงการ PHP ได้เห็นสิ่งที่ไม่มีใครคาดคิด — VibePHP คือ PHP runtime ที่ไม่มี interpreter, ไม่มี compiler, และไม่มีแม้แต่ฐานข้อมูล แต่มันก็ยังสามารถ "รัน" โค้ด PHP ของคุณได้ เพราะมันใช้ AI อ่านซอร์สโค้ดแล้วปั้นผลลัพธ์ขึ้นมาตามความรู้สึก (vibes) ของมันเอง

ฟังดูบ้าไหม? ใช่ครับ แต่นั่นคือประเด็น — VibePHP เป็นโปรเจกต์เสียดสีแนว satire ที่สร้างขึ้นโดย Matthieu Napoli เพื่อล้อเลียนเทรนด์ "vibe coding" ที่กำลังระบาดในวงการ dev โดยที่โค้ดเดอร์บางคนปล่อยให้ AI สร้างโค้ดโดยไม่ตรวจสอบหรือเข้าใจมันเลย

😄 หมายเหตุ: VibePHP ไม่ใช่ production framework จริง มันคืองาน satire ที่ฉลาดมาก และบทความนี้จะใช้มันเป็นจุดเริ่มต้นสอนแนวคิด PHP สมัยใหม่ที่คุณนำไปใช้จริงได้

VibePHP ทำงานอย่างไร?

สถาปัตยกรรมของ VibePHP เรียบง่ายเหมือนชื่อมัน: Laravel ทำหน้าที่รับ HTTP request แล้วส่งซอร์สโค้ดให้ AI อ่าน จากนั้น AI จะ "ประมวลผล" ในหัว (literally) แล้วปั้น HTTP response ส่งกลับ

┌─────────────────┐    ส่งไฟล์ PHP ให้อ่าน (ไม่ได้รัน)
│    Laravel      │──────────────────────────────────┐
│  routes URL→file│                                  ▼
└─────────────────┘         ┌──────────────────────────────────┐
                            │     VibePhpRuntime (AI)          │
HTTP response ◄─────────── │  "ฉันคือ PHP engine ตอนนี้"      │
{status, headers, body}    │  • ประมวลผลในจินตนาการ           │
                            │  • ปั้นข้อมูลที่หายไปขึ้นมาเอง  │
                            └──────────────────────────────────┘

เช่น ถ้าโค้ดของคุณเรียก SELECT * FROM posts โดยที่ไม่มีฐานข้อมูลอยู่จริง — VibePHP จะปั้น "blog posts" ที่น่าเชื่อถือขึ้นมา 5 รายการให้คุณโดยที่คุณไม่ต้องทำอะไรเลย

ตัวอย่างโค้ด: PHP แบบ "Vibe" vs แบบ "Real"

🌀 โค้ดสไตล์ VibePHP (ล้อเลียน)

vibe_style.php
// ไม่มี $db, ไม่มีฐานข้อมูล, ไม่มี posts จริง ๆ
// แต่ VibePHP จะปั้นมาให้ 5 รายการ ✨
$result = query("SELECT * FROM posts ORDER BY published_at DESC");

// ฟังก์ชันนี้ไม่ได้ define ไว้ที่ไหนเลย
// แต่จะ return อุณหภูมิที่ "น่าเชื่อถือ" ให้คุณ
$temp = fetchTemperatureFromSomeApiThatDoesNotExist();

echo "Bangkok temperature: {$temp}°C";

✅ โค้ดสไตล์ Real PHP ที่ใช้งานได้จริง (CodeIgniter 4)

app/Controllers/PostController.php
<?php

namespace App\Controllers;

use App\Models\PostModel;
use CodeIgniter\Controller;

class PostController extends Controller
{
    public function index()
    {
        // ใช้ Model จริง เชื่อมฐานข้อมูลจริง
        $model = new PostModel();
        $data['posts'] = $model
            ->orderBy('published_at', 'DESC')
            ->findAll();

        return view('posts/index', $data);
    }
}
💡 โค้ดจริงใน CodeIgniter 4 ต้องมี Model, Controller, และการเชื่อมต่อฐานข้อมูลที่ชัดเจน — ไม่มีใคร "ปั้น" ข้อมูลให้คุณในโลกจริง

ตัวอย่างใน Laravel: Route + Controller + View

VibePHP ตัวมันเองสร้างบน Laravel — ซึ่งทำให้เห็นชัดว่า Laravel เป็น framework ที่ทรงพลังมากพอจะเป็นโครงสร้างหลักให้กับ runtime ใหม่ ๆ ได้ ลองดูตัวอย่าง blog posts listing แบบ "ของจริง" ใน Laravel:

routes/web.php
use App\Http\Controllers\PostController;
use Illuminate\Support\Facades\Route;

Route::get('/', [PostController::class, 'index']);
Route::get('/posts/{id}', [PostController::class, 'show']);
app/Http/Controllers/PostController.php
<?php

namespace App\Http\Controllers;

use App\Models\Post;

class PostController extends Controller
{
    public function index()
    {
        $posts = Post::latest()->take(5)->get();

        return view('posts.index', compact('posts'));
    }

    public function show(int $id)
    {
        $post = Post::findOrFail($id);

        return view('posts.show', compact('post'));
    }
}
resources/views/posts/index.blade.php
@extends('layouts.app')

@section('content')
  <h1>บทความล่าสุด</h1>

  @foreach($posts as $post)
    <article>
      <h2>{{ $post->title }}</h2>
      <p>{{ $post->excerpt }}</p>
      <a href="/posts/{{ $post->id }}">อ่านต่อ →</a>
    </article>
  @endforeach
@endsection

เปรียบเทียบ: VibePHP vs PHP จริง

คุณสมบัติVibePHP (Satire)PHP จริง (CI4 / Laravel)
RuntimeAI อ่านโค้ดแล้วปั้นผลลัพธ์Interpreter/Compiler จริง (PHP-FPM)
ฐานข้อมูลAI ปั้นข้อมูลขึ้นมาเองMySQL, PostgreSQL, SQLite ที่ config จริง
ความถูกต้องไม่ deterministic เลยDeterministic ทุก request
ค่าใช้จ่ายจ่ายต่อ "particle-millisecond" 💀ค่า server/hosting ปกติ
Generics"ได้ เพราะไม่มีอะไรหยุดมัน"PHP 8.x ยังไม่มี native generics
ใช้งาน production ได้?❌ ไม่✅ ได้ 100%
เหมาะกับขำ, เรียนรู้แนวคิด satireWeb app จริงทุกขนาด

บทเรียนจาก VibePHP: Vibe Coding ภัยเงียบของโปรแกรมเมอร์

แม้ VibePHP จะเป็นโปรเจกต์ล้อเลียน แต่มันสะท้อนปัญหาจริงในปี 2026 — นั่นคือเทรนด์ "Vibe Coding" ที่โปรแกรมเมอร์บางคนปล่อยให้ AI generate โค้ดโดยไม่อ่าน ไม่ทำความเข้าใจ แล้วก็ deploy ขึ้น production เลย

⚠️ Vibe Coding คือ: การเขียนโค้ดที่พึ่ง AI 100% โดยไม่ตรวจสอบ ไม่เข้าใจ logic และไม่รู้ว่าโค้ดมันทำอะไรจริง ๆ — ปัญหาที่ตามมาคือ security hole, bug ที่หาไม่เจอ, และ codebase ที่ maintain ไม่ได้

ในฐานะ PHP developer ที่ดี สิ่งที่ควรทำคือ:

  • ใช้ AI เป็น เครื่องมือช่วย ไม่ใช่ให้ AI ทำงานทั้งหมดแทน
  • อ่านและทำความเข้าใจโค้ดทุกบรรทัดก่อน deploy
  • เขียน unit test เพื่อยืนยันว่าโค้ดทำงานถูกต้อง
  • เรียนรู้ fundamental ของ PHP, SQL, และ HTTP เป็นของตัวเอง

PHP สมัยใหม่ที่ใช้ได้จริงใน 2026

แทนที่จะฝันถึง VibePHP มาดู feature จริงของ PHP ที่คุณใช้ได้เลยวันนี้:

modern_php.php — PHP 8.3+
<?php

// Named Arguments (PHP 8.0+)
htmlspecialchars(string: $input, double_encode: false);

// Match Expression (PHP 8.0+)
$label = match($status) {
    'active'  => '✅ เปิดใช้งาน',
    'pending' => '⏳ รอดำเนินการ',
    default   => '❌ ไม่ทราบสถานะ',
};

// Readonly Properties (PHP 8.1+)
class User {
    public function __construct(
        public readonly int    $id,
        public readonly string $name,
        public readonly string $email,
    ) {}
}

// Fibers / Async-like (PHP 8.1+)
$fiber = new Fiber(function(): void {
    $value = Fiber::suspend('hello');
    echo "ได้รับค่า: {$value}\n";
});

$value = $fiber->start();
$fiber->resume('world');

สรุป: Vibe หรือ Real — เลือกอะไร?

VibePHP คือผลงาน satire ที่ฉลาดมาก มันสะท้อนให้เห็นว่าวงการ dev ในปี 2026 กำลังเดินไปในทิศทางที่น่าเป็นห่วง เมื่อ "vibe" มาก่อน "understanding" เสมอ

สำหรับคนที่อยากสร้าง web application จริง ๆ — ไม่ว่าจะด้วย CodeIgniter 4 หรือ Laravel — รากฐานสำคัญยังเป็นเรื่องเดิม: เข้าใจ HTTP, เขียน PHP ที่ deterministic, ใช้ฐานข้อมูลอย่างถูกต้อง และทำ code review อยู่เสมอ

AI เป็นเครื่องมือที่ทรงพลัง ใช้มันให้ถูกทาง — ให้มันช่วยคิด ช่วย draft โค้ด แต่ความเข้าใจขั้นสุดท้ายต้องอยู่ที่คุณ ไม่ใช่ที่ vibe

จัด โครงสร้าง PHP Project ให้เป็นระเบียบตั้งแต่วันแรก

โดย CyberMAN



PHP Best Practices

จัด โครงสร้าง PHP Project
ให้เป็นระเบียบตั้งแต่วันแรก

เทคนิคจัดโฟลเดอร์และไฟล์ที่นักพัฒนามืออาชีพใช้ รองรับทั้ง CodeIgniter 4 และ Laravel

🗂️ โครงสร้างโปรเจกต์🐘 PHP 8+⏱️ อ่าน 8 นาที
คุณเคยเปิดโปรเจกต์ PHP เก่าของตัวเองแล้วรู้สึกหลงทางไหม? ไฟล์กระจัดกระจาย ไม่รู้ว่า Logic อยู่ตรงไหน Database อยู่ที่ไหน — ปัญหานี้เกิดจากการไม่มี โครงสร้างโปรเจกต์ที่ชัดเจน ตั้งแต่แรก บทความนี้จะพาคุณเรียนรู้วิธีจัดระเบียบโปรเจกต์ PHP อย่างมืออาชีพ พร้อมตัวอย่างโค้ดจริงสำหรับ CodeIgniter 4 และ Laravel

ทำไมโครงสร้างโปรเจกต์ถึงสำคัญ?

PHP ถูกใช้งานในเว็บไซต์กว่า 80% ทั่วโลก ตั้งแต่บล็อกเล็กๆ ไปจนถึงระบบขนาดใหญ่อย่าง Facebook, Wikipedia และ Slack ล้วนใช้ PHP เป็นแกนหลัก สิ่งที่ทำให้โปรเจกต์เหล่านี้ดูแลรักษาได้ง่ายคือ โครงสร้างไฟล์และโฟลเดอร์ที่มีระเบียบ

เมื่อโปรเจกต์ขยายตัว สมาชิกในทีมเพิ่มขึ้น หรือคุณต้องกลับมาแก้ไขโค้ดหลังจากผ่านไปหลายเดือน โครงสร้างที่ดีจะช่วยให้:

✅ ประโยชน์ของโครงสร้างที่ดี
นำทางในโค้ดได้ง่าย · ทำงานร่วมกันในทีมได้ลื่นขึ้น · เพิ่มฟีเจอร์ใหม่โดยไม่กระทบส่วนอื่น · เขียน Unit Test ได้สะดวก · Deploy และ Maintain ได้ง่ายในระยะยาว

โครงสร้างโฟลเดอร์มาตรฐาน PHP

โปรเจกต์ PHP สมัยใหม่ควรแยกหน้าที่ของแต่ละส่วนออกจากกันอย่างชัดเจน นี่คือโครงสร้างที่นิยมใช้กัน:

project-root/
├── bin/                 # CLI scripts & console commands
├── config/              # Configuration files (db, mail, cache)
├── public/              # Web root — index.php, assets, images
├── src/                 # Application source code
│   ├── Controllers/
│   ├── Models/
│   └── Services/
├── templates/           # Views / Blade templates
├── tests/               # Automated tests
├── var/                 # Logs, cache (ไม่ commit เข้า Git)
├── vendor/              # Composer dependencies
├── .env                 # Environment variables (secret!)
├── composer.json
└── README.md

หลักสำคัญคือ โฟลเดอร์ public/ ควรเป็นจุดเดียวที่ Web Server เข้าถึงได้จากภายนอก ส่วนโค้ดหลักทั้งหมดอยู่ใน src/ ซึ่ง User ไม่สามารถเข้าถึงโดยตรงได้ เป็นการป้องกันความปลอดภัยขั้นพื้นฐาน

โครงสร้างใน CodeIgniter 4

CodeIgniter 4 มาพร้อมโครงสร้างที่ออกแบบมาอย่างดี ทำให้ผู้เริ่มต้นเข้าใจง่าย มาดูตัวอย่าง Controller และ Model ที่เขียนถูกหลักการ:

Controller — รับ Request และส่งต่อให้ Model

app/Controllers/ArticleController.php
<?php

namespace App\Controllers;

use App\Models\ArticleModel;
use CodeIgniter\HTTP\RedirectResponse;

class ArticleController extends BaseController
{
    protected ArticleModel $model;

    public function __construct()
    {
        $this->model = new ArticleModel();
    }

    // แสดงรายการบทความทั้งหมด
    public function index(): string
    {
        $data = [
            'articles' => $this->model->findAll(),
            'title'    => 'บทความทั้งหมด',
        ];

        return view('articles/index', $data);
    }

    // แสดงบทความเดียว
    public function show(int $id): string
    {
        $article = $this->model->find($id);

        if (! $article) {
            throw new \CodeIgniter\Exceptions\PageNotFoundException();
        }

        return view('articles/show', ['article' => $article]);
    }
}

Model — จัดการข้อมูลและ Business Logic

app/Models/ArticleModel.php
<?php

namespace App\Models;

use CodeIgniter\Model;

class ArticleModel extends Model
{
    protected $table      = 'articles';
    protected $primaryKey = 'id';
    protected $returnType = 'array';

    // กำหนดฟิลด์ที่อนุญาตให้บันทึก (Mass Assignment Protection)
    protected $allowedFields = ['title', 'body', 'slug', 'author_id'];

    // Validation rules
    protected $validationRules = [
        'title'  => 'required|min_length[5]|max_length[200]',
        'body'   => 'required|min_length[20]',
    ];

    // ดึงบทความล่าสุด 10 รายการ
    public function getLatest(int $limit = 10): array
    {
        return $this->orderBy('created_at', 'DESC')
                   ->limit($limit)
                   ->findAll();
    }
}

โครงสร้างใน Laravel — Blade Template

Laravel ใช้ Blade เป็น Template Engine ที่ช่วยให้ View สะอาดและอ่านง่าย ตัวอย่างด้านล่างแสดงการใช้ Layout Inheritance และ Component:

resources/views/layouts/app.blade.php
<!DOCTYPE html>
<html lang="th">
<head>
    <meta charset="UTF-8">
    <title>@yield('title', 'My App')</title>
</head>
<body>
    @include('partials.navbar')

    <main class="container">
        @yield('content')  {{-- ส่วนที่ Child View จะ inject เนื้อหาเข้ามา --}}
    </main>

    @stack('scripts')      {{-- สำหรับ JavaScript เฉพาะหน้า --}}
</body>
</html>
resources/views/articles/index.blade.php
@extends('layouts.app')

@section('title', 'บทความทั้งหมด')

@section('content')
    <h1>บทความล่าสุด</h1>

    @forelse($articles as $article)
        <div class="card">
            <h2>{{ $article->title }}</h2>
            <p>{{ Str::limit($article->body, 150) }}</p>
            <a href="{{ route('articles.show', $article) }}">
                อ่านต่อ →
            </a>
        </div>
    @empty
        <p>ยังไม่มีบทความ</p>
    @endforelse

    {{ $articles->links() }}  {{-- Pagination --}}
@endsection

จัดการ Configuration อย่างปลอดภัย

หนึ่งในข้อผิดพลาดที่พบบ่อยที่สุดของนักพัฒนาหน้าใหม่คือการ เขียน password หรือ API key ลงในโค้ดโดยตรง วิธีที่ถูกต้องคือใช้ไฟล์ .env และโหลดค่าผ่าน Config Class:

.env
# ❌ อย่า commit ไฟล์นี้เข้า Git!
DB_HOST=localhost
DB_NAME=my_database
DB_USER=root
DB_PASS=supersecret123

MAIL_HOST=smtp.mailtrap.io
MAIL_PORT=587

APP_DEBUG=false
APP_ENV=production
config/Database.php (CI4 style)
<?php

namespace Config;

use CodeIgniter\Database\Config;

class Database extends Config
{
    public array $default = [
        'hostname' => env('DB_HOST', 'localhost'),
        'database' => env('DB_NAME', 'ci4app'),
        'username' => env('DB_USER', 'root'),
        'password' => env('DB_PASS', ''),
        'DBDriver' => 'MySQLi',
        'charset'  => 'utf8mb4',
    ];
}
💡 เคล็ดลับ: เพิ่ม .env เข้าไปใน .gitignore เสมอ แล้วสร้าง .env.example ที่มีแค่ชื่อตัวแปรโดยไม่มีค่าจริง เพื่อให้ทีมรู้ว่าต้องตั้งค่าอะไรบ้าง

เปรียบเทียบโครงสร้าง: CI4 vs Laravel

แม้ทั้งสองเฟรมเวิร์กจะมีแนวคิด MVC เหมือนกัน แต่มีความแตกต่างในโครงสร้างไฟล์ที่ควรรู้:

ส่วนประกอบCodeIgniter 4Laravel 11+หน้าที่
Controllersapp/Controllers/app/Http/Controllers/รับ Request, เรียก Model, Return View
Modelsapp/Models/app/Models/จัดการข้อมูลและ Business Logic
Viewsapp/Views/resources/views/HTML Template แสดงผล
Routesapp/Config/Routes.phproutes/web.phpกำหนด URL → Controller
Configapp/Config/config/ค่าตั้งค่าระบบ
Migrationsapp/Database/Migrations/database/migrations/สร้าง/แก้ไขตารางในฐานข้อมูล
Assetspublic/public/CSS, JS, Images ที่ Browser เข้าถึงได้
Teststests/tests/Automated Testing

เพิ่ม Service Layer — แยก Logic ออกจาก Controller

เมื่อ Business Logic ซับซ้อนขึ้น ควรแยกออกมาเป็น Service Class เพื่อให้ Controller บางและทดสอบได้ง่าย:

app/Services/ArticleService.php
<?php

namespace App\Services;

use App\Models\ArticleModel;

class ArticleService
{
    public function __construct(
        private readonly ArticleModel $model
    ) {}

    /**
     * สร้างบทความใหม่พร้อม slug อัตโนมัติ
     */
    public function create(array $data): int
    {
        $data['slug'] = $this->makeSlug($data['title']);
        $data['author_id'] = user_id(); // หรือ auth()->id()

        return $this->model->insert($data);
    }

    private function makeSlug(string $title): string
    {
        return strtolower(str_replace(' ', '-', $title));
    }
}

สรุป: หลัก 5 ข้อในการจัดโครงสร้าง PHP Project

การจัดโครงสร้าง PHP Project ที่ดีไม่ใช่เรื่องซับซ้อน เพียงแค่ยึดหลักพื้นฐานเหล่านี้:

1. แยก public/ ออกจาก source code — ป้องกันไม่ให้ User เข้าถึงโค้ดหลัก

2. ใช้ .env สำหรับค่า secret ทุกอย่าง — ไม่เขียน password ลงในโค้ด

3. ยึดหลัก MVC อย่างเคร่งครัด — Controller บาง, Model จัดการ Logic, View แสดงผลล้วนๆ

4. เพิ่ม Service Layer เมื่อ Logic ซับซ้อน — ทำให้โค้ดทดสอบได้ง่าย

5. เขียน Test ในโฟลเดอร์ tests/ — ป้องกัน Bug เมื่อแก้ไขโค้ดในอนาคต

ทั้ง CodeIgniter 4 และ Laravel ต่างมีโครงสร้างที่ออกแบบมาตามหลักการเหล่านี้แล้ว สิ่งที่คุณต้องทำคือ ทำความเข้าใจว่าแต่ละส่วนทำหน้าที่อะไร และเขียนโค้ดให้อยู่ในที่ที่ถูกต้อง เพียงเท่านี้โปรเจกต์ของคุณก็จะดูแลรักษาได้ง่ายขึ้นอย่างเห็นได้ชัด



PHP CI MANIA - PHP Code Generator 

โปรแกรมช่วยสร้างโค้ด "ลดเวลาการเขียนโปรแกรม"
ราคาสุดคุ้ม  
http://www.phpcodemania.com