- vừa được xem lúc

Cách query filter đơn giản hơn trong Laravel.

0 0 52

Người đăng: quycoi

Theo Viblo Asia

Trong một dự án thực tế việc chúng ta phải query filter cho các phần search data cho một website là một chức năng thông thường. Vậy làm sao để xây dựng filter một cách hiệu quả dễ mở rộng, thay đổi, tái sử dụng. Trong bài viết này mình sẽ hướng dẫn các bạn query filter trong laravel và nó không thật sự quá khó đâu.

1. Cách làm thông thường

public function index(Request $request)
{ $user = User::query(); if ($request->has('name')) { $user->where('name', 'LIKE', '%' . $request->name . '%'); } if ($request->has('status')) { $user->where('status', $request->status); } if ($request->has('birthday')) { $user->whereDate('birthday', $request->birthday); } return $user->get();
}

Đây là cách chúng ta thường hay sử dụng, rất dễ viết tuy nhiên có một nhược điểm là sẽ khó kiểm soát nếu cần search nhiều field, phải lặp đi lặp lại điều kiện if nhiều lần và không tái sử dụng được. Ở bài viết trước mình có giới thiệu về scope, nó có thể được sử dụng để giải quyết vấn đề không tái sử dụng được. Cách viết sẽ như thế này:

public function scopeName($query, $request)
{ if ($request->has('name')) { $query->where('name', 'LIKE', '%' . $request->name . '%'); } return $query;
} public function scopeStatus($query, $request)
{ if ($request->has('status')) { $query->where('status', $request->status); } return $query;
} public function scopeBirthday($query, $request)
{ if ($request->has('birthday')) { $query->whereDate('birthday', $request->birthday); } return $query;
}

Sau đó chúng ta sẽ gọi như sau:

public function index(Request $request)
{ $user = User::query() ->name($request) ->status($request) ->birthday($request); return $user->get();
}

Nhìn nó sẽ clear hơn, và chúng ta có thể sử dụng lại được ở những trường hợp khác khi cần filter theo 1 field nào đó.

2. Xây dựng class filter

Để tránh việc mỗi lần cần filter thêm một trường ta lại phải thêm một scope function trong model. Ta sẽ đi xây dựng một hàm filter, ý tưởng ở đây sẽ như thế này:

public function index(Request $request)
{ $user = User::filter($request); return $user->get();
}

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. Tôi sẽ tạo một trait Filterable:

<?php namespace App\Traits; use App\Filters\QueryFilter;
use Illuminate\Database\Eloquent\Builder; trait Filterable
{ public function scopeFilter(Builder $builder, QueryFilter $filters, array $filterFields = ['*'], array $orderFields = []) { return $filters->apply($builder, $filterFields, $orderFields); }
} 

Trait này sẽ được gọi ở mỗi model. Và tạo thêm một class base filter ở đây tôi đặt tên là QueryFilter:

<?php namespace App\Filters; use App\Helpers\Common\StringHelper;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Http\Request;
use Illuminate\Support\Str; class QueryFilter
{ /** * @var Request */ public $request; /** * @var array */ protected $filters; /** * @var array */ protected $search = []; /** * @var $builder */ protected $builder; /** * @var string|null */ protected $orderField = null; /** * @var string */ protected $orderType = 'desc'; /** * @var $filterable */ protected $filterable; /** * QueryFilter constructor. * @param Request $request */ public function __construct(Request $request) { $this->request = $request; $this->filters = $this->request->all(); } /** * @param Builder $builder * @param array $filterFields * @param array $orderFields * @return Builder */ public function apply(Builder $builder, array $filterFields, array $orderFields = []) { $this->builder = $builder; $this->orderFields = $orderFields; foreach ($this->filters as $name => $value) { $method = 'filter' . Str::studly($name); if (is_null($value) || $value == '') { continue; } if (method_exists($this, $method)) { $this->{$method}($value); continue; } if (empty($this->filterable) || !is_array($this->filterable)) { continue; } if (in_array($name, $this->filterable)) { $this->builder->where($name, $value); continue; } if (key_exists($name, $this->filterable)) { $this->builder->where($this->filterable[$name], $value); continue; } } return $this->builder; }
} 

Sau đó tôi có thể tạo một class filter riêng cho model user ví dụ như sau:

