Laravel 11: สร้างแอปพลิเคชันสมบูรณ์ตั้งแต่เริ่มต้น

คู่มือครบวงจรสำหรับสร้างแอปพลิเคชัน Laravel 11 พร้อมระบบยืนยันตัวตน, REST API, Eloquent ORM และการ Deploy บทเรียนเชิงปฏิบัติสำหรับนักพัฒนาระดับเริ่มต้นถึงกลาง

บทเรียน Laravel 11 สร้างแอปพลิเคชันสมบูรณ์

Laravel 11 ปฏิวัติการพัฒนา PHP สมัยใหม่ด้วยสถาปัตยกรรมที่กระชับและประสิทธิภาพที่ได้รับการปรับแต่ง เวอร์ชันหลักนี้ลบไฟล์คอนฟิกค่าเริ่มต้นจำนวนมาก นำเสนอโครงสร้างที่เรียบง่ายขึ้น และปรับปรุงประสบการณ์นักพัฒนาอย่างมีนัยสำคัญ คู่มือนี้จะพาผ่านกระบวนการสร้างแอปพลิเคชันจัดการงานอย่างครบถ้วน ตั้งแต่การติดตั้งจนถึงการ Deploy

จุดเด่นของ Laravel 11

Laravel 11 ใช้โครงสร้างแบบ Minimalist: ไม่มีไฟล์ Kernel อีกต่อไป คอนฟิกรวมศูนย์ใน bootstrap/app.php และไดเรกทอรี app/ ที่สะอาดขึ้น เฟรมเวิร์กต้องการ PHP 8.2 ขั้นต่ำ และรองรับ SQLite แบบ Native สำหรับการเริ่มต้นอย่างรวดเร็ว

การติดตั้งและตั้งค่าโปรเจกต์

Laravel 11 ติดตั้งผ่าน Composer หรือ Laravel installer โครงสร้างโปรเจกต์ใหม่กระชับกว่าเดิม มีไฟล์คอนฟิกที่ต้องจัดการน้อยลงตั้งแต่เริ่มต้น

bash
# terminal
# Install the Laravel installer (recommended)
composer global require laravel/installer

# Create a new Laravel 11 project
laravel new task-manager

# Or directly with Composer
composer create-project laravel/laravel task-manager

# Navigate to the project
cd task-manager

Installer มีตัวเลือกแบบโต้ตอบหลายอย่าง: เลือก Starter Kit (Breeze, Jetstream) เฟรมเวิร์กทดสอบ (Pest, PHPUnit) และฐานข้อมูล

bash
# terminal
# Start the development server
php artisan serve

# Server starts at http://localhost:8000

การตั้งค่าฐานข้อมูล

Laravel 11 ใช้ SQLite เป็นค่าเริ่มต้น เหมาะสำหรับการพัฒนา สำหรับแอปพลิเคชัน Production การตั้งค่า MySQL หรือ PostgreSQL ทำในไฟล์ .env

env
# .env
# MySQL configuration
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=task_manager
DB_USERNAME=root
DB_PASSWORD=secret

# PostgreSQL configuration (alternative)
# DB_CONNECTION=pgsql
# DB_HOST=127.0.0.1
# DB_PORT=5432
# DB_DATABASE=task_manager
# DB_USERNAME=postgres
# DB_PASSWORD=secret

ตรวจสอบการเชื่อมต่อฐานข้อมูลด้วยคำสั่ง Artisan

bash
# terminal
# Verify database connection
php artisan db:show

# Run default migrations
php artisan migrate

การสร้าง Model และ Migration

Eloquent ORM ทำให้การจัดการข้อมูลง่ายขึ้นด้วย Model ที่มีความหมายชัดเจน คำสั่ง make:model สร้าง Model, Migration, Controller และ Factory ในคราวเดียว

bash
# terminal
# Generate Task model with migration, factory, seeder, and controller
php artisan make:model Task -mfsc

# -m : migration
# -f : factory
# -s : seeder
# -c : controller

คำสั่งนี้สร้างไฟล์สำคัญสี่ไฟล์สำหรับการจัดการงานอย่างครบถ้วน

