Laravel Soft Delete & Role-Based: Nghệ Thuật Ẩn Hiện Dữ Liệu Theo Quyền Hạn
Trong các hệ thống quản trị, việc xóa "thẳng tay" (Hard Delete) dữ liệu là cực kỳ mạo hiểm. Laravel cung cấp Soft Delete để đưa dữ liệu vào "thùng rác" thay vì xóa vĩnh viễn.
Tuy nhiên, một yêu cầu phổ biến là: Chỉ Admin mới có quyền xem và khôi phục các bản ghi đã xóa. Hôm nay, mình sẽ hướng dẫn các bạn cách tích hợp withTrashed() vào hệ thống phân quyền (Role-based) một cách mượt mà nhất.
1. Thiết lập Soft Delete cơ bản
Đầu tiên, hãy đảm bảo Model của bạn đã sẵn sàng.
Migration:
Schema::table('posts', function (Blueprint $table) {
$table->softDeletes(); // Tạo cột deleted_at
});
Model:
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
class Post extends Model {
use SoftDeletes;
}
2. Truy vấn dữ liệu dựa trên Role
Vấn đề nảy sinh khi bạn viết Controller. Bạn không thể cứ Post::withTrashed()->get() cho tất cả mọi người. Chúng ta cần một cơ chế kiểm tra quyền.
Cách 1: Xử lý trực tiếp tại Controller (Dành cho dự án nhỏ)
public function index(Request $request)
{
$query = Post::query();
// Nếu là Admin, cho phép xem cả hàng đã xóa
if ($request->user()->hasRole('admin')) {
$query->withTrashed();
}
return $query->paginate(10);
}
Cách 2: Sử dụng Local Scope (Sạch sẽ và tái sử dụng)
Thay vì viết if-else khắp nơi, hãy đưa logic này vào Model.
Model Post:
public function scopeWithTrashedIfAdmin($query)
{
if (auth()->check() && auth()->user()->hasRole('admin')) {
return $query->withTrashed();
}
return $query;
}
Sử dụng tại Controller:
$posts = Post::withTrashedIfAdmin()->get();
3. Bảo vệ Route với Policy (Nâng cao)
Khi Admin muốn xem chi tiết một bản ghi đã Soft Delete (ví dụ: posts/99), Laravel mặc định sẽ trả về 404 nếu bạn dùng Route Model Binding. Để fix việc này, chúng ta cần can thiệp vào Route.
Route:
Route::get('/posts/{post}', [PostController::class, 'show'])->withTrashed();
Policy: Sau khi Route cho phép nhận cả bản ghi đã xóa, bạn phải dùng Policy để ngăn User thường "dòm ngó":
public function view(User $user, Post $post)
{
// Nếu post đã bị xóa, chỉ Admin mới được xem
if ($post->trashed()) {
return $user->hasRole('admin');
}
return true; // Post chưa xóa thì ai cũng xem được
}
4. Trải nghiệm người dùng: Thùng rác (Trash Can)
Một hệ thống chuyên nghiệp thường có một menu riêng gọi là "Thùng rác". Tại đây, chúng ta sử dụng onlyTrashed():
public function trash()
{
$this->authorize('viewTrash', Post::class); // Chỉ Admin mới vào được route này
$deletedPosts = Post::onlyTrashed()->get();
return view('posts.trash', compact('deletedPosts'));
}
5. Những lưu ý "xương máu"
Quan hệ (Relations): Khi bạn dùng withTrashed() trên Model cha, các Model con liên quan (nếu cũng dùng Soft Delete) sẽ KHÔNG tự động hiện ra trừ khi bạn gọi withTrashed() trên từng quan hệ đó.
Unique Constraint: Nếu bạn có cột email là Unique, khi Soft Delete một User, bạn sẽ không thể tạo User mới với email đó vì nó vẫn tồn tại trong DB.
Giải pháp: Append thêm timestamp vào email khi xóa hoặc sử dụng Index phức hợp bao gồm cả deleted_at.
Hiệu năng: deleted_at mặc định không có Index. Nếu bảng của bạn có hàng triệu dòng, hãy nhớ đánh Index cho cột này để tránh làm chậm câu Query WHERE deleted_at IS NULL.
Kết luận
Tích hợp Soft Delete với Role-Based giúp hệ thống của bạn trở nên an toàn và chuyên nghiệp hơn rất nhiều. Admin có quyền "sinh sát" và khôi phục, trong khi User vẫn có trải nghiệm sạch sẽ.
Hy vọng bài viết này giúp anh em làm chủ được thực thể Eloquent trong Laravel. Đừng quên Upvote và chia sẻ nếu bạn thấy hữu ích nhé!
Cảm ơn các bạn đã đọc bài!
All rights reserved