当前位置:网站首页>使用多个可选过滤器过滤 Eloquent 模型
使用多个可选过滤器过滤 Eloquent 模型
2022-04-22 00:34:00 【rorg】
wpcmf: wpcmf cms ,内容管理系统,类似 wordpress 系统
在显示到视图时,我们经常需要过滤 eloquent 模型。如果我们有少量过滤器,这可能很好,但如果您需要添加多个过滤器,则控制器可能会变得混乱且难以阅读。
在处理可以结合使用的多个可选过滤器时尤其如此。
但是,有一些方法可以创建这些过滤器,甚至可以使它们可重复使用。在本文结束时,您将能够更好地处理项目中的复杂过滤选项。
定义问题
比方说,我有一个控制器方法,它返回我们商店中的所有产品,这可以用于 API,将其传递给刀片模板,或者无论如何,控制器可能看起来像这样:
<?php
namespace App\Http\Controllers;
use App\Models\Product;
use Illuminate\Http\Request;
class ProductsController extends Controller
{
public function index()
{
return Product::all();
}
}
然而,这根本不现实。通常情况下,我们需要添加一些过滤,例如,假设我们只想获取给定类别中的产品,我们可以通过在查询字符串中发送类别 slug 然后使用 slug 进行过滤来做到这一点。所以我们的新控制器看起来像这样:
class ProductsController extends Controller
{
public function index()
{
if ($request->filled('category')) {
$categorySlug = $request->category;
return Product::whereHas('category', function ($query) use ($categorySlug) {
$query->where('slug', $categorySlug);
});
}
return Product::all();
}
}
如果我们只需要一个过滤器,这很好。但是,看看如果我们只引入另外两个过滤选项会发生什么:
class ProductsController extends Controller
{
public function index(Request $request)
{
$query = Product::query();
if ($request->filled('price')) {
list($min, $max) = explode(",", $request->price);
$query->where('price', '>=', $min)
->where('price', '<=', $max);
}
if ($request->filled('category')) {
$categorySlug = $request->category;
$query->whereHas('category', function ($query) use ($categorySlug) {
$query->where('slug', $categorySlug);
});
}
if ($request->filled('brand')) {
$brandSlug = $request->brand;
$query->whereHas('brand', function ($query) use ($brandSlug) {
$query->where('slug', $brandSlug);
});
}
return $query->get();
}
}
我们又推出了两种过滤器,一种用于品牌 slug,一种用于价格范围。在我看来,这太忙了,很难跟踪这里发生的事情,如果我们需要添加更多过滤选项,情况会很快变得更糟。
例如,当您在 eBay 上搜索产品时,您通常会获得十多个可选过滤器。我们需要寻找另一种方法来做到这一点。
想像
如果不是在我们的控制器中包含所有这些过滤器,我们可以做这样的事情怎么办:
class ProductsController extends Controller
{
public function index(ProductFilters $filters)
{
return Product::filter($filters)->get();
}
}
在这里,我们收到一个ProductFilters可能包含所有过滤器的类,然后我们可以将它们应用于名为 的查询范围filter。这使得我们的控制器非常纤薄,很容易猜到它发生了什么。我们正在筛选产品,如果我们需要了解更多细节,我们可以查看ProductFilters类。
实施新方法
首先,让我们将作用域添加到我们的模型中:
class Product extends Model
{
use HasFactory;
public function category()
{
return $this->belongsTo(Category::class);
}
public function brand()
{
return $this->belongsTo(Brand::class);
}
// This is the scope we added
public function scopeFilter($query, $filters)
{
return $filters->apply($query);
}
}
在这个范围内,我们接收一个QueryBuilder实例和ProductFilters我们从控制器传递下来的实例。apply然后我们在这个$filter实例上调用方法。
到目前为止,我们知道这个ProductFilters类看起来像这样:
namespace App\Filters;
class ProductFilters
{
public function apply($query)
{
if (request()->filled('price')) {
list($min, $max) = explode(",", $request->price);
$query->where('price', '>=', $min)
->where('price', '<=', $max);
}
if (request()->filled('category')) {
$categorySlug = $request->category;
$query->whereHas('category', function ($query) use ($categorySlug) {
$query->where('slug', $categorySlug);
});
}
if (request()->filled('brand')) {
$brandSlug = $request->brand;
$query->whereHas('brand', function ($query) use ($brandSlug) {
$query->where('slug', $brandSlug);
});
}
return $query->get();
}
}
这段代码可以工作,但并不比控制器中的过滤器好多少。而不是这样做,我想有单独的过滤器类,只包含它们的过滤逻辑。
让我们为类别过滤器执行此操作:
namespace App\Filters;
class CategoryFilter
{
function __invoke($query, $categorySlug)
{
return $query->whereHas('category', function ($query) use ($categorySlug) {
$query->where('slug', $categorySlug);
});
}
}
这里我们有一个自包含的类,我们甚至可以在其他模型中使用,假设我们有一个附加到商店的博客,我们可以对博客文章使用相同的过滤器。
顺便说一句,如果您不熟悉__invoke魔术方法,可以在这里找到更多信息:https ://www.php.net/manual/en/language.oop5.magic.php#object.invoke 。简而言之,它只是让我们像调用函数一样调用类的实例。
在为过滤器创建类之后,我们需要找到一种从ProductFilters类中调用它们的方法。有很多方法可以做到这一点,对于本文,我将采用以下方法:
namespace App\Filters;
class ProductFilters
{
protected $filters = [
'price' => PriceFilter::class,
'category' => CategoryFilter::class,
'brand' => BrandFilter::class,
];
public function apply($query)
{
foreach ($this->receivedFilters() as $name => $value) {
$filterInstance = new $this->filters[$name];
$query = $filterInstance($query, $value);
}
return $query;
}
public function receivedFilters()
{
return request()->only(array_keys($this->filters));
}
}
这是完成的课程,让我分解并解释每个部分。
这是如何运作的?
首先,让我们从$filters属性开始
protected $filters = [
'category' => CategoryFilter::class,
'price' => PriceFilter::class,
'brand' => BrandFilter::class,
];
在这里,我为产品定义了每个可选过滤器,数组的键是用于检查过滤器是否在请求中的键,数组的值是定义过滤器行为的类。这使我们可以轻松添加更多过滤器,当我们需要添加新过滤器时,我们只需创建新过滤器类并将该类添加到此数组中。
第二部分是这个方法:
public function receivedFilters()
{
return request()->only(array_keys($this->filters));
}
此方法用于查找请求中正在使用哪些过滤器,因此我们仅应用所需的过滤器。我们还调用该only方法并将$filters数组的键传递给它,以防止我们尝试调用不存在的过滤器类。假设有人发送过滤器“大小”,但我们目前不支持它,这将使请求密钥被忽略。
现在来说说这个类的肉,apply方法:
public function apply($query)
{
foreach ($this->receivedFilters() as $name => $value) {
$filterInstance = new $this->filters[$name];
$query = $filterInstance($query, $value);
}
return $query;
}
这个方法只是一个 for each 循环通过接收到的过滤器创建一个过滤器类,然后使用$query请求中接收到的值和值调用过滤器。
例如,假设我们收到一个如下所示的请求:
['category' => 'mobile-phones', 'price' => '100,150']
此类将首先创建一个实例,CategoryFilter然后将$query和“手机”传递给它。本质上,它会这样做:
$filterInstance = new CategoryFilter();
$query = $filterInstance($query, 'mobile-phones');
下一个同样的事情PhoneFilter:
$filterInstance = new PhoneFilter();
$query = $filterInstance($query, '100,150');
然后在应用所有查询之后,它将返回$query带有每个请求过滤器的对象。这就是我们在控制器中应用范围时收到的内容:
class ProductsController extends Controller
{
public function index(ProductFilters $filters)
{
return Product::filter($filters)->get();
}
}
结论
我们创建了一种可扩展的方式来为我们的产品制作多个过滤器,并且这里有一些我们可以改进的地方,例如为过滤器值添加验证或创建一个基类,ProductFilters以便我们可以将相同类型的过滤器添加到其他模型中。
版权声明
本文为[rorg]所创,转载请带上原文链接,感谢
https://blog.csdn.net/lemqs0123/article/details/124264577
边栏推荐
- Introduction to Haskell monoid
- 物聯網專業未來沒有前途了嗎?
- [knowledge atlas] tushare data acquisition and display (1 / *)
- 2022 superstar learning link: media creative economy, entering ASEAN, career promotion, music appreciation, emergencies and self rescue
- Ivorysql unveiled at postgresconf SV 2022 Silicon Valley Postgres Conference
- Basic use of jedis
- 观测云登陆阿里云计算巢,共建ISV新生态
- 基于eBPF技术的开源项目Kindling之探针架构介绍
- 终极套娃 2.0|云原生 PaaS 平台的可观测性实践分享
- Public testing, exclusive, penetration testing, picking up ragged tips
猜你喜欢