php
<?php
// database/migrations/2026_01_14_000000_create_tasks_table.php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

// Migration to create the tasks table
return new class extends Migration
{
    public function up(): void
    {
        Schema::create('tasks', function (Blueprint $table) {
            // Auto-incrementing primary key
            $table->id();
            // Relationship with the owner user
            $table->foreignId('user_id')->constrained()->cascadeOnDelete();
            // Task title (required)
            $table->string('title');
            // Detailed description (optional)
            $table->text('description')->nullable();
            // Status: pending, in_progress, completed
            $table->string('status')->default('pending');
            // Priority: low, medium, high
            $table->string('priority')->default('medium');
            // Due date (optional)
            $table->date('due_date')->nullable();
            // Completion marker
            $table->timestamp('completed_at')->nullable();
            // Automatic timestamps (created_at, updated_at)
            $table->timestamps();
            // Soft deletes for trash functionality
            $table->softDeletes();

            // Indexes to optimize frequent queries
            $table->index(['user_id', 'status']);
            $table->index('due_date');
        });
    }

    public function down(): void
    {
        Schema::dropIfExists('tasks');
    }
};

Migration กำหนดโครงสร้างตารางพร้อมความสัมพันธ์ ดัชนี และ Soft Delete เพื่อป้องกันการสูญเสียข้อมูลโดยไม่ตั้งใจ

php
<?php
// app/Models/Task.php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Database\Eloquent\Builder;

class Task extends Model
{
    // Traits for advanced functionality
    use HasFactory, SoftDeletes;

    // Mass-assignable attributes
    protected $fillable = [
        'user_id',
        'title',
        'description',
        'status',
        'priority',
        'due_date',
        'completed_at',
    ];

    // Automatic attribute casting
    protected function casts(): array
    {
        return [
            'due_date' => 'date',
            'completed_at' => 'datetime',
        ];
    }

    // Constants for valid statuses
    public const STATUS_PENDING = 'pending';
    public const STATUS_IN_PROGRESS = 'in_progress';
    public const STATUS_COMPLETED = 'completed';

    // Constants for priorities
    public const PRIORITY_LOW = 'low';
    public const PRIORITY_MEDIUM = 'medium';
    public const PRIORITY_HIGH = 'high';

    // Relationship: a task belongs to a user
    public function user(): BelongsTo
    {
        return $this->belongsTo(User::class);
    }

    // Scope: pending tasks
    public function scopePending(Builder $query): Builder
    {
        return $query->where('status', self::STATUS_PENDING);
    }

    // Scope: in-progress tasks
    public function scopeInProgress(Builder $query): Builder
    {
        return $query->where('status', self::STATUS_IN_PROGRESS);
    }

    // Scope: completed tasks
    public function scopeCompleted(Builder $query): Builder
    {
        return $query->where('status', self::STATUS_COMPLETED);
    }

    // Scope: overdue tasks
    public function scopeOverdue(Builder $query): Builder
    {
        return $query->where('due_date', '<', now())
                     ->whereNull('completed_at');
    }

    // Scope: tasks for a specific user
    public function scopeForUser(Builder $query, int $userId): Builder
    {
        return $query->where('user_id', $userId);
    }

    // Accessor: check if task is overdue
    public function getIsOverdueAttribute(): bool
    {
        return $this->due_date
            && $this->due_date->isPast()
            && !$this->completed_at;
    }

    // Method: mark as completed
    public function markAsCompleted(): void
    {
        $this->update([
            'status' => self::STATUS_COMPLETED,
            'completed_at' => now(),
        ]);
    }
}

Model Task ใช้ Eloquent Scope สำหรับ Query ที่อ่านง่ายและนำกลับมาใช้ซ้ำได้ ค่าคงที่รวมศูนย์ค่าที่ถูกต้องสำหรับสถานะและลำดับความสำคัญ

Soft Deletes

Trait SoftDeletes เพิ่มคอลัมน์ deleted_at งานที่ถูกลบจะไม่หายไปจากฐานข้อมูล แต่จะถูกทำเครื่องหมายว่าถูกลบแล้ว ใช้ withTrashed() เพื่อรวมข้อมูลเหล่านี้ในการ Query

