matejsvajger / laravel-distillery
优雅地过滤Eloquent模型。
Requires
- php: ^7.1.3
Requires (Dev)
- orchestra/testbench: ^3.7
- phpunit/phpunit: ^7.4
This package is auto-updated.
Last update: 2024-09-17 11:19:34 UTC
README
简介
Laravel Distillery提供了一个优雅的方式来过滤和分页Eloquent模型。Distillery利用Laravel的API资源集合,同时构建了分页过滤后的模型/资源结果,并使使用Laravel的分页模板成为可能。
安装与配置
您可以使用Composer将Distillery安装到您的Laravel项目中。
composer require matejsvajger/laravel-distillery
安装Distillery后,使用vendor:publish
Artisan命令发布其配置。
php artisan vendor:publish --tag=distillery-config
发布Distillery的配置后,其配置文件将位于config/distillery.php
。此配置文件允许您配置应用程序设置选项,并且每个配置选项都包含其用途的描述,因此请确保彻底探索此文件。
快速入门
假设您在路由/product-list
上有一个产品列表,您需要做的只是将Distillable
特性附加到您的Product模型上。
namespace App\Models use Illuminate\Database\Eloquent\Model; use matejsvajger\Distillery\Traits\Distillable; class Product extends Model { use Distillable; protected $distillery = [ 'hidden' => [ // ], 'default' => [ // ] ]; ... }
Distillable特性添加了一个static function distill($filters = null)
。在处理/product-list
路由的控制器中,只需将Product模型的获取调用(例如:Product:all()
)替换为::distill()
class ProductListController extends Controller { public function index() { return view('product.list',[ 'products' => Product::distill() ]); } }
分页
distill()
将返回15项的分页响应。这是Eloquent模型在$perPage
属性上的默认值。您可以通过在模型中覆盖值或设置limit
的默认值来调整它。
要添加分页链接到视图调用,请在blade模板中调用$products->links();
... <!-- Your existing list --> @foreach($products as $product) <tr> <td>{{ $product->id }}</td> <td>{{ $product->name }}</td> <td>{{ $product->description }}</td> <td>{{ $product->price }}</td> </tr> @endforeach ... <div class="text-center"> {{ $products->links() }} <!-- Add this. --> </div> ...
这就是一个Product模型的分页列表。
什么?!这就像Laravel的Paginator!没错,我们还想过滤它,对吧?好吧,继续。
过滤
如果我们想通过在name
和description
上的搜索查询过滤上述产品列表,我们需要为产品模型创建一个搜索过滤器。让我们创建它。
php artisan distillery:filter Search Product
这将在app/Filters/Product/Search.php
中生成一个搜索过滤器。
生成的类实现了apply(Builder $builder, $value)
方法,该方法接收Eloquent构建器和过滤器值。
对于上述搜索示例,我们会这样做:
namespace App\Filters\Product; use Illuminate\Database\Eloquent\Builder; use matejsvajger\Distillery\Contracts\Filter; class Search implements Filter { public static function apply(Builder $builder, $value) { return $builder->where(function ($query) use ($value) { $query ->where('name', 'like', "{$value}%") ->orWhere('description', 'like', "%{$value}%"); }); } }
要将过滤器应用于前面的产品列表,只需将搜索查询字符串参数添加到URL中。
/product-list?search=socks
,并且集合将自动过滤,分页链接将反映设置过的过滤器。
有关过滤器的更多示例,请查看示例部分。
工作原理
Distillery背后的想法是每个请求参数都是一个过滤器名称/值对。Distillery遍历所有请求参数,检查是否为选定的模型存在过滤器,并根据它们的值构建过滤后的资源集合。
默认情况下,Distillery预测您有
- 模型存储在
app/Models
中, - 资源存储在
app/Http/Resources
中, - 并且过滤器位于
app/Filters
中。
所有值都可以通过配置文件进行配置。
过滤器名称
- page和
- limit
是用于Laravel分页器的保留关键字。
深入了解
Artisan命令distillery:filter
Distillery附带一个Artisan生成器命令,用于为现有模型生成过滤器类。签名有两个参数
'distillery:filter {filter} {model?}'
{filter}
过滤器名称(必需){model?}
模型类名称(可选)
如果您传入模型名称,过滤器将在子命名空间中生成:App\Filters\{Model}
。如果没有可选的模型参数,则过滤器在App\Filters
中生成,用于在多个模型上通用。
要启用对通用过滤器的回退,您需要将'fallback' => true
设置在distillery模型属性上。
在生成过程中,您可以选择一些标准过滤器模板
空白模板
生成的类返回未经修改的
$builder
。您需要自己编写逻辑。
排序模板
您定义一个要排序的模型字段列表,并选择默认排序字段和方向。
搜索模板
定义要搜索的模型字段。将生成一个具有
"like {$value}%"
的过滤器。
服务器端过滤器值
有时您可能需要在服务器端附加额外的过滤器。默认情况下,您不需要传递任何过滤器。Distilliery将从请求中提取它们。通常,您会有一个seo路由,它已经返回了部分模型而不是全部;例如,产品的分类路由:/category/summer-clothing?search=bikini
。
通常,您不会在参数中传递分类ID,因为它已经通过seo slug定义。
您可以通过传递一个数组到distill函数中来添加未在URI中定义的附加过滤器或覆盖它们。例如:如果您有一个接受id的Category过滤器,可以在控制器中附加它
public function list(Request $request, Category $category) { return view('product.list',[ 'products' => Product::distill([ 'category' => $category->id, ]); ]); }
Distillery外观
Distillery包含一个外观,它允许您在不需要Distillable特性的情况下对任何模型进行蒸馏。它接受两个参数:模型完全限定名和过滤器数组。
Distillery::distill(Product::class, $filters);
模型资源
如果您将Distillery用作API端点,您可能不希望向全世界公开您的整个模型,或者您可能想附加一些附加数据。Distillery检查是否存在Eloquent资源,并将过滤后的集合映射到它们,否则返回普通模型。
如果您没有它们,只需用Artisan创建它们即可
php artisan make:resource Product
并查看文档,了解如何使用它们。
每个模型的默认过滤器值
可以按模型定义默认过滤器值。例如,如果您想为某些模型设置默认过滤器值,可以在模型本身的protected $distillery
数组中设置一个'default'键。
class User extends Model { protected $distillery = [ 'default' => [ 'sort' => 'updated_at-desc' ] ]; }
从URI查询字符串中隐藏过滤器
在模型上有一个可用的'hidden'
配置数组,可以隐藏当它们在服务器端应用时从URI中应用的过滤器。
class User extends Model { protected $distillery = [ 'hidden' => [ 'category' // - applied in controller; set from seo url ] ]; }
启用对通用过滤器的回退
如果模型中没有定义过滤器,则可以使用通用过滤器。
class User extends Model { protected $distillery = [ 'fallback' => true ]; }
API过滤器-分页路由
Distillery提供了一个标准的过滤路由,您可以在其中自动过滤/分页任何模型,而无需将特性附加到模型上。
此功能默认是禁用的。您需要在配置中启用它。
过滤模型的默认路由是/distill
,与模型名称和查询字符串结合使用。
/distill/{model}?page=1&limit=10&search=socks
需要通过此路由进行过滤的模型需要添加到distillery.routing.models
配置数组中。
'routing' => [ 'enabled' => true, 'path' => 'distill', 'middleware' => [ 'web', ], 'models' => [ 'product' => App\Models\Product::class, ] ],
可以在配置中更改路由路径。如果您想用Auth来保护它,例如,您还可以在配置中将自定义中间件附加到路由上。
自定义分页链接
分页链接是Laravel分页的一部分。查看Laravel文档,了解如何自定义它们。
示例
排序
对于按多个字段排序模型,您可以使用如下格式的排序过滤器: sort=field-asc
和 sort=field-desc
php artisan distillery:filter Sort Product
class Sort implements Filter { protected static $allowed = ['price', 'name', 'updated_at']; public static function apply(Builder $builder, $value) { if (Str::endsWith($value, ['-asc', '-desc'])) { [$field, $dir] = explode('-', $value); if (in_array($field, static::$allowed)) { return $builder->orderBy($field, $dir); } } return $builder->orderBy('updated_at', 'desc'); } }
要应用过滤器,只需将其添加到qs中: /product-list?search=socks&sort=price-desc
。
过滤关系
有时您可能想要根据模型的关系进行过滤。
假设您有一个包含多个颜色附件的Product模型
class Color implements Filter { public static function apply(Builder $builder, $value) { $value = is_array($value) ? $value : [$value]; $query = $builder->with('colors'); foreach ($value as $colorId) { $query->whereHas('colors', function ($q) use ($colorId) { $q->where('id', $colorId); }); } return $query; } }
要应用它: /product-list?search=socks&sort=price-desc&color[]=2&color[]=5
。
通往1.0.0的道路
- 增加生成标准预定义过滤器(排序、搜索等)的可能性。
- 使能够定义要隐藏从URL查询字符串中的哪些参数。
- 添加通用过滤器的回退,这些过滤器可以在不同的模型之间重用。
- 编写测试。
许可
Laravel Distillery是开源软件,根据MIT许可证授权。