Ở các bài CRUD trước, khi gọi:
$user->delete();
record sẽ bị xóa khỏi database.
Ví dụ:
users
────────────────────────
id | name
1 | Nguyễn Văn A
2 | Trần Văn B
3 | Lê Văn C
Xóa User id = 2:
$user->delete();
thì record có thể biến mất hoàn toàn:
users
────────────────────────
id | name
1 | Nguyễn Văn A
3 | Lê Văn C
Trong hệ thống thực tế, cách này khá nguy hiểm.
Ví dụ Admin vô tình xóa một bài viết quan trọng. Chúng ta sẽ muốn:
🗑️ Xóa
↓
📦 Đưa vào Thùng rác
↓
♻️ Có thể khôi phục
Đây chính là Soft Delete.
MỤC LỤC
- Soft Delete là gì?
- Khi nào nên dùng Soft Delete?
- Thêm cột
deleted_at - Soft Delete cho Posts
- Thêm
SoftDeletesvào Model delete()lúc này hoạt động thế nào?- Vì sao User đã xóa không xuất hiện?
withTrashed()onlyTrashed()- Restore
- Route Restore
- Controller Restore
- Nút Restore trong Trash
forceDelete()- Phân biệt
delete()vàforceDelete() - Tạo trang Trash
- Hiển thị Trash
- Xóa vĩnh viễn
- Controller
forceDelete() - Xóa toàn bộ Trash
- Restore nhiều User
- Nút Trash trong trang Users
- Luồng hoạt động hoàn chỉnh
- Soft Delete với Posts
- Soft Delete và Relationship
- Kiểm tra một record có bị xóa không
- Soft Delete không phải Backup
- Khi nào không nên dùng Soft Delete?
- Best Practice
- Tổng kết Bài 30
1. Soft Delete là gì?
Soft Delete là cơ chế đánh dấu một record đã bị xóa thay vì xóa thật khỏi database.
Laravel sử dụng cột:
deleted_at
Ví dụ:
posts
────────────────────────────────────
id | title | deleted_at
1 | Bài viết 1 | NULL
2 | Bài viết 2 | 2026-08-18 10:30:00
3 | Bài viết 3 | NULL
Trong đó:
deleted_at = NULL
→ record đang hoạt động.
deleted_at = thời gian
→ record đã bị Soft Delete.
2. Khi nào nên dùng Soft Delete?
Soft Delete đặc biệt hữu ích với:
Users
Posts
Categories
Products
Orders
Customers
Documents
Comments
Đặc biệt với Admin CMS:
Xóa
↓
Trash
↓
Restore
thường an toàn hơn xóa vĩnh viễn.
3. Thêm cột deleted_at
Tạo migration:
php artisan make:migration add_deleted_at_to_users_table
Mở migration:
Schema::table('users', function (Blueprint $table) {
$table->softDeletes();
});
Laravel sẽ tạo:
deleted_at
với kiểu dữ liệu phù hợp.
Sau đó:
php artisan migrate
4. Soft Delete cho Posts
Nếu muốn áp dụng cho Posts:
php artisan make:migration add_deleted_at_to_posts_table
Migration:
Schema::table('posts', function (Blueprint $table) {
$table->softDeletes();
});
Sau đó:
php artisan migrate
5. Thêm SoftDeletes vào Model
Đây là bước quan trọng.
Mở:
app/Models/User.php
Thêm:
use Illuminate\Database\Eloquent\SoftDeletes;
Sau đó:
class User extends Authenticatable
{
use SoftDeletes;
}
Model hoàn chỉnh có thể:
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Foundation\Auth\User as Authenticatable;
class User extends Authenticatable
{
use HasFactory;
use SoftDeletes;
}
6. delete() lúc này hoạt động thế nào?
Khi:
$user->delete();
Laravel không xóa record khỏi database.
Thay vào đó:
deleted_at = thời gian hiện tại
Ví dụ:
id | name | deleted_at
1 | Nguyễn Văn A | NULL
2 | Trần Văn B | 2026-08-18 11:00:00
User id = 2 vẫn tồn tại trong database.
7. Vì sao User đã xóa không xuất hiện?
Laravel tự động loại các record Soft Delete khỏi những truy vấn Eloquent thông thường.
Ví dụ:
$users = User::all();
sẽ chỉ lấy:
deleted_at IS NULL
Do đó User đã đưa vào Trash sẽ không xuất hiện trong danh sách bình thường.
Đây là một trong những ưu điểm lớn của Soft Delete.
8. withTrashed()
Nếu muốn lấy cả record đang hoạt động và đã xóa:
$users = User::withTrashed()->get();
Kết quả:
User bình thường
+
User đã Soft Delete
9. onlyTrashed()
Nếu chỉ muốn lấy những record đã xóa:
$users = User::onlyTrashed()->get();
Đây chính là dữ liệu cho trang:
🗑️ Trash
Ví dụ:
public function trash()
{
$users = User::onlyTrashed()->latest('deleted_at')->paginate(10);
return view('users.trash', compact('users'));
}
10. Restore
Muốn khôi phục User:
$user->restore();
Laravel sẽ đưa:
deleted_at
về:
NULL
Ví dụ:
Trước:
id | name | deleted_at
2 | ABC | 2026-08-18
Sau restore:
id | name | deleted_at
2 | ABC | NULL
User xuất hiện trở lại trong danh sách bình thường.
11. Route Restore
Trong:
routes/web.php
thêm:
Route::patch(
'users/{user}/restore',
[UserController::class, 'restore']
)->name('users.restore');
12. Controller Restore
Trong UserController:
public function restore($id)
{
$user = User::withTrashed()->findOrFail($id);
$user->restore();
return redirect()
->route('users.index')
->with('success', 'Khôi phục User thành công.');
}
Chú ý:
withTrashed()
rất quan trọng.
Nếu không có nó, User đã Soft Delete sẽ không được tìm thấy bởi truy vấn Eloquent thông thường.
13. Nút Restore trong Trash
Blade:
<form
method="POST"
action="{{ route('users.restore', $user->id) }}"
>
@csrf
@method('PATCH')
<button type="submit">
♻️ Khôi phục
</button>
</form>
14. forceDelete()
Soft Delete không xóa thật.
Nếu muốn xóa vĩnh viễn:
$user->forceDelete();
Record sẽ thực sự bị xóa khỏi database.
Ví dụ:
Soft Delete:
id | name | deleted_at
2 | ABC | 2026-08-18
Sau:
$user->forceDelete();
record id = 2 không còn tồn tại.
15. Phân biệt delete() và forceDelete()
| Method | Kết quả |
|---|---|
delete() | Soft Delete |
restore() | Khôi phục |
forceDelete() | Xóa vĩnh viễn |
withTrashed() | Lấy cả record đã xóa |
onlyTrashed() | Chỉ lấy record đã xóa |
Đây là 5 method cần nhớ.
16. Tạo trang Trash
Route:
Route::get(
'users/trash',
[UserController::class, 'trash']
)->name('users.trash');
Controller:
public function trash()
{
$users = User::onlyTrashed()
->latest('deleted_at')
->paginate(10);
return view('users.trash', compact('users'));
}
17. Hiển thị Trash
Ví dụ:
<table class="w-full">
<thead>
<tr>
<th>ID</th>
<th>Name</th>
<th>Email</th>
<th>Deleted At</th>
<th>Action</th>
</tr>
</thead>
<tbody>
@foreach ($users as $user)
<tr>
<td>{{ $user->id }}</td>
<td>{{ $user->name }}</td>
<td>{{ $user->email }}</td>
<td>
{{ $user->deleted_at }}
</td>
<td>
<form
method="POST"
action="{{ route('users.restore', $user->id) }}"
>
@csrf
@method('PATCH')
<button type="submit">
♻️ Restore
</button>
</form>
</td>
</tr>
@endforeach
</tbody>
</table>
{{ $users->links() }}
18. Xóa vĩnh viễn
Trong Trash:
<form
method="POST"
action="{{ route('users.forceDelete', $user->id) }}"
>
@csrf
@method('DELETE')
<button type="submit">
🗑️ Xóa vĩnh viễn
</button>
</form>
Route:
Route::delete(
'users/{user}/force-delete',
[UserController::class, 'forceDelete']
)->name('users.forceDelete');
19. Controller forceDelete()
public function forceDelete($id)
{
$user = User::onlyTrashed()->findOrFail($id);
$user->forceDelete();
return redirect()
->route('users.trash')
->with('success', 'Đã xóa vĩnh viễn.');
}
Ở đây sử dụng:
onlyTrashed()
để đảm bảo chỉ xóa vĩnh viễn những record đang nằm trong Trash.
20. Xóa toàn bộ Trash
Có thể xóa nhiều record:
User::onlyTrashed()->forceDelete();
Hoặc:
User::onlyTrashed()->each(function ($user) {
$user->forceDelete();
});
Tuy nhiên với dữ liệu lớn, cần cân nhắc cách xử lý để tránh tải quá nhiều model vào memory.
21. Restore nhiều User
Có thể khôi phục nhiều record:
User::onlyTrashed()
->whereIn('id', $ids)
->restore();
Ví dụ:
ids = [5, 8, 12]
sẽ khôi phục ba User.
22. Nút Trash trong trang Users
Trang Users có thể thêm:
<a href="{{ route('users.trash') }}">
🗑️ Thùng rác
</a>
Giao diện:
Users
[+ Thêm User] [🗑️ Thùng rác]
-------------------------------------
| ID | Name | Email | Role | Action |
-------------------------------------
23. Luồng hoạt động hoàn chỉnh
USERS
│
│ delete()
↓
Soft Delete
│
↓
deleted_at
│
↓
TRASH
↙ ↘
restore() forceDelete()
↓ ↓
Users Xóa thật
24. Soft Delete với Posts
Posts cũng tương tự.
Model:
use Illuminate\Database\Eloquent\SoftDeletes;
class Post extends Model
{
use SoftDeletes;
}
Migration:
$table->softDeletes();
Xóa:
$post->delete();
Trash:
Post::onlyTrashed()->get();
Restore:
$post->restore();
Xóa vĩnh viễn:
$post->forceDelete();
25. Soft Delete và Relationship
Ví dụ User có Posts:
$user->posts
Các Post đã Soft Delete thông thường sẽ không xuất hiện.
Nếu muốn lấy cả Posts đã xóa:
$user->posts()->withTrashed()->get();
Chỉ lấy Posts đã xóa:
$user->posts()->onlyTrashed()->get();
Điều này rất quan trọng khi xây dựng hệ thống CMS có nhiều relationship.
26. Kiểm tra một record có bị xóa không
Có thể sử dụng:
$user->trashed();
Nếu:
true
→ User đang bị Soft Delete.
Nếu:
false
→ User đang hoạt động.
Ví dụ:
if ($user->trashed()) {
// User đang nằm trong Trash
}
27. Soft Delete không phải Backup
Đây là điểm rất quan trọng.
Soft Delete:
Database
↓
deleted_at
không phải:
Backup Database
Nếu database bị:
mất dữ liệu
hỏng ổ đĩa
DROP TABLE
server lỗi
thì Soft Delete không thể bảo vệ dữ liệu.
Vì vậy dự án thực tế vẫn cần:
Database Backup
+
Soft Delete
28. Khi nào không nên dùng Soft Delete?
Không phải bảng nào cũng cần.
Ví dụ một số dữ liệu tạm thời hoặc dữ liệu không cần khôi phục có thể xóa trực tiếp.
Soft Delete cũng làm database giữ lại record đã xóa, vì vậy bảng có lượng dữ liệu rất lớn cần có chiến lược dọn dẹp phù hợp.
29. Best Practice
Đối với Blog CMS trong khóa học này, có thể áp dụng:
Users
↓
Soft Delete
Posts
↓
Soft Delete
Categories
↓
Soft Delete
Giao diện Admin:
Users
├── Active
└── Trash
Posts
├── Published
├── Draft
└── Trash
Categories
├── Active
└── Trash
30. Tổng kết Bài 30
Sau bài này, chúng ta đã biết:
Soft Delete
↓
deleted_at
Các method quan trọng:
delete()
→ đưa vào Trash.
restore()
→ khôi phục.
forceDelete()
→ xóa vĩnh viễn.
withTrashed()
→ lấy cả record đã xóa.
onlyTrashed()
→ chỉ lấy record đã xóa.
trashed()
→ kiểm tra record có đang bị Soft Delete hay không.
Mô hình hoàn chỉnh:
CRUD
│
↓
Users
│
┌────┴────┐
↓ ↓
Active Trash
│
┌────┴────┐
↓ ↓
Restore Force Delete
│
↓
Active
🎯 Bài tập thực hành
Thêm Soft Delete cho
users.Thêm Soft Delete cho
posts.Thêm nút 🗑️ Thùng rác.
Hiển thị danh sách record đã xóa.
Thêm ♻️ Restore.
Thêm 🗑️ Xóa vĩnh viễn.
Thêm xác nhận trước khi
forceDelete().Thêm Pagination cho Trash.
Thử kết hợp Trash với DataTables ở Bài 29.
Bài tiếp theo — Bài 31: Accessor & Mutator sẽ tìm hiểu cách Laravel biến đổi dữ liệu khi đọc/ghi Model, ví dụ định dạng tên, tạo thuộc tính ảo và tự động xử lý dữ liệu trước khi lưu vào database.




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