การตั้งค่าระบบยืนยันตัวตนด้วย Breeze

Laravel Breeze มีระบบยืนยันตัวตนครบถ้วนพร้อม View แบบ Blade หรือ API การติดตั้งใช้เวลาเพียงไม่กี่นาทีและสร้างทุกสิ่งที่จำเป็น: Route, Controller, View และ Test

bash
# terminal
# Install Laravel Breeze
composer require laravel/breeze --dev

# Install the Blade stack (traditional)
php artisan breeze:install blade

# Or for API only (SPA/mobile)
php artisan breeze:install api

# Compile assets
npm install && npm run build

# Run migrations (users, sessions tables, etc.)
php artisan migrate

Breeze สร้าง Route ยืนยันตัวตน Controller และ View สำหรับการลงทะเบียน เข้าสู่ระบบ รีเซ็ตรหัสผ่าน และยืนยันอีเมล

php
<?php
// app/Models/User.php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;

class User extends Authenticatable
{
    use HasFactory, Notifiable;

    protected $fillable = [
        'name',
        'email',
        'password',
    ];

    protected $hidden = [
        'password',
        'remember_token',
    ];

    protected function casts(): array
    {
        return [
            'email_verified_at' => 'datetime',
            'password' => 'hashed',
        ];
    }

    // Relationship: a user owns multiple tasks
    public function tasks(): HasMany
    {
        return $this->hasMany(Task::class);
    }

    // Shortcut: user's pending tasks
    public function pendingTasks(): HasMany
    {
        return $this->tasks()->pending();
    }

    // Shortcut: user's overdue tasks
    public function overdueTasks(): HasMany
    {
        return $this->tasks()->overdue();
    }
}

พร้อมที่จะพิชิตการสัมภาษณ์ Laravel แล้วหรือยังครับ?

ฝึกฝนด้วยตัวจำลองแบบโต้ตอบ, flashcards และแบบทดสอบเทคนิคครับ

การสร้าง Controller และ Route

Controller จัดการ Logic ทางธุรกิจระหว่าง HTTP Request และ Model Laravel 11 สนับสนุนการใช้ Single-action Controller หรือ RESTful Resource

php
<?php
// app/Http/Controllers/TaskController.php

namespace App\Http\Controllers;

use App\Models\Task;
use Illuminate\Http\Request;
use Illuminate\Http\RedirectResponse;
use Illuminate\View\View;
use Illuminate\Support\Facades\Gate;