js原型,原型链,构造函数

Under the catalysis of the epidemic, why can this product promote the commercialization of automatic distribution?

Why does the web login need a verification code?

精彩联动!OpenMLDB Pulsar Connector原理和实操

Analysis and interpretation of the current situation and challenges faced by enterprise operation and maintenance in the digital era

Discrete mathematical propositional logic

Russian portal yandex open source YDB database

MySQL基础合集

想要加盟智能家居,你需要了解些什么?

. net treasure API: ihostedservice, background task execution
随机推荐
How to use the drawing board to draw the structure diagram easily?
2022 年 4 月中国数据库排行榜:春风拂面春意暖,分数回升四月天
Redis has three modes - master-slave replication, sentinel mode and cluster
MySQL进阶之表的增删改查
Build a personal blog (WordPress) on Alibaba cloud
短视频APP相关推荐资源位的高扩展高可用工程实践
JS array object de duplication
2022 superstar learning link: media creative economy, entering ASEAN, career promotion, music appreciation, emergencies and self rescue
If the garment industry wants to survive the "epidemic disaster", why does winner fashion become a growth sample?
Probe architecture of open source project kindling based on ebpf Technology
诚邀报名丨首期OpenHarmony开发者成长计划分享日
muduo项目介绍
MySQL basic collection
Mobile Internet development major, an interview experience about JVM
RPCX源码学习-server端
[langage C] opération de fichier d'analyse approfondie [niveau avancé examen spécial]
3D 沙盒游戏之人物的点击行走移动
Prometheus 的使用
Logstash import movie lens test data
解决Svelte项目使用pnpm后Prettier无效问题