Các form search dữ liệu là chức năng thông thường của website. Nhưng dường như để làm nó một cách rõ ràng, dễ dàng mở rộng và tái sử dụng thì không đơn giản. Trong bài viết này, mình sẽ hướng dẫn query filter cho laravel. Bạn sẽ thấy rằng, nó không khó quá đâu.
Cách làm truyền thống
Phía trên là code với cách làm thông thường, rất dễ để viết, người đọc cũng không khó khăn để hiểu. Nhưng nhược điểm của cách này rất nhiều: khó kiểm soát nếu có nhiều field, khó tái sử dụng, phải lặp đi lặp lại việc kiểm tra điều kiện...
Sử dụng query scope
Chúng ta sẽ thêm các scopes vào model User:
Khi đó việc filter đã trở nên dễ nhìn hơn (dù chưa clean)
Giả sử chúng ta muốn filter thêm field gender thì sao? Tất nhiên là thêm hàm scope vào Model và thêm vào câu truy vấn phải không ạ.
Tạo hàm scope filter
Để tránh việc mỗi lần filter một field mới là phải đi tìm và thêm code ở khắp nơi, ta sẽ viết một hàm scope filter chịu trách nhiệm gọi các hàm filter field một cách tự động tùy vào tham số truyền vào. Chúng ta sẽ sửa lại trong controller như sau:
Hàm filter không nên khai báo nhiều lần, nó chỉ là hàm "trung chuyển" gọi các hàm filter của mỗi model. Ta sẽ triển khai nó bằng trait để các model có thể dùng chung.
Tiếp theo trong Model User ta sẽ đổi tên các hàm scopeName, scopeStatus... thành filterName, filterStatus... cũng như thay đổi chút nội dung.
Và use Filterable vào
Đến đây có thể gọi là tạm đủ cho chức năng filter rồi ạ, nhưng chúng ta vẫn có thể tối ưu được thêm nữa.
Refactor những đoạn code lặp lại nhiều lần
Nếu nhìn lại các hàm filter field ở Model User, ta sẽ thấy
Các hàm có điểm chung là chỉ "where" field bằng giá trị truyền vào. Ý tưởng là hàm filter sẽ thực hiện query tự động theo field luôn, không cần khai báo hàm filter ở model nữa. Nhưng model lại cần có cái gì đó để hàm filter nhận biết những field đó.
Tạo biến $filterable ở Model User:
Bổ sung thêm code cho hàm filter:
Cuối cùng chúng ta sẽ làm cho hàm filter nhìn ổn hơn
Kết luận
Như vậy là chúng ta đã triển khai xong một "cơ chế" filter đơn giản. Từ bây giờ, nếu muốn filter cho một model nào, chỉ cần use Filterable, các field đơn giản thì khai báo trong biến $filterable, với các logic phức tạp hơn thì tạo các hàm filter{$field}() tương ứng. Sau đó chỉ cần đơn giản gọi hàm scope filter($param) là xong ạ.