class TaskController extends Controller
{
    // List tasks for the authenticated user
    public function index(Request $request): View
    {
        // Retrieve tasks with optional filtering
        $tasks = $request->user()
            ->tasks()
            ->when($request->status, fn($q, $status) => $q->where('status', $status))
            ->when($request->priority, fn($q, $priority) => $q->where('priority', $priority))
            ->orderBy('due_date')
            ->orderBy('priority', 'desc')
            ->paginate(15);

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

    // Creation form
    public function create(): View
    {
        return view('tasks.create');
    }

    // Store a new task
    public function store(Request $request): RedirectResponse
    {
        // Validate incoming data
        $validated = $request->validate([
            'title' => 'required|string|max:255',
            'description' => 'nullable|string|max:5000',
            'priority' => 'required|in:low,medium,high',
            'due_date' => 'nullable|date|after_or_equal:today',
        ]);

        // Create the task linked to the user
        $request->user()->tasks()->create($validated);

        return redirect()
            ->route('tasks.index')
            ->with('success', 'Task created successfully.');
    }

    // Display a task
    public function show(Task $task): View
    {
        // Verify user ownership
        Gate::authorize('view', $task);

        return view('tasks.show', compact('task'));
    }

    // Edit form
    public function edit(Task $task): View
    {
        Gate::authorize('update', $task);

        return view('tasks.edit', compact('task'));
    }

    // Update a task
    public function update(Request $request, Task $task): RedirectResponse
    {
        Gate::authorize('update', $task);

        $validated = $request->validate([
            'title' => 'required|string|max:255',
            'description' => 'nullable|string|max:5000',
            'status' => 'required|in:pending,in_progress,completed',
            'priority' => 'required|in:low,medium,high',
            'due_date' => 'nullable|date',
        ]);

        // Update completed_at if status = completed
        if ($validated['status'] === Task::STATUS_COMPLETED && !$task->completed_at) {
            $validated['completed_at'] = now();
        }

        $task->update($validated);

        return redirect()
            ->route('tasks.index')
            ->with('success', 'Task updated.');
    }

    // Delete a task
    public function destroy(Task $task): RedirectResponse
    {
        Gate::authorize('delete', $task);

        $task->delete();

        return redirect()
            ->route('tasks.index')
            ->with('success', 'Task deleted.');
    }

    // Mark as completed (quick action)
    public function complete(Task $task): RedirectResponse
    {
        Gate::authorize('update', $task);

        $task->markAsCompleted();

        return back()->with('success', 'Task completed!');
    }
}

Controller ใช้ Gate สำหรับการอนุญาต Validation ในตัว และ Route Model Binding สำหรับโค้ดที่กระชับและปลอดภัย

การกำหนด Route

Route กำหนด URL และเชื่อมโยงกับ Action ของ Controller Laravel 11 รวมศูนย์ Route ใน routes/web.php

php
<?php
// routes/web.php

use App\Http\Controllers\TaskController;
use App\Http\Controllers\ProfileController;
use Illuminate\Support\Facades\Route;

// Public homepage
Route::get('/', function () {
    return view('welcome');
});

// Routes protected by authentication
Route::middleware(['auth', 'verified'])->group(function () {
    // Dashboard with statistics
    Route::get('/dashboard', function () {
        $user = auth()->user();

        return view('dashboard', [
            'totalTasks' => $user->tasks()->count(),
            'pendingTasks' => $user->tasks()->pending()->count(),
            'completedTasks' => $user->tasks()->completed()->count(),
            'overdueTasks' => $user->tasks()->overdue()->count(),
        ]);
    })->name('dashboard');

    // RESTful routes for tasks
    Route::resource('tasks', TaskController::class);

    // Quick action to complete a task
    Route::patch('/tasks/{task}/complete', [TaskController::class, 'complete'])
        ->name('tasks.complete');

    // Profile routes (generated by Breeze)
    Route::get('/profile', [ProfileController::class, 'edit'])->name('profile.edit');
    Route::patch('/profile', [ProfileController::class, 'update'])->name('profile.update');
    Route::delete('/profile', [ProfileController::class, 'destroy'])->name('profile.destroy');
});

// Authentication routes (generated by Breeze)
require __DIR__.'/auth.php';

เมธอด Route::resource() สร้าง Route RESTful มาตรฐานเจ็ดเส้นทางโดยอัตโนมัติ: index, create, store, show, edit, update และ destroy

การตั้งค่า Policy สำหรับการอนุญาต

Policy รวมศูนย์ Logic การอนุญาตสำหรับแต่ละ Model กำหนดว่าใครสามารถทำอะไรกับทรัพยากรได้บ้าง

bash
# terminal
# Generate the policy for Task
php artisan make:policy TaskPolicy --model=Task
php
<?php
// app/Policies/TaskPolicy.php

namespace App\Policies;

use App\Models\Task;
use App\Models\User;

class TaskPolicy
{
    // Check before all methods
    // Returning true bypasses all checks (admin)
    public function before(User $user, string $ability): ?bool
    {
        if ($user->is_admin) {
            return true;
        }

        return null; // Continue to specific methods
    }

    // Can view the list of tasks
    public function viewAny(User $user): bool
    {
        return true; // Any authenticated user
    }

    // Can view a specific task
    public function view(User $user, Task $task): bool
    {
        return $user->id === $task->user_id;
    }

    // Can create a task
    public function create(User $user): bool
    {
        return true;
    }

    // Can update a task
    public function update(User $user, Task $task): bool
    {
        return $user->id === $task->user_id;
    }

    // Can delete a task
    public function delete(User $user, Task $task): bool
    {
        return $user->id === $task->user_id;
    }

    // Can restore a soft-deleted task
    public function restore(User $user, Task $task): bool
    {
        return $user->id === $task->user_id;
    }