<?php namespace App\Filters; class UserFilter extends QueryFilter
{ protected $filterable = [ 'id', 'author_id', 'birth_day', 'gender', ]; public function filterName($name) { return $this->builder->where('name', 'like', '%' . $name . '%'); }
} 

Class UserFilter này được extends vs class QueryFilter. Tôi sẽ giải thích luồng xử lý từ lúc gọi function filter đến lúc làm sao mỗi class filter này sẽ được áp dụng với các model. Đầu tiên ở model user chúng ta use trait Filterable. Khi đó ở model user sẽ call function scopeFilter ở trait, có nghĩa là mỗi khi chúng ta gọi đến User::filter() thì Laravel sẽ hiểu được là các bạn đang gọi đến 1 local scope tương tự như các bạn gọi từng scope chúng ta tạo trong model như ví dụ trên. Mỗi lần ta gọi đến hàm filter và chuyền vào một instance của class UserFilter

User::filter($userFilter)

Khi đó hàm scopeFilter trong trait chúng ta khai báo sẽ được gọi và gọi đến function apply, tôi sẽ giải thích logic trong hàm này để các bạn dễ hiểu hơn. Đầu tiên thuộc tính $filters đã được gán value là một mảng của request người dùng gửi lên, value này là các giá trị người dùng gửi lên từ phía client để filter theo mong muốn của họ value có thể được gửi bằng form data or json data. Ở class UserFilter tôi đã khai báo thêm một biến $filterable là một array sẽ chứa các trường tôi mong muốn filter theo điều kiện where('fieldName', value), và khai báo function bắt đầu name là filter theo sau đó là name của fieldname camel case được gửi từ client. Ở mỗi vòng lặp tôi sẽ build query dựa vào value được gửi lên và dựa theo function và các field name được khai báo trong biến thuộc tính filterable. Khi tôi gọi đến scope filter và truyền vào một instance của class user function này sẽ build và return ra câu query có instance là Builder sau đó ở cuối câu query tôi chỉ cần gọi tới function get() để lấy ra data mong muốn được filter.

User::filter($userFilter)->get();

Cách làm này tôi học được khi làm dự án thực tế. Nếu có chỗ nào hiểu sai mong các bạn góp ý chứ đừng ném gạch nhé =))

Bình luận

Bài viết tương tự

- vừa được xem lúc

Tìm hiểu về Resource Controller trong Laravel

Giới thiệu. Trong laravel, việc sử dụng các route post, get, group để gọi đến 1 action của Controller đã là quá quen đối với các bạn sử dụng framework này.

0 0 358

- vừa được xem lúc

Phân quyền đơn giản với package Laravel permission

Như các bạn đã biết, phân quyền trong một ứng dụng là một phần không thể thiếu trong việc phát triển phần mềm, dù đó là ứng dụng web hay là mobile. Vậy nên, hôm nay mình sẽ giới thiệu một package có thể giúp các bạn phân quyền nhanh và đơn giản trong một website được viết bằng PHP với framework là L

0 0 449

- vừa được xem lúc

Sử dụng Swagger để xây dựng API documentation

Giới thiệu về Swagger. RESTful API là một tiêu chuẩn dùng trong việc thiết kế API cho các ứng dụng web (thiết kế Web services) để tiện cho việc quản lý các resource.

0 0 1k

- vừa được xem lúc

Ví dụ CRUD với Laravel và Vuejs.

1. Cài đặt Laravel. composer create-project --prefer-dist laravel/laravel vuelaravelcrud. .

0 0 160

- vừa được xem lúc

Một số tips khi dùng laravel (Part 1)

1. Show database query in raw SQL format. DB::enableQueryLog(); // Bật tính năng query logging. DB::table('users')->get(); // Chạy truy vấn bạn muốn ghi log.

0 0 84

- vừa được xem lúc

Inertiajs - Xây dựng Single Page App không cần API

Tiêu đề là mình lấy từ trang chủ của https://inertiajs.com/ chứ không phải mình tự nghĩ ra đâu nhé :v. Lâu lâu rồi chưa động tới Laravel (dự án cuối cùng mình code là ở ver 5.8), thế nên một ngày đẹp trời lượn vào đọc docs ver 8.

0 0 242