Bỏ qua đến nội dung chính

Controller trong Laravel: cách tạo và các phương thức CRUD

Controller gom các hàm xử lý request của cùng một đối tượng. Bài viết hướng dẫn tạo controller, resource controller CRUD, middleware và nguyên tắc giữ controller gọn.

Tan Hien
Founder
Cập nhật 5 phút đọc
Chia sẻ:
Controller trong Laravel: cách tạo và các phương thức CRUD
Nội dung bài viết
  1. 1.Tạo controller bằng Artisan
  2. 2.Controller cơ bản và cách nối với route
  3. 3.Resource controller: 7 phương thức CRUD
  4. 3.1index(): danh sách
  5. 3.2create() và store(): form tạo mới và lưu
  6. 3.3show(): xem chi tiết
  7. 3.4edit() và update(): form sửa và cập nhật
  8. 3.5destroy(): xóa
  9. 3.6Giới hạn phương thức của resource
  10. 4.Gắn middleware cho controller
  11. 5.Single action controller và dependency injection
  12. 6.Nguyên tắc giữ controller gọn
  13. 7.Kinh nghiệm từ Hozitech

Controller trong Laravel là class gom các hàm xử lý request liên quan tới cùng một đối tượng, ví dụ PostController lo hiển thị, tạo, sửa, xóa bài viết. Route nhận request rồi chuyển cho một phương thức của controller; controller gọi model lấy dữ liệu và trả về view hoặc JSON. Controller nằm trong app/Http/Controllers, là chữ C trong mô hình MVC.

Bài viết cập nhật theo Laravel 13 (bản hiện hành, PHP 8.3+): tạo controller, nối với route, resource controller với 7 phương thức CRUD, gắn middleware kiểu mới và các nguyên tắc giữ controller gọn.

Tạo controller bằng Artisan

php artisan make:controller PostController                         # controller trống
php artisan make:controller PostController --resource --model=Post # đủ 7 phương thức CRUD
php artisan make:controller PostController --api                   # bỏ create, edit (cho API)
php artisan make:controller ExportPostsController --invokable      # controller một hành động

Lệnh không có tùy chọn tạo ra class trống, chưa có phương thức nào. Muốn có sẵn khung CRUD thì thêm --resource; thêm --model=Post để các phương thức nhận sẵn tham số Post $post, và --requests nếu muốn tạo kèm Form Request cho store, update.

Controller được tạo kế thừa App\Http\Controllers\Controller. Từ Laravel 11, lớp cơ sở này là lớp rỗng: không còn sẵn $this->middleware(), $this->authorize() hay $this->validate() như các bản cũ.

Controller cơ bản và cách nối với route

<?php
namespace App\Http\Controllers;
use App\Models\Post;
use Illuminate\View\View;
class PostController extends Controller
{
    public function show(Post $post): View
    {
        return view('posts.show', ['post' => $post]);
    }
}
// routes/web.php
use App\Http\Controllers\PostController;
Route::get('/posts/{post}', [PostController::class, 'show'])->name('posts.show');

Cú pháp chuỗi 'PostController@show' trong các bài hướng dẫn cũ không còn là chuẩn: từ Laravel 8 nó không chạy nếu không cấu hình namespace. Dùng mảng [PostController::class, 'show'] để IDE gợi ý và báo lỗi khi đổi tên class. Tham số {post} được Laravel đổi thành model nhờ route model binding, giải thích trong bài route trong Laravel.

Resource controller: 7 phương thức CRUD

Một dòng Route::resource('posts', PostController::class) sinh đủ route cho thao tác thêm, xem, sửa, xóa:

Bảng 7 phương thức của resource controller trong Laravel: index, create, store, show, edit, update, destroy với method, URI và tên route
Các route và phương thức mà Route::resource sinh ra

Dưới đây là controller hoàn chỉnh, thống nhất dùng cột title và body, có kiểm tra dữ liệu và phân quyền.

index(): danh sách

public function index(): View
{
    $posts = Post::with('user')->latest()->paginate(10);
    return view('posts.index', compact('posts'));
}

create() và store(): form tạo mới và lưu

public function create(): View
{
    return view('posts.create');
}
public function store(StorePostRequest $request): RedirectResponse
{
    $post = $request->user()->posts()->create($request->validated());
    return redirect()->route('posts.show', $post)
        ->with('status', 'Đã tạo bài viết.');
}

StorePostRequest là Form Request chứa luật kiểm tra dữ liệu; nếu dữ liệu sai, Laravel tự quay lại form kèm lỗi mà không chạy vào store(). Cách viết Form Request có trong bài Laravel validation.

show(): xem chi tiết

public function show(Post $post): View
{
    return view('posts.show', compact('post'));
}

edit() và update(): form sửa và cập nhật

