iis服务器助手广告广告
返回顶部
首页 > 资讯 > 精选 >如何实现一个Laravel查询过滤器
  • 489
分享到

如何实现一个Laravel查询过滤器

2023-07-05 00:07:17 489人浏览 八月长安
摘要

今天小编给大家分享一下如何实现一个Laravel查询过滤器的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。上下文在撰写本文时,

今天小编给大家分享一下如何实现一个Laravel查询过滤器的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。

上下文

在撰写本文时,我在 PHP 8.1 和 MySQL 8 上使用 Laravel 9。我相信技术栈不是一个大问题,这里我们主要关注构建一个查询过滤器系统。在本文中,我将演示为 users 表构建过滤器。

<?phpuse Illuminate\Database\Migrations\Migration;use Illuminate\Database\Schema\Blueprint;use Illuminate\Support\Facades\Schema;return new class extends Migration{        public function up()    {        Schema::create('users', function (Blueprint $table) {            $table->id();            $table->string('name');            $table->string('email')->unique();            $table->string('gender', 10)->nullable()->index();            $table->boolean('is_active')->default(true)->index();            $table->boolean('is_admin')->default(false)->index();            $table->timestamp('birthday')->nullable();            $table->timestamp('email_verified_at')->nullable();            $table->string('passWord');            $table->rememberToken();            $table->timestamps();        });    }        public function down()    {        Schema::dropIfExists('users');    }}

此外,我还使用 Laravel Telescope 轻松监控查询。

初始点

学习使用 Laravel 的第一天,我经常直接在控制器上调用过滤器。简单,没有魔法,容易理解,但是这种方式有问题:

  • 控制器中放置的大量逻辑导致控制器膨胀

  • 不能重复使用

  • 许多相同的工作重复

