NTM Solutions

Thứ Bảy, 8 tháng 8, 2026

🚀 Laravel 12 (2026) — Bài 27 — CRUD Posts trong Laravel 12

Sau khi hoàn thành module Categories, chúng ta sẽ xây dựng chức năng quan trọng nhất của Blog CMS: quản lý bài viết (Posts).

Khác với Category chỉ có vài trường dữ liệu, Post sẽ liên kết với nhiều bảng khác nhau như Category, User, đồng thời hỗ trợ upload hình ảnh, kiểm tra dữ liệu đầu vào và hiển thị danh sách bài viết chuyên nghiệp.

Đây cũng là module có số lượng kiến thức nhiều nhất trong toàn bộ dự án.


Mục tiêu bài học

Sau bài này bạn sẽ thực hiện được:

  • CRUD bài viết

  • Upload ảnh đại diện

  • Chọn Category

  • Gán tác giả (User)

  • Validation dữ liệu

  • Hiển thị Relationship

  • Tự động tạo slug

  • Phân trang dữ liệu


Kết quả sau bài học

Danh sách bài viết sẽ hiển thị như sau:

ẢnhTiêu đềCategoryTác giảNgày đăngThao tác
Laravel 12 mới có gì?LaravelAdmin07/08/2026Edit / Delete

1. Cấu trúc Database

Bảng posts

FieldKiểu
idbigint
user_idforeignId
category_idforeignId
titlestring
slugstring
imagestring
excerpttext
contentlongText
created_attimestamp

Quan hệ

User
   │
   ├──────< Posts >────── Category

Một User có nhiều Post.

Một Category có nhiều Post.

Một Post chỉ thuộc một User và một Category.


2. Relationship

User.php

public function posts()
{
    return $this->hasMany(Post::class);
}

Category.php

public function posts()
{
    return $this->hasMany(Post::class);
}

Post.php

public function category()
{
    return $this->belongsTo(Category::class);
}

public function user()
{
    return $this->belongsTo(User::class);
}

Đây là ba relationship được sử dụng xuyên suốt dự án.


3. Resource Controller

Tạo controller

php artisan make:controller PostController --resource

Đăng ký route

Route::resource('posts', PostController::class);

Laravel sẽ tạo sẵn:

index
create
store
show
edit
update
destroy

4. Hiển thị danh sách bài viết

Controller

public function index()
{
    $posts = Post::with(['category','user'])
        ->latest()
        ->paginate(10);

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

Điểm quan trọng

with()

Laravel sẽ eager loading.

Không xảy ra lỗi N+1 Query.


5. Hiển thị Relationship

Trong Blade

{{ $post->category->name }}
{{ $post->user->name }}

Không cần viết Query JOIN.

Đó chính là sức mạnh của Eloquent ORM.


6. Form tạo bài viết

Form sẽ có:

  • Tiêu đề

  • Slug

  • Category

  • Hình ảnh

  • Mô tả ngắn

  • Nội dung

Category

<select name="category_id">

@foreach($categories as $category)

<option value="{{ $category->id }}">
    {{ $category->name }}
</option>

@endforeach

</select>

Laravel sẽ lấy toàn bộ Category để người dùng lựa chọn.


7. Validation

$request->validate([
    'title'=>'required|max:255',
    'slug'=>'required|unique:posts',
    'category_id'=>'required|exists:categories,id',
    'image'=>'nullable|image|max:2048',
    'content'=>'required'
]);

Laravel sẽ kiểm tra:

  • tiêu đề bắt buộc

  • slug không được trùng

  • category phải tồn tại

  • chỉ nhận ảnh

  • dung lượng tối đa

  • nội dung không được rỗng


8. Upload hình ảnh

Form

enctype="multipart/form-data"

Controller

$image = null;

if($request->hasFile('image')){

    $image = $request->file('image')
        ->store('posts','public');

}

Kết quả

storage/app/public/posts/

abc123.jpg

xyz456.png

Laravel sẽ tự sinh tên file ngẫu nhiên để tránh trùng.


9. Lưu dữ liệu

Post::create([

    'user_id'=>auth()->id(),

    'category_id'=>$request->category_id,

    'title'=>$request->title,

    'slug'=>$request->slug,

    'image'=>$image,

    'excerpt'=>$request->excerpt,

    'content'=>$request->content

]);

Tác giả sẽ được lấy từ

auth()->id()

Người dùng không thể giả mạo tác giả bài viết.


10. Hiển thị ảnh

Blade

<img src="{{ asset('storage/'.$post->image) }}">

Đừng quên tạo symbolic link

php artisan storage:link

Sau khi chạy lệnh này, Laravel sẽ tạo

public/storage

trỏ tới

storage/app/public

11. Cập nhật bài viết

Nếu người dùng chọn ảnh mới

Storage::disk('public')->delete($post->image);

sau đó upload lại.

Nếu không chọn ảnh

Laravel vẫn giữ ảnh cũ.

Đây là cách xử lý được sử dụng trong hầu hết các CMS hiện nay.


12. Xóa bài viết

Khi xóa

Storage::disk('public')->delete($post->image);

$post->delete();

Không nên để file ảnh bị "mồ côi" trong thư mục Storage.


13. Giao diện quản trị

Danh sách nên hiển thị

  • Thumbnail

  • Tiêu đề

  • Danh mục

  • Tác giả

  • Ngày tạo

  • Trạng thái

  • Nút Edit

  • Nút Delete

Có thể kết hợp:

  • Tailwind CSS

  • DataTables.net

  • Badge màu

  • Icon Heroicons

  • Xác nhận trước khi xóa

Đây cũng là giao diện mà chúng ta sẽ sử dụng xuyên suốt các module còn lại.


14. Kết quả đạt được

Sau bài học này, Blog CMS đã có module quản lý bài viết hoàn chỉnh.

✔ Thêm bài viết

✔ Sửa bài viết

✔ Xóa bài viết

✔ Upload ảnh

✔ Relationship User

✔ Relationship Category

✔ Validation

✔ Hiển thị ảnh

✔ Phân trang

✔ Quản lý tác giả

Đây là một module CRUD thực tế mà bạn sẽ gặp trong hầu hết các hệ thống quản trị nội dung sử dụng Laravel.


Tổng kết

Trong bài học này chúng ta đã kết hợp rất nhiều kiến thức đã học trước đó:

  • Resource Controller để xây dựng CRUD nhanh chóng.

  • Eloquent Relationship để liên kết Post với Category và User.

  • Validation để đảm bảo dữ liệu hợp lệ.

  • Storage để quản lý hình ảnh.

  • Authentication để tự động gán tác giả cho bài viết.

  • Pagination giúp danh sách bài viết dễ theo dõi khi số lượng tăng lên.

Đến thời điểm này, dự án Blog CMS đã gần hoàn chỉnh và có cấu trúc tương tự nhiều hệ thống quản trị nội dung được sử dụng trong thực tế.


Bài tiếp theo

Bài 28 — CRUD Users, chúng ta sẽ xây dựng module quản lý người dùng dành cho quản trị viên:

  • Danh sách User

  • Chỉnh sửa thông tin

  • Thay đổi Role (Admin/User)

  • Khóa hoặc mở khóa tài khoản

  • Đặt lại mật khẩu

Sau bài này, Blog CMS sẽ có đầy đủ ba module quản trị cốt lõi: Categories, PostsUsers.

Không có nhận xét nào:

Đăng nhận xét

Facebook Youtube RSS