public function edit(Post $post): View
{
    Gate::authorize('update', $post);
    return view('posts.edit', compact('post'));
}
public function update(UpdatePostRequest $request, Post $post): RedirectResponse
{
    Gate::authorize('update', $post);
    $post->update($request->validated());
    return redirect()->route('posts.show', $post)
        ->with('status', 'Đã cập nhật.');
}

destroy(): xóa

public function destroy(Post $post): RedirectResponse
{
    Gate::authorize('delete', $post);
    $post->delete();
    return redirect()->route('posts.index')
        ->with('status', 'Đã xóa bài viết.');
}

Phần đầu file cần các dòng use: App\Models\Post, App\Http\Requests\StorePostRequest, App\Http\Requests\UpdatePostRequest, Illuminate\Http\RedirectResponse, Illuminate\Support\Facades\Gate, Illuminate\View\View. Gate::authorize() dùng Policy PostPolicy; xem bài authentication và authorization trong Laravel.

Giới hạn phương thức của resource

Route::resource('posts', PostController::class)->only(['index', 'show']);
Route::resource('posts', PostController::class)->except(['destroy']);
Route::apiResource('posts', Api\PostController::class); // không có create, edit

Cú pháp mảng cũ Route::resource('post', 'PostController', ['only' => [...]]) nên đổi sang ->only([...]) như trên. Route bổ sung (ví dụ /posts/popular) phải khai báo trước Route::resource, nếu không sẽ bị route show bắt mất.

Gắn middleware cho controller

Cách đơn giản nhất là gắn ở route: Route::resource('posts', PostController::class)->middleware('auth'). Nếu muốn khai báo ngay trong controller, kiểu cũ $this->middleware('auth') trong __construct() không còn chạy từ Laravel 11. Thay bằng interface HasMiddleware:

use Illuminate\Routing\Controllers\HasMiddleware;
use Illuminate\Routing\Controllers\Middleware;
class PostController extends Controller implements HasMiddleware
{
    public static function middleware(): array
    {
        return [
            new Middleware('auth', except: ['index', 'show']),
        ];
    }
}

Laravel 13 cho phép viết gọn hơn bằng attribute #[Middleware('auth', except: ['index', 'show'])] đặt trên class, hoặc #[Authorize('update', 'post')] trên từng phương thức. Chi tiết về middleware có trong bài middleware trong Laravel.

Single action controller và dependency injection

Hành động phức tạp, không thuộc CRUD (xuất file, đồng bộ dữ liệu) nên tách thành controller một phương thức __invoke():

class ExportPostsController extends Controller
{
    public function __invoke(PostExporter $exporter)
    {
        return $exporter->download();
    }
}
Route::post('/posts/export', ExportPostsController::class);

Ví dụ trên cũng cho thấy dependency injection: Laravel tự khởi tạo và truyền PostExporter (một class bạn tự viết) vào phương thức. Có thể inject qua constructor theo cách tương tự. Khi phương thức vừa nhận Request vừa nhận tham số route, đặt tham số route ở sau.

Nguyên tắc giữ controller gọn

  • Kiểm tra dữ liệu bằng Form Request, không viết hàng chục dòng validate trong controller.
  • Phân quyền bằng Policy, controller chỉ gọi Gate::authorize().
  • Truy vấn phức tạp đặt trong model (scope, quan hệ), xem bài model trong Laravel.
  • Logic nghiệp vụ nhiều bước (tạo đơn, trừ kho, gửi email) đặt ở class riêng, controller chỉ điều phối.
  • Controller vượt quá 7 phương thức resource thường là dấu hiệu nên tách thành controller nhỏ hơn.

Kinh nghiệm từ Hozitech

Trong các dự án Laravel của Hozitech, controller trang quản trị thường được viết dạng resource controller đi kèm Form Request và Policy; nhờ vậy khi bàn giao mã nguồn cho khách hàng, lập trình viên khác đọc vào là biết ngay chỗ kiểm tra dữ liệu, chỗ phân quyền và chỗ xử lý. Các tác vụ đặc thù như xuất báo cáo hay đồng bộ đơn hàng được tách thành controller một hành động.

Nếu doanh nghiệp cần xây dựng website với mã nguồn rõ ràng, được bàn giao đầy đủ, có thể tham khảo dịch vụ thiết kế website của Hozitech.

Tan Hien

Về tác giả

Founder

Xin chào mình là Hiền hiện đang là Founder của Hozitech. Mình là kỹ sư phần mềm có hơn 9+ năm kinh nghiệm trong lĩnh vực thiết kế website, ứng dụng di động... Gần đây mình đang nghiên cứu thêm về lĩnh vực marketing. Mình rất vui khi được chia sẽ những kiến thức và kinh nghiệm của mình đến mọi người!

Câu hỏi thường gặp

Câu hỏi thường gặp

Sẵn sàng bắt đầu dự án của bạn?

Hãy để chúng tôi giúp bạn biến ý tưởng thành hiện thực. Đăng ký tư vấn miễn phí ngay hôm nay để nhận được giải pháp công nghệ tối ưu cho doanh nghiệp.