    // Can permanently delete
    public function forceDelete(User $user, Task $task): bool
    {
        return $user->id === $task->user_id;
    }
}

Laravel ค้นหา Policy โดยอัตโนมัติผ่านแบบแผนการตั้งชื่อ TaskPolicy จะถูกนำไปใช้กับ Model Task

ความปลอดภัยของ Policy

Policy มีความสำคัญอย่างยิ่งต่อความปลอดภัย หากไม่มี Policy ผู้ใช้สามารถแก้ไข URL เพื่อเข้าถึงงานของผู้ใช้คนอื่นได้ ต้องตรวจสอบความเป็นเจ้าของทรัพยากรเสมอ

การสร้าง REST API

Laravel ทำให้การสร้าง RESTful API ง่ายขึ้นด้วย Route เฉพาะ, JSON Resource และ Sanctum สำหรับการยืนยันตัวตน

bash
# terminal
# Install Sanctum (included by default since Laravel 11)
php artisan install:api

# This command:
# - Publishes Sanctum migrations
# - Adds the HasApiTokens trait to the User model
# - Configures API routes
php
<?php
// app/Http/Controllers/Api/TaskController.php

namespace App\Http\Controllers\Api;

use App\Http\Controllers\Controller;
use App\Http\Resources\TaskResource;
use App\Http\Resources\TaskCollection;
use App\Models\Task;
use Illuminate\Http\Request;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Resources\Json\AnonymousResourceCollection;

class TaskController extends Controller
{
    // Paginated list of tasks
    public function index(Request $request): AnonymousResourceCollection
    {
        $tasks = $request->user()
            ->tasks()
            ->when($request->status, fn($q, $s) => $q->where('status', $s))
            ->when($request->priority, fn($q, $p) => $q->where('priority', $p))
            ->when($request->boolean('overdue'), fn($q) => $q->overdue())
            ->latest()
            ->paginate($request->input('per_page', 15));

        return TaskResource::collection($tasks);
    }

    // Create a task
    public function store(Request $request): JsonResponse
    {
        $validated = $request->validate([
            'title' => 'required|string|max:255',
            'description' => 'nullable|string|max:5000',
            'priority' => 'required|in:low,medium,high',
            'due_date' => 'nullable|date|after_or_equal:today',
        ]);

        $task = $request->user()->tasks()->create($validated);

        return response()->json([
            'message' => 'Task created successfully.',
            'data' => new TaskResource($task),
        ], 201);
    }

    // Display a task
    public function show(Task $task): TaskResource
    {
        $this->authorize('view', $task);

        return new TaskResource($task);
    }

    // Update a task
    public function update(Request $request, Task $task): TaskResource
    {
        $this->authorize('update', $task);

        $validated = $request->validate([
            'title' => 'sometimes|required|string|max:255',
            'description' => 'nullable|string|max:5000',
            'status' => 'sometimes|required|in:pending,in_progress,completed',
            'priority' => 'sometimes|required|in:low,medium,high',
            'due_date' => 'nullable|date',
        ]);

        if (isset($validated['status'])
            && $validated['status'] === Task::STATUS_COMPLETED
            && !$task->completed_at) {
            $validated['completed_at'] = now();
        }

        $task->update($validated);

        return new TaskResource($task->fresh());
    }

