# Laravel 11: Xây dựng Ứng dụng Hoàn chỉnh từ Đầu > Hướng dẫn toàn diện xây dựng ứng dụng Laravel 11 với xác thực, REST API, Eloquent ORM và triển khai. Bài hướng dẫn thực hành dành cho lập trình viên từ cơ bản đến trung cấp. - Published: 2026-01-14 - Updated: 2026-04-08 - Author: SharpSkill - Tags: laravel, php, rest api, eloquent, tutorial - Reading time: 15 min --- Laravel 11 tái định nghĩa phát triển PHP hiện đại với kiến trúc tinh gọn và hiệu suất được tối ưu hóa. Phiên bản chính này loại bỏ nhiều file cấu hình mặc định, giới thiệu cấu trúc gọn nhẹ hơn, và cải thiện đáng kể trải nghiệm lập trình viên. Bài hướng dẫn này đi qua toàn bộ quá trình xây dựng ứng dụng quản lý công việc, từ cài đặt đến triển khai. > **Điểm nổi bật của Laravel 11** > > Laravel 11 áp dụng cấu trúc tối giản: không còn file Kernel, cấu hình được hợp nhất trong bootstrap/app.php, và thư mục app/ sạch hơn. Framework yêu cầu tối thiểu PHP 8.2 và tích hợp sẵn SQLite để khởi động nhanh. ## Cài đặt và Thiết lập Dự án Laravel 11 được cài đặt thông qua Composer hoặc Laravel installer. Cấu trúc dự án mới gọn nhẹ hơn, với ít file cấu hình cần quản lý ngay từ đầu. ```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 cung cấp nhiều tùy chọn tương tác: lựa chọn starter kit (Breeze, Jetstream), framework testing (Pest, PHPUnit), và cơ sở dữ liệu. ```bash # terminal # Start the development server php artisan serve # Server starts at http://localhost:8000 ``` ### Cấu hình Cơ sở Dữ liệu Laravel 11 sử dụng SQLite mặc định, lý tưởng cho môi trường phát triển. Đối với ứng dụng production, cấu hình MySQL hoặc PostgreSQL được thực hiện trong file `.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 ``` Xác minh kết nối cơ sở dữ liệu sử dụng lệnh Artisan. ```bash # terminal # Verify database connection php artisan db:show # Run default migrations php artisan migrate ``` ## Tạo Model và Migration Eloquent ORM đơn giản hóa thao tác dữ liệu với các model biểu cảm. Lệnh `make:model` tạo model, migration, controller và factory cùng lúc. ```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 ``` Lệnh này tạo bốn file thiết yếu cho việc quản lý công việc hoàn chỉnh. ```php 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 định nghĩa cấu trúc bảng với các quan hệ, chỉ mục và soft delete để ngăn mất dữ liệu ngoài ý muốn. ```php '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 sử dụng Eloquent scope cho các truy vấn dễ đọc và tái sử dụng. Hằng số tập trung các giá trị hợp lệ cho trạng thái và mức độ ưu tiên. > **Soft Deletes** > > Trait SoftDeletes thêm cột deleted_at. Các công việc bị xóa không biến mất khỏi cơ sở dữ liệu mà được đánh dấu là đã xóa. Sử dụng withTrashed() để đưa chúng vào truy vấn. ## Thiết lập Xác thực với Breeze Laravel Breeze cung cấp hệ thống xác thực đầy đủ với giao diện Blade hoặc API. Quá trình cài đặt mất vài phút và tạo mọi thứ cần thiết: route, controller, view và 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 tạo các route xác thực, controller và view cho đăng ký, đăng nhập, đặt lại mật khẩu và xác minh email. ```php '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(); } } ``` ## Tạo Controller và Route Controller điều phối logic nghiệp vụ giữa các yêu cầu HTTP và model. Laravel 11 khuyến khích sử dụng single-action controller hoặc RESTful resource. ```php 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 sử dụng Gate cho phân quyền, validation tích hợp sẵn và route model binding cho mã nguồn ngắn gọn và an toàn. ### Định nghĩa Route Route định nghĩa URL và liên kết chúng với các action của controller. Laravel 11 tập trung route trong `routes/web.php`. ```php 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'; ``` Phương thức `Route::resource()` tự động tạo bảy route RESTful tiêu chuẩn: index, create, store, show, edit, update và destroy. ## Thiết lập Policy Phân quyền Policy tập trung logic phân quyền cho từng model. Policy xác định ai có thể thực hiện hành động nào trên tài nguyên. ```bash # terminal # Generate the policy for Task php artisan make:policy TaskPolicy --model=Task ``` ```php 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 tự động phát hiện policy thông qua quy ước đặt tên. `TaskPolicy` áp dụng cho model `Task`. > **Bảo mật Policy** > > Policy rất quan trọng cho bảo mật. Nếu không có policy, người dùng có thể thao tác URL để truy cập công việc của người dùng khác. Luôn xác minh quyền sở hữu tài nguyên. ## Xây dựng REST API Laravel đơn giản hóa việc tạo RESTful API với route chuyên dụng, JSON resource và Sanctum cho xác thực. ```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 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 cho Chuyển đổi Dữ liệu API resource chuyển đổi model Eloquent thành phản hồi JSON có cấu trúc, kiểm soát chính xác dữ liệu được phơi bày. ```php $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 với Xác thực Sanctum ```php 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(), ]); }); }); ``` ## Kiểm thử Tự động với Pest Laravel 11 tích hợp Pest PHP mặc định, một framework kiểm thử thanh lịch và biểu cảm. Kiểm thử đảm bảo ứng dụng hoạt động chính xác sau mỗi lần thay đổi. ```php 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', ]); }); ``` Chạy kiểm thử với 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 ``` ## Tối ưu hóa cho Production Trước khi triển khai, một số bước tối ưu hóa giúp cải thiện hiệu suất ứng dụng 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 ``` ### Cấu hình File .env cho 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 ``` > **Biến Nhạy cảm** > > Tuyệt đối không commit file .env vào kho Git. Sử dụng .env.example làm template và cấu hình các biến nhạy cảm trực tiếp trên server production hoặc thông qua secret manager. ## Kết luận Laravel 11 đơn giản hóa đáng kể việc phát triển PHP hiện đại với cấu trúc tinh gọn và các quy ước thông minh. Bài hướng dẫn này đã bao quát các kiến thức nền tảng để xây dựng ứng dụng hoàn chỉnh: model Eloquent, xác thực, controller RESTful, policy phân quyền, API với Sanctum và kiểm thử tự động. ### Checklist cho Ứng dụng Laravel Chất lượng - Sử dụng migration cho mọi thay đổi schema - Triển khai policy để bảo mật truy cập tài nguyên - Validate tất cả dữ liệu đầu vào tại controller - Sử dụng API resource để kiểm soát dữ liệu được phơi bày - Viết kiểm thử cho các tính năng quan trọng - Cấu hình cache và tối ưu hóa trước khi triển khai - Bảo mật các biến môi trường nhạy cảm Hệ sinh thái Laravel tiếp tục phát triển với các gói như Livewire cho giao diện reactive, Horizon cho quản lý hàng đợi và Octane cho hiệu suất vượt trội. Làm chủ những kiến thức nền tảng này mở ra con đường tới các ứng dụng PHP mạnh mẽ và dễ bảo trì. --- Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack. HTML version of this page: https://sharpskill.dev/vi/blog/laravel/laravel-11-building-complete-application