<?phpnamespace App\Http\Controllers;use App\Models\User;use Illuminate\Http\Request;class UserController extends Controller{    public function __invoke(Request $request)    {        // /users?name=ryder&email=hartman&gender=male&is_active=1&is_admin=0&birthday=2014-11-30        $query = User::query();        if ($request->has('name')) {            $query->where('name', 'like', "%{$request->input('name')}%");        }        if ($request->has('email')) {            $query->where('email', 'like', "%{$request->input('email')}%");        }        if ($request->has('gender')) {            $query->where('gender', $request->input('gender'));        }        if ($request->has('is_active')) {            $query->where('is_active', $request->input('is_active') ? 1 : 0);        }        if ($request->has('is_admin')) {            $query->where('is_admin', $request->input('is_admin') ? 1 : 0);        }        if ($request->has('birthday')) {            $query->whereDate('birthday', $request->input('birthday'));        }        return $query->paginate();        // select * from `users` where `name` like '%ryder%' and `email` like '%hartman%' and `gender` = 'male' and `is_active` = 1 and `is_admin` = 0 and date(`birthday`) = '2014-11-30' limit 15 offset 0    }}

使用 Local Scope

为了能够在过滤期间隐藏逻辑,让我们尝试使用 Laravel 的 Local Scope。将查询转换为 User 模型中的函数范围:

// User.phppublic function scopeName(Builder $query): Builder{    if (request()->has('name')) {        $query->where('name', 'like', "%" . request()->input('name') . "%");    }    return $query;}public function scopeEmail(Builder $query): Builder{    if (request()->has('email')) {        $query->where('email', 'like', "%" . request()->input('email') . "%");    }    return $query;}public function scopeGender(Builder $query): Builder{    if (request()->has('gender')) {        $query->where('gender', request()->input('gender'));    }    return $query;}public function scopeIsActive(Builder $query): Builder{    if (request()->has('is_active')) {        $query->where('is_active', request()->input('is_active') ? 1 : 0);    }    return $query;}public function scopeIsAdmin(Builder $query): Builder{    if (request()->has('is_admin')) {        $query->where('is_admin', request()->input('is_admin') ? 1 : 0);    }    return $query;}public function scopeBirthday(Builder $query): Builder{    if (request()->has('birthday')) {        $query->where('birthday', request()->input('birthday'));    }    return $query;}// UserController.phppublic function __invoke(Request $request){    // /users?name=john&email=desmond&gender=female&is_active=1&is_admin=0&birthday=2015-04-11     $query = User::query()             ->name()            ->email()            ->gender()            ->isActive()            ->isAdmin()            ->birthday();    return $query->paginate();    // select * from `users` where `name` like '%john%' and `email` like '%desmond%' and `gender` = 'female' and `is_active` = 1 and `is_admin` = 0 and `birthday` = '2015-04-11' limit 15 offset 0}

通过这种设置,我们将大部分数据库操作移到了模型类中,但是代码重复非常多。示例 2 的名称和电子邮件范围过滤器相同,性别生日和 is_active/is_admin 组相同。我们将对类似的查询功能进行分组。

// User.phppublic function scopeRelativeFilter(Builder $query, $inputName): Builder{    if (request()->has($inputName)) {        $query->where($inputName, 'like', "%" . request()->input($inputName) . "%");    }    return $query;}public function scopeExactFilter(Builder $query, $inputName): Builder{    if (request()->has($inputName)) {        $query->where($inputName, request()->input($inputName));    }    return $query;}public function scopeBooleanFilter(Builder $query, $inputName): Builder{    if (request()->has($inputName)) {        $query->where($inputName, request()->input($inputName) ? 1 : 0);    }    return $query;}// UserController.phppublic function __invoke(Request $request){    // /users?name=john&email=desmond&gender=female&is_active=1&is_admin=0&birthday=2015-04-11    $query = User::query()        ->relativeFilter('name')        ->relativeFilter('email')        ->exactFilter('gender')        ->booleanFilter('is_active')        ->booleanFilter('is_admin')        ->exactFilter('birthday');    return $query->paginate();    // select * from `users` where `name` like '%john%' and `email` like '%desmond%' and `gender` = 'female' and `is_active` = 1 and `is_admin` = 0 and `birthday` = '2015-04-11' limit 15 offset 0}

至此,我们已经对大部分重复项进行了分组。但是,删除 if 语句或将这些过滤器扩展到另一个模型有点困难。我们正在寻找一种方法来彻底解决这个问题。

使用管道设计模式

管道设计模式是一种设计模式,它提供了逐步构建和执行一系列操作的能力。 Laravel 有内置的 Pipeline 让我们可以很容易地在实际中应用这种设计模式,但由于某种原因,它没有在官方文档中列出。 Laravel 本身也将 Pipeline 应用于请求和响应之间的中间件。最基本的,要在 Laravel 中使用 Pipeline,我们可以这样使用

app(\Illuminate\Pipeline\Pipeline::class)    ->send($intialData)    ->through($pipes)    ->thenReturn(); // data with pipes applied

对于我们的问题,可以将初始查询 User:query() 传递给 pipeline,通过过滤器步骤,并返回应用过滤器的查询构建器。

// UserControllerpublic function __invoke(Request $request){    // /users?name=john&email=desmond&gender=female&is_active=1&is_admin=0&birthday=2015-04-11    $query = app(Pipeline::class)        ->send(User::query())        ->through([            // filters        ])        ->thenReturn();    return $query->paginate();

现在我们需要构建管道过滤器:

// File: app/Models/Pipes/RelativeFilter.php<?phpnamespace App\Models\Pipes;use Illuminate\Database\Eloquent\Builder;class RelativeFilter{    public function __construct(protected string $inputName)    {    }    public function handle(Builder $query, \Closure $next)    {        if (request()->has($this->inputName)) {            $query->where($this->inputName, 'like', "%" . request()->input($this->inputName) . "%");        }        return $next($query);    }}// File: app/Models/Pipes/ExactFilter.php<?phpnamespace App\Models\Pipes;use Illuminate\Database\Eloquent\Builder;class ExactFilter{    public function __construct(protected string $inputName)    {    }    public function handle(Builder $query, \Closure $next)    {        if (request()->has($this->inputName)) {            $query->where($this->inputName, request()->input($this->inputName));        }        return $next($query);    }}//File: app/Models/Pipes/BooleanFilter.php<?phpnamespace App\Models\Pipes;use Illuminate\Database\Eloquent\Builder;class BooleanFilter{    public function __construct(protected string $inputName)    {    }    public function handle(Builder $query, \Closure $next)    {        if (request()->has($this->inputName)) {            $query->where($this->inputName, request()->input($this->inputName) ? 1 : 0);        }        return $next($query);    }}// UserControllerpublic function __invoke(Request $request){    // /users?name=john&email=desmond&gender=female&is_active=1&is_admin=0&birthday=2015-04-11    $query = app(Pipeline::class)        ->send(User::query())        ->through([            new \App\Models\Pipes\RelativeFilter('name'),            new \App\Models\Pipes\RelativeFilter('email'),            new \App\Models\Pipes\ExactFilter('gender'),            new \App\Models\Pipes\BooleanFilter('is_active'),            new \App\Models\Pipes\BooleanFilter('is_admin'),            new \App\Models\Pipes\ExactFilter('birthday'),        ])        ->thenReturn();    return $query->paginate();    // select * from `users` where `name` like '%john%' and `email` like '%desmond%' and `gender` = 'female' and `is_active` = 1 and `is_admin` = 0 and `birthday` = '2015-04-11' limit 15 offset 0}

通过将每个查询逻辑移动到一个单独的类,我们解了使用 OOP 的定制可能性,包括多态、继承、封装、抽象。比如你在 pipeline 的 handle 函数中看到,只有 if 语句中的逻辑不同,我会通过创建抽象类 BaseFilter 的方式将其分离抽象出来

//File: app/Models/Pipes/BaseFilter.php<?phpnamespace App\Models\Pipes;use Illuminate\Database\Eloquent\Builder;abstract class BaseFilter{    public function __construct(protected string $inputName)    {    }    public function handle(Builder $query, \Closure $next)    {        if (request()->has($this->inputName)) {            $query = $this->apply($query);        }        return $next($query);    }    abstract protected function apply(Builder $query): Builder;}// BooleanFilterclass BooleanFilter extends BaseFilter{    protected function apply(Builder $query): Builder    {        return $query->where($this->inputName, request()->input($this->inputName) ? 1 : 0);    }}// ExactFilterclass ExactFilter extends BaseFilter{    protected function apply(Builder $query): Builder    {        return $query->where($this->inputName, request()->input($this->inputName));    }}// RelativeFilterclass RelativeFilter extends BaseFilter{    protected function apply(Builder $query): Builder    {        return $query->where($this->inputName, 'like', "%" . request()->input($this->inputName) . "%");    }}

现在我们的过滤器直观且高度可重用,易于实现甚至扩展,只需创建一个管道,扩展 BaseFilter 并声明函数 apply 即可应用到 Pipeline.中。

将 Local Scope 与 Pipeline 相结合

此时,我们将尝试在控制器上隐藏 Pipeline,通过在 Model 中创建一个调用 Pipeline 的作用域来使我们的代码更简洁。

// User.phppublic function scopeFilter(Builder $query){    $criteria = $this->filterCriteria();    return app(\Illuminate\Pipeline\Pipeline::class)        ->send($query)        ->through($criteria)        ->thenReturn();}public function filterCriteria(): array{    return [        new \App\Models\Pipes\RelativeFilter('name'),        new \App\Models\Pipes\RelativeFilter('email'),        new \App\Models\Pipes\ExactFilter('gender'),        new \App\Models\Pipes\BooleanFilter('is_active'),        new \App\Models\Pipes\BooleanFilter('is_admin'),        new \App\Models\Pipes\ExactFilter('birthday'),    ];}// UserController.phppublic function __invoke(Request $request){    // /users?name=john&email=desmond&gender=female&is_active=1&is_admin=0&birthday=2015-04-11    return User::query()        ->filter()        ->paginate()        ->appends($request->query()); // 将所有当前查询附加到分页链接中    // select * from `users` where `name` like '%john%' and `email` like '%desmond%' and `gender` = 'female' and `is_active` = 1 and `is_admin` = 0 and `birthday` = '2015-04-11' limit 15 offset 0}

用户现在可以从任何地方调用过滤器。但是其他模型也想实现过滤,我们将创建一个包含范围的 Trait,并在模型内部声明参与过滤过程的 Pipeline。

// User.phpuse App\Models\Concerns\Filterable;class User extends Authenticatable {        use Filterable;        protected function getFilters()        {            return [                new \App\Models\Pipes\RelativeFilter('name'),                new \App\Models\Pipes\RelativeFilter('email'),                new \App\Models\Pipes\ExactFilter('gender'),                new \App\Models\Pipes\BooleanFilter('is_active'),                new \App\Models\Pipes\BooleanFilter('is_admin'),                new \App\Models\Pipes\ExactFilter('birthday'),            ];        }        // 其余代码// File: app/Models/Concerns/Filterable.phpnamespace App\Models\Concerns;use Illuminate\Database\Eloquent\Builder;use Illuminate\Pipeline\Pipeline;trait Filterable{    public function scopeFilter(Builder $query)    {        $criteria = $this->filterCriteria();        return app(Pipeline::class)            ->send($query)            ->through($criteria)            ->thenReturn();    }    public function filterCriteria(): array    {        if (method_exists($this, 'getFilters')) {            return $this->getFilters();        }        return [];    }}

以上就是“如何实现一个Laravel查询过滤器”这篇文章的所有内容,感谢各位的阅读!相信大家阅读完这篇文章都有很大的收获,小编每天都会为大家更新不同的知识,如果还想学习更多的知识,请关注编程网精选频道。

--结束END--

本文标题: 如何实现一个Laravel查询过滤器

本文链接: https://www.lsjlt.com/news/348674.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • 如何实现一个Laravel查询过滤器
    今天小编给大家分享一下如何实现一个Laravel查询过滤器的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。上下文在撰写本文时,...
    99+
    2023-07-05
  • EntityFrameworkCore实现软删除与查询过滤器
    注意:我使用的是 Entity Framework Core 2.0 (2.0.0-preview2-final)。正式版发布后,功能可能存在变动。 继续探索Entity Frame...
    99+
    2024-04-02
  • RocketMQ消息过滤与查询的实现
    消息过滤 RocketMQ分布式消息队列的消息过滤方式有别于其它MQ中间件,是在Consumer端订阅消息时再做消息过滤的。 RocketMQ这么做是还是在于其Producer端写入...
    99+
    2024-04-02
  • SQLite中如何实现一个查询规划器
    本篇文章为大家展示了SQLite中如何实现一个查询规划器,内容简明扼要并且容易理解,绝对能使你眼前一亮,通过这篇文章的详细介绍希望你能有所收获。 1.0 介绍查询规划器的任务是找到最好的算法或者说...
    99+
    2024-04-02
  • J-Hi查询过滤器的实现原理是什么
    本篇文章给大家分享的是有关J-Hi查询过滤器的实现原理是什么,小编觉得挺实用的,因此分享给大家学习,希望大家阅读完这篇文章后可以有所收获,话不多说,跟着小编一起来看看吧。J-Hi设计自己的查询过滤器而没有直接采用Hibernate的Crit...
    99+
    2023-06-17
  • SQL汇总统计与GROUPBY过滤查询实现
    目录1、汇总统计2、GROUT BY3、如何对分组统计的结果进行过滤4、如何对分组统计的结果进行排序5、介绍SELECT语句中各个子句的书写顺序6、上方用到的表1、汇总统计 ...
    99+
    2023-01-05
    SQL汇总统计 SQL GROUP BY
  • Angular如何实现内置过滤器orderBy排序与模糊查询功能
    小编给大家分享一下Angular如何实现内置过滤器orderBy排序与模糊查询功能,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!...
    99+
    2024-04-02
  • 在Django中动态地过滤查询集的实现
    目录简介开始使用数据准备创建视图创建URL创建模板创建客户端脚本结语简介 要建立一个允许过滤和分页的列表页,你必须让一些独立的东西一起工作。Django的对象关系映射器(ORM)和内...
    99+
    2024-04-02
  • SQL汇总统计与GROUP BY过滤查询实现
    目录1、汇总统计2、GROUT BY3、如何对分组统计的结果进行过滤4、如何对分组统计的结果进行排序5、介绍SELECT语句中各个子句的书写顺序6、上方用到的表1、汇总统计 介绍几个聚集函数 有多少名学生 SE...
    99+
    2023-01-05
    SQL汇总统计 SQLGROUPBY
  • Jquery如何实现过滤选择器
    这篇文章主要介绍了Jquery如何实现过滤选择器,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。  1.根据某过滤规则进行元素的匹配...
    99+
    2024-04-02
  • 如何借助MySQL子查询来过滤数据?
    通过使用IN关键字,我们可以使用子查询来过滤数据。这是因为我们可以像使用值列表一样使用查询结果,使用IN运算符根据另一个查询的结果来过滤查询。子查询出现在IN关键字后的括号中。示例我们将使用以下表格中的数据来说明这个示例 −mysql>...
    99+
    2023-10-22
  • MyBatis3一个查询DAO的实现
    MyBatis3一个查询DAO的实现 public List<Ord> queryOrd(String ordno, String custno, int startRow, int rowSize, Ord.St.....
    99+
    2023-01-31
    DAO
  • jQuery如何创建一个嵌套的过滤器
    这篇文章给大家分享的是有关jQuery如何创建一个嵌套的过滤器的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。创建一个嵌套的过滤器.filter(":not(:has(.selected))")...
    99+
    2023-06-27
  • 利用mybatis如何实现一个一对多查询功能
    利用mybatis如何实现一个一对多查询功能?针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。1.需求分析:在开发中会遇到这样一个问题,查询订单信息,级联查询出用户信息和订单明...
    99+
    2023-05-31
    mybatis 一对多查询
  • Redis如何实现布隆过滤器
    小编给大家分享一下Redis如何实现布隆过滤器,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!布隆过滤器(Bloom Filter...
    99+
    2024-04-02
  • vue如何实现货币过滤器
    这篇文章主要介绍vue如何实现货币过滤器,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!自定义事件也可以用来创建自定义的表单输入组件,使用 v-model 来进行数据双向绑定。所以要让...
    99+
    2024-04-02
  • 使用golang怎么实现一个布谷鸟过滤器
    本文章向大家介绍使用golang怎么实现一个布谷鸟过滤器的基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。golang适合做什么golang可以做服务器端开发,但golang很适合做日志处理、数据打包、虚拟机处理、...
    99+
    2023-06-06
  • vue-cli 3中怎么实现一个全局过滤器
    vue-cli 3中怎么实现一个全局过滤器,相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。1、创建 filters.js首先新建一个filter...
    99+
    2024-04-02
  • SpringBoot+Redis如何实现布隆过滤器
    小编给大家分享一下SpringBoot+Redis如何实现布隆过滤器,希望大家阅读完这篇文章之后都有所收获,下面让我们一起去探讨吧!简述关于布隆过滤器的详细介绍,我在这里就不再赘述一遍了我们首先知道:BloomFilter使用长度为m bi...
    99+
    2023-06-29
  • MySQL如何实现一对多查询
    这篇文章给大家分享的是有关MySQL如何实现一对多查询的内容。小编觉得挺实用的,因此分享给大家做个参考。一起跟随小编过来看看吧。这次要实现的是一对多查询,使用 MySQL 的 group_concat 函数...
    99+
    2024-04-02
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作