    // Delete a task
    public function destroy(Task $task): JsonResponse
    {
        $this->authorize('delete', $task);

        $task->delete();

        return response()->json([
            'message' => 'Task deleted successfully.',
        ]);
    }
}

API Resource สำหรับการแปลงข้อมูล

API Resource แปลง Model Eloquent เป็น JSON Response ที่มีโครงสร้าง ควบคุมข้อมูลที่เปิดเผยได้อย่างแม่นยำ

php
<?php
// app/Http/Resources/TaskResource.php

namespace App\Http\Resources;

use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;

class TaskResource extends JsonResource
{
    public function toArray(Request $request): array
    {
        return [
            'id' => $this->id,
            'title' => $this->title,
            'description' => $this->description,
            'status' => $this->status,
            'priority' => $this->priority,
            'due_date' => $this->due_date?->format('Y-m-d'),
            'is_overdue' => $this->is_overdue,
            'completed_at' => $this->completed_at?->toISOString(),
            'created_at' => $this->created_at->toISOString(),
            'updated_at' => $this->updated_at->toISOString(),
            // Conditional inclusion of the user
            'user' => $this->whenLoaded('user', fn() => [
                'id' => $this->user->id,
                'name' => $this->user->name,
            ]),
        ];
    }
}

Route API พร้อมการยืนยันตัวตน Sanctum

php
<?php
// routes/api.php

use App\Http\Controllers\Api\TaskController;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;

// Route to retrieve the authenticated user
Route::get('/user', function (Request $request) {
    return $request->user();
})->middleware('auth:sanctum');

// API routes protected by Sanctum
Route::middleware('auth:sanctum')->group(function () {
    Route::apiResource('tasks', TaskController::class);

    // Dashboard statistics
    Route::get('/dashboard/stats', function (Request $request) {
        $user = $request->user();

        return response()->json([
            'total' => $user->tasks()->count(),
            'pending' => $user->tasks()->pending()->count(),
            'in_progress' => $user->tasks()->inProgress()->count(),
            'completed' => $user->tasks()->completed()->count(),
            'overdue' => $user->tasks()->overdue()->count(),
        ]);
    });
});

การทดสอบอัตโนมัติด้วย Pest

Laravel 11 รวม Pest PHP เป็นค่าเริ่มต้น เฟรมเวิร์กทดสอบที่สวยงามและมีความหมายชัดเจน การทดสอบช่วยให้มั่นใจว่าแอปพลิเคชันทำงานถูกต้องหลังจากทุกการเปลี่ยนแปลง

php
<?php
// tests/Feature/TaskTest.php

use App\Models\Task;
use App\Models\User;

// Test: a user can view their tasks
test('user can view their tasks', function () {
    $user = User::factory()->create();
    $tasks = Task::factory()->count(5)->for($user)->create();

    $response = $this->actingAs($user)->get('/tasks');

    $response->assertOk();
    $response->assertViewHas('tasks');
});

// Test: a user can create a task
test('user can create a task', function () {
    $user = User::factory()->create();

    $response = $this->actingAs($user)->post('/tasks', [
        'title' => 'New task',
        'description' => 'Task description',
        'priority' => 'high',
        'due_date' => now()->addDays(7)->format('Y-m-d'),
    ]);

    $response->assertRedirect('/tasks');

    $this->assertDatabaseHas('tasks', [
        'user_id' => $user->id,
        'title' => 'New task',
        'priority' => 'high',
    ]);
});

// Test: title validation required
test('task title is required', function () {
    $user = User::factory()->create();

    $response = $this->actingAs($user)->post('/tasks', [
        'title' => '',
        'priority' => 'medium',
    ]);

    $response->assertSessionHasErrors('title');
});

// Test: a user cannot view another user's task
test('user cannot view another users task', function () {
    $user = User::factory()->create();
    $otherUser = User::factory()->create();
    $task = Task::factory()->for($otherUser)->create();

    $response = $this->actingAs($user)->get("/tasks/{$task->id}");

    $response->assertForbidden();
});

// Test: mark a task as completed
test('user can complete a task', function () {
    $user = User::factory()->create();
    $task = Task::factory()->for($user)->create(['status' => 'pending']);

    $response = $this->actingAs($user)->patch("/tasks/{$task->id}/complete");

    $response->assertRedirect();

    $task->refresh();
    expect($task->status)->toBe('completed');
    expect($task->completed_at)->not->toBeNull();
});

// API test: retrieve tasks
test('api returns paginated tasks', function () {
    $user = User::factory()->create();
    Task::factory()->count(20)->for($user)->create();

    $response = $this->actingAs($user, 'sanctum')
        ->getJson('/api/tasks?per_page=10');

    $response->assertOk()
        ->assertJsonCount(10, 'data')
        ->assertJsonStructure([
            'data' => [
                '*' => ['id', 'title', 'status', 'priority', 'due_date'],
            ],
            'links',
            'meta',
        ]);
});

การรันการทดสอบด้วย Pest

bash
# terminal
# Run all tests
php artisan test

# Tests with code coverage
php artisan test --coverage

# Tests for a specific file
php artisan test tests/Feature/TaskTest.php

# Parallel tests (faster)
php artisan test --parallel

การปรับแต่งสำหรับ Production

ก่อนการ Deploy มีขั้นตอนการปรับแต่งหลายอย่างที่ช่วยเพิ่มประสิทธิภาพแอปพลิเคชัน Laravel

bash
# terminal
# Cache configuration
php artisan config:cache

# Cache routes
php artisan route:cache

# Cache views
php artisan view:cache

# Optimize Composer autoloader
composer install --optimize-autoloader --no-dev

# Generate asset manifest
npm run build

การตั้งค่าไฟล์ .env สำหรับ Production

env
# .env.production
APP_NAME="Task Manager"
APP_ENV=production
APP_DEBUG=false
APP_URL=https://taskmanager.example.com

# Cache and session via Redis (recommended)
CACHE_DRIVER=redis
SESSION_DRIVER=redis
QUEUE_CONNECTION=redis

REDIS_HOST=127.0.0.1
REDIS_PASSWORD=null
REDIS_PORT=6379

# Database
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_DATABASE=task_manager_prod
DB_USERNAME=app_user
DB_PASSWORD=strong_password_here

# Mail
MAIL_MAILER=smtp
MAIL_HOST=smtp.mailgun.org
MAIL_PORT=587
MAIL_USERNAME=postmaster@taskmanager.example.com
MAIL_PASSWORD=secret
MAIL_ENCRYPTION=tls
ตัวแปรที่มีข้อมูลอ่อนไหว

ห้าม Commit ไฟล์ .env เข้าสู่ Git Repository ใช้ .env.example เป็นเทมเพลต และตั้งค่าตัวแปรที่มีข้อมูลอ่อนไหวโดยตรงบนเซิร์ฟเวอร์ Production หรือผ่าน Secret Manager

สรุป

Laravel 11 ทำให้การพัฒนา PHP สมัยใหม่ง่ายขึ้นอย่างมากด้วยโครงสร้างที่กระชับและแบบแผนที่ชาญฉลาด คู่มือนี้ครอบคลุมพื้นฐานสำหรับการสร้างแอปพลิเคชันสมบูรณ์: Model Eloquent, ระบบยืนยันตัวตน, Controller แบบ RESTful, Policy สำหรับการอนุญาต, API ด้วย Sanctum และการทดสอบอัตโนมัติ

เช็คลิสต์สำหรับแอปพลิเคชัน Laravel คุณภาพ

