Trong các bài trước, chúng ta đã xây dựng được các chức năng CRUD cho:
Categories
Posts
Users
Tuy nhiên, khi dữ liệu bắt đầu nhiều lên, một bảng kiểu:
User::all();sẽ nhanh chóng trở nên bất tiện.
Ví dụ hệ thống có 10.000 User:
Không thể hiển thị tất cả trên một trang.
Người quản trị cần tìm User theo tên/email.
Cần sắp xếp theo tên, ngày đăng ký...
Cần lọc theo Role.
Cần phân trang.
Và nếu dữ liệu lớn, nên để Database xử lý Search / Sort / Filter / Pagination thay vì tải toàn bộ dữ liệu về trình duyệt.
Trong Laravel 13, Query Builder/Eloquent hỗ trợ trực tiếp các cơ chế phân trang như paginate(), simplePaginate() và cursorPaginate(). (Laravel API)
🎯 Mục tiêu bài học
Sau bài này, chúng ta sẽ xây dựng trang:
Dashboard
└── Users
├── 🔎 Search
├── 🎯 Filter Role
├── ↕ Sort
├── 📄 Pagination
└── 📊 DataTablesNgười học sẽ hiểu được hai cách:
Cách 1 — Laravel Pagination
Browser
↓
Request
↓
Laravel
↓
MySQL
↓
paginate()
↓
BladeCách 2 — DataTables AJAX
Browser
↓
DataTables
↓ AJAX
Laravel
↓
MySQL
↓
JSON
↓
DataTablesVới dữ liệu rất lớn, DataTables có cơ chế server-side processing, tức là việc tìm kiếm, sắp xếp và phân trang được thực hiện phía server/database thay vì tải toàn bộ dữ liệu về browser. (DataTables Plus)
1. Chuẩn bị dữ liệu Users
Trong dự án Laravel 13 của chúng ta đã có bảng:
usersVí dụ:
| id | name | role | |
|---|---|---|---|
| 1 | Nguyễn Văn A | a@gmail.com | admin |
| 2 | Trần Văn B | b@gmail.com | user |
| 3 | Lê Văn C | c@gmail.com | editor |
Model:
app/Models/User.phpVí dụ:
class User extends Authenticatable
{
protected $fillable = [
'name',
'email',
'password',
'role',
];
}2. Tạo User Controller
Nếu chưa có:
php artisan make:controller UserController --resourceLaravel sẽ tạo:
app/Http/Controllers/UserController.phpController có các method:
index()
create()
store()
show()
edit()
update()
destroy()Trong bài này chúng ta tập trung vào:
index()3. Route
Mở:
routes/web.phpThêm:
use App\Http\Controllers\UserController;
Route::resource('users', UserController::class);Sau đó:
php artisan route:listChúng ta sẽ có:
GET /users
POST /users
GET /users/create
GET /users/{user}
GET /users/{user}/edit
PUT /users/{user}
DELETE /users/{user}Trang danh sách User:
/userssẽ chạy:
UserController@index4. Pagination cơ bản
Đầu tiên thử cách đơn giản nhất.
Trong:
UserController.phpviết:
use App\Models\User;
public function index()
{
$users = User::latest()->paginate(10);
return view('users.index', compact('users'));
}Ý nghĩa:
paginate(10)là:
Mỗi trang tối đa 10 User.
Ví dụ có 53 User:
Trang 1 → 10 User
Trang 2 → 10 User
Trang 3 → 10 User
Trang 4 → 10 User
Trang 5 → 10 User
Trang 6 → 3 UserLaravel cung cấp paginate() để tạo paginator đầy đủ thông tin tổng số bản ghi; ngoài ra còn có simplePaginate() và cursorPaginate() cho những trường hợp phù hợp khác. (Laravel API)
5. Hiển thị Pagination trong Blade
Trong:
resources/views/users/index.blade.phpsau bảng:
<div class="mt-4">
{{ $users->links() }}
</div>Ví dụ:
<table class="w-full">
<thead>
<tr>
<th>ID</th>
<th>Name</th>
<th>Email</th>
<th>Role</th>
</tr>
</thead>
<tbody>
@foreach ($users as $user)
<tr>
<td>{{ $user->id }}</td>
<td>{{ $user->name }}</td>
<td>{{ $user->email }}</td>
<td>{{ $user->role }}</td>
</tr>
@endforeach
</tbody>
</table>
<div class="mt-4">
{{ $users->links() }}
</div>6. Search User
Bây giờ chúng ta thêm ô tìm kiếm.
Giao diện:
┌─────────────────────────────────────────────┐
│ 🔎 Tìm User... [Search] │
└─────────────────────────────────────────────┘Blade:
<form method="GET" action="{{ route('users.index') }}">
<input
type="text"
name="search"
value="{{ request('search') }}"
placeholder="Tìm tên hoặc email..."
>
<button type="submit">
Tìm kiếm
</button>
</form>Khi người dùng nhập:
nguyenURL sẽ trở thành:
/users?search=nguyen7. Xử lý Search trong Controller
Sửa:
public function index()
{
$users = User::latest()->paginate(10);
return view('users.index', compact('users'));
}thành:
public function index()
{
$query = User::query();
if (request('search')) {
$search = request('search');
$query->where(function ($q) use ($search) {
$q->where('name', 'like', "%{$search}%")
->orWhere('email', 'like', "%{$search}%");
});
}
$users = $query
->latest()
->paginate(10)
->withQueryString();
return view('users.index', compact('users'));
}Đây là một pattern cực kỳ quan trọng:
$query = User::query();Sau đó xây dựng query từng bước.
8. Vì sao dùng withQueryString()?
Nếu tìm:
nguyentrang đầu tiên có URL:
/users?search=nguyenKhi chuyển sang trang 2, chúng ta muốn:
/users?page=2&search=nguyenchứ không phải:
/users?page=2Vì vậy:
->withQueryString()giúp giữ lại các query parameter hiện tại.
9. Search nhiều trường
Chúng ta có thể tìm:
name
emailbằng:
$query->where(function ($q) use ($search) {
$q->where('name', 'like', "%{$search}%")
->orWhere('email', 'like', "%{$search}%");
});SQL tương đương về ý tưởng:
WHERE
name LIKE '%nguyen%'
OR email LIKE '%nguyen%'Đây là lý do phải sử dụng:
where(function () {})để nhóm điều kiện OR lại với nhau.
10. Filter theo Role
Bây giờ thêm bộ lọc:
Role:
[ Tất cả ▼ ]
[ admin ]
[ editor ]
[ user ]Blade:
<select name="role">
<option value="">Tất cả Role</option>
<option value="admin"
@selected(request('role') === 'admin')>
Admin
</option>
<option value="editor"
@selected(request('role') === 'editor')>
Editor
</option>
<option value="user"
@selected(request('role') === 'user')>
User
</option>
</select>11. Xử lý Filter
Trong Controller:
public function index()
{
$query = User::query();
// SEARCH
if ($search = request('search')) {
$query->where(function ($q) use ($search) {
$q->where('name', 'like', "%{$search}%")
->orWhere('email', 'like', "%{$search}%");
});
}
// FILTER ROLE
if ($role = request('role')) {
$query->where('role', $role);
}
// PAGINATION
$users = $query
->latest()
->paginate(10)
->withQueryString();
return view('users.index', compact('users'));
}Bây giờ có thể:
Search:
nguyen
Role:
adminURL:
/users?search=nguyen&role=adminDatabase sẽ tìm:
name/email chứa "nguyen"
AND
role = admin12. Sort
Bây giờ chúng ta muốn:
Sắp xếp:
Mới nhất
Cũ nhất
Tên A → Z
Tên Z → ABlade:
<select name="sort">
<option value="latest"
@selected(request('sort', 'latest') === 'latest')>
Mới nhất
</option>
<option value="oldest"
@selected(request('sort') === 'oldest')>
Cũ nhất
</option>
<option value="name_asc"
@selected(request('sort') === 'name_asc')>
Tên A → Z
</option>
<option value="name_desc"
@selected(request('sort') === 'name_desc')>
Tên Z → A
</option>
</select>13. Xử lý Sort
Controller:
switch (request('sort')) {
case 'oldest':
$query->oldest();
break;
case 'name_asc':
$query->orderBy('name', 'asc');
break;
case 'name_desc':
$query->orderBy('name', 'desc');
break;
default:
$query->latest();
break;
}Sau đó:
$users = $query
->paginate(10)
->withQueryString();14. Hoàn chỉnh UserController@index
Đến đây chúng ta có:
Search
Filter
Sort
Pagination
Code hoàn chỉnh:
public function index()
{
$query = User::query();
/*
|--------------------------------------------------------------------------
| SEARCH
|--------------------------------------------------------------------------
*/
if ($search = request('search')) {
$query->where(function ($q) use ($search) {
$q->where('name', 'like', "%{$search}%")
->orWhere('email', 'like', "%{$search}%");
});
}
/*
|--------------------------------------------------------------------------
| FILTER ROLE
|--------------------------------------------------------------------------
*/
if ($role = request('role')) {
$query->where('role', $role);
}
/*
|--------------------------------------------------------------------------
| SORT
|--------------------------------------------------------------------------
*/
switch (request('sort')) {
case 'oldest':
$query->oldest();
break;
case 'name_asc':
$query->orderBy('name', 'asc');
break;
case 'name_desc':
$query->orderBy('name', 'desc');
break;
default:
$query->latest();
break;
}
/*
|--------------------------------------------------------------------------
| PAGINATION
|--------------------------------------------------------------------------
*/
$users = $query
->paginate(10)
->withQueryString();
return view('users.index', compact('users'));
}Đây chính là cấu trúc mà chúng ta sẽ thường xuyên sử dụng trong các trang quản trị Laravel.
15. Gộp Search + Filter + Sort thành một Form
Blade:
<form
method="GET"
action="{{ route('users.index') }}"
class="flex gap-2 mb-4"
>
{{-- SEARCH --}}
<input
type="text"
name="search"
value="{{ request('search') }}"
placeholder="Tìm tên hoặc email..."
>
{{-- ROLE --}}
<select name="role">
<option value="">
Tất cả Role
</option>
<option
value="admin"
@selected(request('role') === 'admin')
>
Admin
</option>
<option
value="editor"
@selected(request('role') === 'editor')
>
Editor
</option>
<option
value="user"
@selected(request('role') === 'user')
>
User
</option>
</select>
{{-- SORT --}}
<select name="sort">
<option
value="latest"
@selected(request('sort', 'latest') === 'latest')
>
Mới nhất
</option>
<option
value="oldest"
@selected(request('sort') === 'oldest')
>
Cũ nhất
</option>
<option
value="name_asc"
@selected(request('sort') === 'name_asc')
>
Tên A → Z
</option>
<option
value="name_desc"
@selected(request('sort') === 'name_desc')
>
Tên Z → A
</option>
</select>
<button type="submit">
🔎 Tìm kiếm
</button>
</form>Kết quả:
┌──────────────────────────────────────────────────────────┐
│ 🔎 Nguyễn [Admin ▼] [Mới nhất ▼] [ Tìm kiếm ] │
└──────────────────────────────────────────────────────────┘16. Nút Reset
Nên có thêm:
<a href="{{ route('users.index') }}">
Xóa bộ lọc
</a>Khi click:
/userstoàn bộ:
search
role
sortđược xóa.
17. Pagination với Tailwind
Nếu dự án đang sử dụng Tailwind:
{{ $users->links() }}Laravel có thể render pagination view tương ứng với frontend stack đã cấu hình.
Nếu cần tùy chỉnh giao diện, Laravel cho phép publish pagination views để chỉnh sửa.
18. Vấn đề của Pagination truyền thống
Cách trên hoàn toàn đủ cho nhiều dự án:
Search
+
Filter
+
Sort
+
PaginationNhưng khi làm Admin Dashboard, người dùng thường muốn giao diện giống:
┌───────────────────────────────────────────────────────────┐
│ Users [+ Add User] │
├───────────────────────────────────────────────────────────┤
│ Search [____________] Role [____] │
├────┬──────────────┬────────────────┬─────────┬───────────┤
│ ID │ Name │ Email │ Role │ Action │
├────┼──────────────┼────────────────┼─────────┼───────────┤
│ 1 │ Nguyễn Văn A │ a@gmail.com │ Admin │ Edit │
│ 2 │ Trần Văn B │ b@gmail.com │ User │ Edit │
│ 3 │ Lê Văn C │ c@gmail.com │ Editor │ Edit │
└────┴──────────────┴────────────────┴─────────┴───────────┘
Showing 1 to 10 of 245 users
1 2 3 4 5 NextĐây là lúc DataTables trở nên hữu ích.
19. DataTables là gì?
DataTables.net là thư viện JavaScript dùng để biến HTML <table> thành một bảng dữ liệu tương tác.
Nó cung cấp nhiều chức năng:
Search
Sort
Pagination
Ajax
Responsive
Column ordering
Filtering
Server-side processingĐặc biệt:
serverSide: truecho phép DataTables gửi yêu cầu lên server mỗi khi người dùng:
chuyển trang
tìm kiếm
sort
filter
DataTables sẽ gửi request tới server và server trả JSON về cho bảng. (DataTables Plus)
20. DataTables Client-side và Server-side
Có hai cách sử dụng.
Client-side
MySQL
↓
Laravel
↓
10.000 records
↓
Browser
↓
DataTablesBrowser nhận toàn bộ:
10.000 recordssau đó DataTables xử lý.
Phù hợp với:
100
500
1.000bản ghi tùy trường hợp.
Server-side
Browser
↓
DataTables
↓
Laravel
↓
MySQLBrowser chỉ nhận dữ liệu cần thiết cho trang hiện tại.
Ví dụ:
Database:
1.000.000 UsersNhưng trang hiện tại chỉ cần:
10 Usersthì server xử lý:
Search
Sort
Filter
Paginationrồi trả về 10 records.
Đây là cách phù hợp hơn với dữ liệu rất lớn. DataTables cũng mô tả server-side processing là cách để database thực hiện phần tìm kiếm, ordering và paging cho các tập dữ liệu lớn. (DataTables Plus)
21. Cài DataTables
Có thể sử dụng CDN.
Trong layout:
<link
rel="stylesheet"
href="https://cdn.datatables.net/2.3.4/css/dataTables.dataTables.min.css"
>
<script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
<script src="https://cdn.datatables.net/2.3.4/js/dataTables.min.js"></script>Lưu ý: Khi triển khai dự án thực tế, nên kiểm tra phiên bản DataTables hiện tại và cố định version phù hợp thay vì phụ thuộc vào một URL không version.
22. HTML Table
Blade:
<table id="users-table" class="display w-full">
<thead>
<tr>
<th>ID</th>
<th>Name</th>
<th>Email</th>
<th>Role</th>
<th>Created</th>
</tr>
</thead>
<tbody>
</tbody>
</table>Chúng ta không cần:
@foreachvì dữ liệu sẽ được DataTables lấy bằng AJAX.
23. Khởi tạo DataTables
JavaScript:
$(document).ready(function () {
$('#users-table').DataTable({
ajax: '{{ route('users.index') }}',
columns: [
{
data: 'id'
},
{
data: 'name'
},
{
data: 'email'
},
{
data: 'role'
},
{
data: 'created_at'
}
]
});
});DataTables sẽ gửi request:
/usersvà mong server trả JSON.
24. Laravel trả JSON
Trong Controller:
public function index(Request $request)
{
if ($request->ajax()) {
$users = User::latest()->get();
return response()->json([
'data' => $users
]);
}
return view('users.index');
}Khi AJAX request:
/usersLaravel trả:
{
"data": [
{
"id": 1,
"name": "Nguyễn Văn A",
"email": "a@gmail.com",
"role": "admin"
},
{
"id": 2,
"name": "Trần Văn B",
"email": "b@gmail.com",
"role": "user"
}
]
}DataTables đọc:
data: 'id'
data: 'name'
data: 'email'và đưa dữ liệu vào bảng.
25. Nhưng có một vấn đề
Đoạn này:
User::latest()->get();sẽ lấy toàn bộ User.
Nếu database có:
500.000 Usersthì:
500.000 Users
↓
Laravel
↓
JSON
↓
Browserlà điều không nên làm.
Đây chính là lúc chúng ta cần:
Server-side DataTables
26. Server-side DataTables
JavaScript:
$('#users-table').DataTable({
processing: true,
serverSide: true,
ajax: '{{ route('users.index') }}',
columns: [
{
data: 'id'
},
{
data: 'name'
},
{
data: 'email'
},
{
data: 'role'
},
{
data: 'created_at'
}
]
});Hai dòng quan trọng:
processing: true,
serverSide: true,DataTables sẽ gửi các thông tin cần thiết cho server để server thực hiện processing. (DataTables Plus)
27. DataTables gửi gì lên Laravel?
Ví dụ người dùng đang ở:
Trang 3DataTables có thể gửi các thông tin liên quan đến:
start
length
search
order
columns
drawVí dụ ý tưởng:
start = 20
length = 10
search = nguyenCó nghĩa:
Bắt đầu từ record 20
Lấy 10 record
Search "nguyen"Server xử lý:
MySQL
↓
WHERE
ORDER BY
LIMIT
OFFSETrồi trả kết quả.
28. Laravel xử lý Server-side
Ví dụ đơn giản:
public function index(Request $request)
{
if ($request->ajax()) {
$query = User::query();
/*
|--------------------------------------------------------------------------
| SEARCH
|--------------------------------------------------------------------------
*/
if ($request->search['value']) {
$search = $request->search['value'];
$query->where(function ($q) use ($search) {
$q->where('name', 'like', "%{$search}%")
->orWhere('email', 'like', "%{$search}%");
});
}
/*
|--------------------------------------------------------------------------
| TOTAL
|--------------------------------------------------------------------------
*/
$recordsTotal = User::count();
$recordsFiltered = $query->count();
/*
|--------------------------------------------------------------------------
| ORDER
|--------------------------------------------------------------------------
*/
$query->orderBy('id', 'desc');
/*
|--------------------------------------------------------------------------
| PAGINATION
|--------------------------------------------------------------------------
*/
$users = $query
->skip($request->start)
->take($request->length)
->get();
/*
|--------------------------------------------------------------------------
| RESPONSE
|--------------------------------------------------------------------------
*/
return response()->json([
'draw' => intval($request->draw),
'recordsTotal' => $recordsTotal,
'recordsFiltered' => $recordsFiltered,
'data' => $users,
]);
}
return view('users.index');
}Đây là phiên bản tự xử lý protocol của DataTables, rất tốt để người học hiểu bản chất.
29. Cấu trúc Response
DataTables cần các thông tin chính:
{
"draw": 1,
"recordsTotal": 500,
"recordsFiltered": 25,
"data": []
}Ý nghĩa:
draw
Đánh dấu request hiện tại.
recordsTotal
Tổng số User trước khi filter:
500recordsFiltered
Tổng số User sau filter:
25data
Dữ liệu của trang hiện tại:
10 Users30. Sort từ DataTables
Bây giờ người dùng click:
Name ↕DataTables gửi thông tin column/order.
Ví dụ:
order[0][column] = 1
order[0][dir] = ascÝ nghĩa:
Column 1
↓
ASCChúng ta có thể xử lý:
$columns = [
'id',
'name',
'email',
'role',
'created_at',
];
$orderColumn = $columns[$request->order[0]['column']] ?? 'id';
$orderDirection = $request->order[0]['dir'] ?? 'desc';
$query->orderBy(
$orderColumn,
$orderDirection
);31. Không nên cho phép Sort tùy ý
Không nên viết:
$query->orderBy(
$request->order[0]['column'],
$request->order[0]['dir']
);vì giá trị từ browser là input không đáng tin cậy.
Thay vào đó:
$columns = [
'id',
'name',
'email',
'role',
'created_at',
];sau đó map:
$orderColumn = $columns[$index] ?? 'id';Đây là một nguyên tắc rất quan trọng:
Không đưa trực tiếp tên column từ request của người dùng vào câu SQL nếu không kiểm soát whitelist.
32. Search + Sort + Pagination
Đến đây DataTables đã có:
┌──────────────┐
│ DataTables │
└──────┬───────┘
│
┌─────────┼─────────┐
↓ ↓ ↓
Search Sort Paging
│ │ │
└─────────┼─────────┘
↓
Laravel
↓
MySQLĐây là kiến trúc rất phổ biến trong Admin Dashboard.
33. Khi nào dùng Laravel Pagination?
Dùng:
paginate()khi:
Website đơn giản
Blog
Danh sách bài viết
Danh mục
Trang frontend
Không cần Ajax
Muốn SEO tốt
Muốn code đơn giản
Ví dụ:
$posts = Post::latest()->paginate(12);34. Khi nào dùng DataTables?
DataTables rất phù hợp với:
Admin DashboardVí dụ:
Users
Posts
Orders
Products
Invoices
Customers
Transactions
LogsĐặc biệt khi cần:
Search
Sort
Filter
Pagination
Ajaxtrong cùng một bảng.
35. Khi nào dùng Server-side DataTables?
Khi dữ liệu lớn.
Ví dụ:
1.000 recordsCó thể client-side vẫn ổn tùy trường hợp.
Nhưng:
100.000
500.000
1.000.000+thì server-side processing thường hợp lý hơn.
DataTables cũng khuyến nghị server-side processing cho các tập dữ liệu rất lớn, vì database có thể thực hiện các tác vụ filtering, ordering và paging hiệu quả hơn phía browser. (DataTables Plus)
36. Một lỗi rất hay gặp
Không nên viết:
$users = User::all();rồi:
collect($users)
->filter(...)
->sort(...)
->paginate(...);Đối với dữ liệu lớn, đây là cách không tốt.
Vì:
MySQL
↓
Tất cả records
↓
PHP RAM
↓
Filter
↓
Sort
↓
PaginationThay vào đó:
MySQL
↓
WHERE
ORDER BY
LIMIT
↓
Laravel
↓
BrowserDatabase phải làm phần lớn công việc.
37. Tư duy Query Builder quan trọng
Thay vì:
$users = User::all();hãy hình thành thói quen:
$query = User::query();Sau đó:
if (...) {
$query->where(...);
}
if (...) {
$query->orderBy(...);
}
$users = $query->paginate(10);Đây là tư duy rất quan trọng khi xây dựng Admin CMS.
38. Tách Filter thành Method
Khi Controller bắt đầu dài, chúng ta có thể đưa logic filter vào Model.
Ví dụ:
class User extends Authenticatable
{
public function scopeSearch($query, $search)
{
return $query->where(function ($q) use ($search) {
$q->where('name', 'like', "%{$search}%")
->orWhere('email', 'like', "%{$search}%");
});
}
}Controller:
$query = User::query();
if ($search = request('search')) {
$query->search($search);
}Code Controller trở nên sạch hơn.
Phần Scope sẽ được học sâu hơn ở:
📘 Bài 32 — Local Scope & Global Scope
39. Tách Filter thành Service
Trong dự án lớn hơn:
Controller
↓
Service
↓
Repository / Query
↓
Model
↓
DatabaseVí dụ:
UserController
↓
UserService
↓
User Query
↓
MySQLNhưng ở giai đoạn hiện tại không nên làm quá phức tạp.
Người mới học Laravel chỉ cần nắm chắc:
User::query()→
where()→
orderBy()→
paginate()là đã rất quan trọng.
40. Validation cho Search
Search cũng là dữ liệu từ người dùng.
Laravel 13 cung cấp nhiều cách validation request, từ $request->validate() đến Form Request cho các trường hợp phức tạp hơn. (Laravel)
Ví dụ:
$request->validate([
'search' => ['nullable', 'string', 'max:100'],
'role' => ['nullable', 'in:admin,editor,user'],
]);Không nhất thiết mọi ô search đều phải validation phức tạp, nhưng đây là thói quen tốt.
41. Một Controller thực tế hơn
Ví dụ:
public function index(Request $request)
{
$request->validate([
'search' => ['nullable', 'string', 'max:100'],
'role' => ['nullable', 'in:admin,editor,user'],
'sort' => [
'nullable',
'in:latest,oldest,name_asc,name_desc'
],
]);
$query = User::query();
// SEARCH
if ($search = $request->search) {
$query->where(function ($q) use ($search) {
$q->where('name', 'like', "%{$search}%")
->orWhere('email', 'like', "%{$search}%");
});
}
// FILTER
if ($request->role) {
$query->where('role', $request->role);
}
// SORT
match ($request->sort) {
'oldest' => $query->oldest(),
'name_asc' => $query->orderBy('name'),
'name_desc' => $query->orderByDesc('name'),
default => $query->latest(),
};
// PAGINATION
$users = $query
->paginate(10)
->withQueryString();
return view('users.index', compact('users'));
}Đây là phiên bản khá tốt cho một CRUD Admin cơ bản.
42. Tổng kết kiến trúc
Sau bài này, chúng ta đã đi từ:
User::all();đến:
User::query()và xây dựng:
USERS
│
↓
User::query()
│
┌──────────┼──────────┐
↓ ↓ ↓
Search Filter Sort
│ │ │
└──────────┼──────────┘
↓
Pagination
│
↓
BladeHoặc với DataTables:
Browser
│
↓
DataTables
│
AJAX
↓
Laravel
│
↓
Eloquent
│
↓
MySQL
│
↓
JSON
│
↓
DataTables43. Bài tập thực hành
Bài tập 1
Tạo trang:
/userscó:
Search
PaginationBài tập 2
Thêm:
Filter Rolevới:
All
Admin
Editor
UserBài tập 3
Thêm:
Sortgồm:
Mới nhất
Cũ nhất
Tên A → Z
Tên Z → ABài tập 4
Kết hợp:
Search
+
Filter
+
Sort
+
Paginationvà đảm bảo khi chuyển trang các query vẫn được giữ lại.
Bài tập 5
Cài:
DataTablesvà hiển thị User bằng AJAX.
Bài tập 6 — Nâng cao
Chuyển DataTables sang:
serverSide: truevà để Laravel xử lý:
Search
Sort
Paginationthay vì tải toàn bộ User về browser.
🎯 Kết quả sau Bài 29
Người học đã có thể xây dựng một bảng Admin hoàn chỉnh:
┌─────────────────────────────────────────────────────────────┐
│ 👥 USERS [+ Thêm User] │
├─────────────────────────────────────────────────────────────┤
│ │
│ 🔎 [ Tìm tên/email ] │
│ │
│ Role: [ Tất cả ▼ ] Sort: [ Mới nhất ▼ ] │
│ │
├────┬────────────────┬──────────────────┬─────────┬─────────┤
│ ID │ Họ tên │ Email │ Role │ Action │
├────┼────────────────┼──────────────────┼─────────┼─────────┤
│ 1 │ Nguyễn Văn A │ a@gmail.com │ Admin │ ✏️ 🗑️ │
│ 2 │ Trần Văn B │ b@gmail.com │ User │ ✏️ 🗑️ │
│ 3 │ Lê Văn C │ c@gmail.com │ Editor │ ✏️ 🗑️ │
└────┴────────────────┴──────────────────┴─────────┴─────────┘
1 2 3 4 5 Next →Đây là bước chuyển rất quan trọng từ CRUD cơ bản sang Admin Dashboard thực tế.
Ghi nhớ công thức:
$query = Model::query();
$query->where(...);
$query->orderBy(...);
$query->paginate(...);Và khi dữ liệu lớn:
DataTables
+
AJAX
+
serverSide
+
Laravel
+
MySQLsẽ trở thành một kiến trúc rất hữu ích cho các bảng quản trị lớn. (DataTables Plus)
Bài tiếp theo — Bài 30: Soft Delete sẽ xây dựng hệ thống Thùng rác (Trash) với SoftDeletes, restore() và forceDelete(), sau đó áp dụng trực tiếp vào CRUD Users/Posts.




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