  • ใช้ Migration สำหรับทุกการเปลี่ยนแปลง Schema
  • ใช้ Policy เพื่อรักษาความปลอดภัยการเข้าถึงทรัพยากร
  • Validate ข้อมูลนำเข้าทั้งหมดใน Controller
  • ใช้ API Resource เพื่อควบคุมข้อมูลที่เปิดเผย
  • เขียนการทดสอบสำหรับฟีเจอร์สำคัญ
  • ตั้งค่า Cache และการปรับแต่งก่อน Deploy
  • รักษาความปลอดภัยตัวแปรสภาพแวดล้อมที่มีข้อมูลอ่อนไหว

เริ่มฝึกซ้อมเลย!

ทดสอบความรู้ของคุณด้วยตัวจำลองสัมภาษณ์และแบบทดสอบเทคนิคครับ

ระบบนิเวศ Laravel ยังคงพัฒนาอย่างต่อเนื่องด้วยแพ็กเกจเช่น Livewire สำหรับอินเทอร์เฟซแบบ Reactive, Horizon สำหรับจัดการ Queue และ Octane สำหรับประสิทธิภาพที่โดดเด่น การเชี่ยวชาญพื้นฐานเหล่านี้เปิดเส้นทางสู่แอปพลิเคชัน PHP ที่แข็งแกร่งและบำรุงรักษาง่าย

แท็ก

#laravel
#php
#rest api
#eloquent
#tutorial

แชร์

บทความที่เกี่ยวข้อง