bayareawebpro / searchable-resource
Laravel 可搜索资源构建器
Requires
- php: ^7.4|^8.0
- laravel/framework: ^8.0|^9.0
Requires (Dev)
- laravel/legacy-factories: ^1.0
- nunomaduro/larastan: ^0.5|^1.0|^2.0
- orchestra/testbench: ^4.0|^5.0|^6.0|^7.0
- phpunit/phpunit: ^8.0|^9.0
This package is auto-updated.
Last update: 2023-10-09 04:18:37 UTC
README
Searchable Resource Builder 是在 Laravel 应用程序中构建可搜索资源响应的抽象。将查询逻辑提取为可重用的块,同时使用流畅的构建器接口处理可搜索/可过滤/可排序请求和 JSON/API 资源。
composer require bayareawebpro/searchable-resource
基本用法
SearchableResources 实现了 Responsable
接口,这使得它们可以从控制器中轻松返回。它也可以与 blade 一起使用。
make
方法接受 Eloquent Builder 的实例。
SearchableResource::make(User::query());
排序和排序
您可以指定任意多的可排序列。
SearchableResource::make(User::query()) ->orderable(['name', 'email']) ->orderBy('name') ->sort('desc') ->paginate(16);
默认设置
- 按 ID 排序
- 降序排序
完整示例
use App\User; use App\Queries\UserSearch; use App\Queries\RoleFilter; use App\Http\Resources\UserResource; use BayAreaWebPro\SearchableResource\SearchableResource; use BayAreaWebPro\SearchableResource\SearchableBuilder; SearchableResource::make(User::query()) ->resource(UserResource::class) ->queries([ UserSearch::class, RoleFilter::class ]) ->orderable([ 'id', 'name', 'email', 'role', 'created_at', 'updated_at', ]) ->appendable([ 'created_for_humans', 'updated_for_humans', ]) ->select([ 'id', 'name', 'email', 'role', 'created_at', 'updated_at', ]) ->rules([ 'my_filter_key' => 'required|string' ]) ->params([ 'my_filter_key' => 'my_default' ]) ->options([ 'my_filter_key' => ['my_default', 'option2', 'option3'] ]) ->with([ 'my_key' => true ]) ->when(true, fn(SearchableBuilder $builder)=>$builder ->with([ 'my_key' => false ]) ) ->orderBy('updated_at') ->sort('desc') ->paginate(16) ->labeled();
Blade/视图示例
执行查询并返回包含项目和选项的视图。
public function index() { $resource = SearchableResource::make(User::query()) ->query(Users::make()) ->orderable(['name', 'email']) ->orderBy('name') ->sort('desc') ->paginate(5) ->execute() ; return view('users.index', [ 'items' =>$resource->getItems(), 'search' =>$resource->getSearch(), 'order_by' =>$resource->getOrderBy(), 'per_page' =>$resource->getPerPage(), 'options' =>$resource->getOptions(), 'sort' =>$resource->getSort(), ]); }
<x-admin::form method="GET"> <x-admin::search name="search" value="{{ $search ?? null }}" /> <x-admin::select name="order_by" value="{{ $order_by ?? null }}" :options="$options->get('order_by')" /> <x-admin::select name="sort" value="{{ $sort ?? null }}" :options="$options->get('sort')" /> <x-admin::select name="per_page" value="{{ $per_page ?? null }}" :options="$options->get('per_page')" /> <x-admin::submit label="{{ __('resources.filter') }}" /> </x-admin::form>
JSON 资源
SearchableResources 默认为通用 JsonResources。您可以在构建响应时轻松指定应使用哪个资源类来映射您的模型。
必须扩展
JsonResource
。
SearchableResource::make(User::query())->resource(UserResource::class);
可调用的查询
查询以可调用的类形式表达,这些类扩展了包含每个请求字段逻辑的 AbstractQuery
类。查询可以应用于多个属性/列,以及多个 orWhere
子句的输入。
php artisan make:searchable NameQuery
以下是一个通用名称查询的示例
<?php declare(strict_types=1); namespace App\Queries; use Illuminate\Database\Eloquent\Builder; use BayAreaWebPro\SearchableResource\AbstractQuery; class LikeQuery extends AbstractQuery { public string $field = 'search'; protected string $attribute = 'name'; public function __invoke(Builder $builder): void { $builder->where($this->attribute, "like", "%{$this->getValue($this->field)}%"); } }
SearchableResource::make(User::query()) ->query( LikeQuery::make() ->field('search') ->attribute('last_name') ) ->query( SelectQuery::make() ->field('role') ->attribute('role') ->options(['admin', 'customer']) ) ;
条件查询协议
实现 ConditionalQuery
协议的查询只有在它们的 applies
方法返回 true
时才会应用。
默认情况下,使用 ConditionalQuery
协议扩展 AbstractQuery
类的查询已经为您实现了此方法,通过在请求上调用 filled
方法。
覆盖父方法以自定义。
<?php declare(strict_types=1); namespace App\Queries; use BayAreaWebPro\SearchableResource\AbstractQuery; use BayAreaWebPro\SearchableResource\Contracts\ConditionalQuery; class ConditionalRoleQuery extends AbstractQuery implements ConditionalQuery { public string $field = 'role'; protected string $attribute = 'role'; public function __invoke(Builder $builder): void { $builder->where($this->attribute, $this->getValue($this->field)); } public function getApplies(): bool { return parent::getApplies(); // Customize with $this->request } }
验证
查询可以通过实现 ValidatableQuery
协议来指定自己的验证规则,以便与可搜索集合合并规则。
ValidatableQuery 协议
实现 ValidatableQuery
协议的查询将它们的返回规则合并到验证器中,否则将忽略规则。
<?php declare(strict_types=1); namespace App\Queries; use BayAreaWebPro\SearchableResource\AbstractQuery; use BayAreaWebPro\SearchableResource\Contracts\ValidatableQuery; class ConditionalRoleQuery extends AbstractQuery implements ValidatableQuery { public string $role = 'role'; public string $admins = 'only_admins'; protected string $attribute = 'role'; public function __invoke(Builder $builder): void { $builder->where($this->attribute, $this->getValue($this->admins) ?: $this->getValue($this->role)); } public function getRules(): array { return [ $this->role => [ 'required' ], $this->admins => [ 'sometimes' ], ]; } }
ProvidesOptions 协议
查询可以通过实现 ProvidesOptions
协议来提供选项,这些选项将通过实现 ProvidesOptions
协议添加到请求选项数据中。此方法应返回一个平面数组,该数组将注入到响应查询选项数据中。
<?php declare(strict_types=1); namespace App\Queries; use BayAreaWebPro\SearchableResource\AbstractQuery; use BayAreaWebPro\SearchableResource\Contracts\ProvidesOptions; class ProvidesOptionsQuery extends AbstractQuery implements ProvidesOptions { public string $field = 'role'; protected string $attribute = 'role'; public function __invoke(Builder $builder): void { $builder->where($this->attribute, $this->getValue($this->field)); } public function getOptions(): array { return [ $this->field => [ 'admin', 'editor' ], ]; } }
选项格式化
选项可以通过在构建器上调用 labeled()
方法进行格式化,以便与表单和过滤器一起使用。labeled 方法接受一个布尔值,可以用来在请求有会话时启用。
您可以从查询中返回预格式化的选项(标签/值数组)或使用格式化器生成带标签的选项。
API 选项模式
API 请求的选项通常未格式化以提高速度。
public function getOptions(): array
{
return [
'role' => [
'admin',
'customer'
]
];
}
Blade 选项模式
Blade 请求的选项可以格式化以提高可用性。
public function getOptions(): array
{
return [
$this->field => [
[
'label' => 'Admin',
'value' => 'admin'
],
[
'label' => 'Customer',
'value' => 'customer'
]
]
];
}
FormatsOptions 协议
您可以通过指定格式化器实例来覆盖默认格式化器。
SearchableResource::make(User::query())->useFormatter(new OptionsFormatter);
<?php declare(strict_types=1); namespace App\Http\Resources\Formatters; use Illuminate\Support\Collection; use BayAreaWebPro\SearchableResource\OptionsFormatter as Formatter; class OptionsFormatter extends Formatter { /** * @param string $key * @param Collection $options * @return Collection */ public function __invoke(string $key, Collection $options): Collection { if($key === 'abilities'){ return $this->nullable($this->literal($options)); } if($key === 'role'){ return $this->nullable($this->titleCase($options)); } return $this->baseOptions($key, $options); } }
设置默认选项
您可以在服务提供器中设置一个解析回调,以预先绑定选项到每个实例。
use BayAreaWebPro\SearchableResource\OptionsFormatter; use BayAreaWebPro\SearchableResource\SearchableBuilder; $this->app->resolving( SearchableBuilder::class, function (SearchableBuilder $builder){ return $builder ->useFormatter(new OptionsFormatter) ->labeled(request()->hasSession()) ->orderBy('created_at') ->paginate(8) ->sort('desc') ; });
添加查询
查询可以通过两种方式添加。首先是通过引用类字符串以便于批量使用。
use App\Queries\RoleQuery; SearchableResource::make(User::query()) ->queries([ RoleQuery::class ]);
通过使用 make
方法实例化每个查询。当您需要更多方法和逻辑来确定使用时,这可能很有用。
use App\Queries\RoleQuery; SearchableResource::make(User::query()) ->query(RoleQuery::make());
可追加数据
可以使用以下方法将属性和字段追加到响应中
对于模型属性
SearchableResource::make(User::query()) ->appendable([ 'created_for_humans', 'updated_for_humans', 'bytes_for_humans', ]);
对于附加数据(追加到响应中)
SearchableResource::make(User::query()) ->with([ 'my_key' => [] ]);
{ "my_key": [], "data": [] }
对于请求字段(追加到响应中的查询)
SearchableResource::make(User::query()) ->fields([ 'my_filter_state' ]);
{ "query": { "my_filter_state": true } }
条件回调
您可以使用回调或可调用类来获得更少的链式方法并实现更多控制。
class SessionEnabledQuery{ public function __invoke(SearchableBuilder $builder): void { $builder->labeled(); } } SearchableResource::make(User::query()) ->when(request()->hasSession(), new SessionEnabledQuery) ->when(request()->hasSession(), function(SearchableBuilder $builder){ $builder->labeled(); }) ;
触摸回调
通过可调用类配置构建器很有用。
use BayAreaWebPro\SearchableResource\SearchableBuilder; use BayAreaWebPro\SearchableResource\Contracts\InvokableBuilder; class UserSearchable implements InvokableBuilder{ public function __invoke(SearchableBuilder $builder): void { $builder->queries([ RoleQuery::class ]); } } SearchableResource::make(User::query())->tap(new UserSearchable);
响应输出
为了方便起见,将相关的查询参数和请求选项追加到输出中。在分页参数中添加了两个附加属性,以消除客户端/用户侧的条件判断,即 isFirstPage
和 isLastPage
,这使得通过属性(Vue | React)轻松禁用分页按钮。
注意:如果不使用
pagination
方法,则所有与分页相关的属性都将从输出数据中过滤掉。
"data": [
//
],
"pagination": {
"isFirstPage": true,
"isLastPage": true,
...default pagination props...
},
"query": {
"page": 1,
"sort": "desc",
"order_by": "id",
"search": "term",
"per_page": 4,
},
"options": {
"orderable": [
"id",
"name"
],
"sort": [
"asc"
"desc"
]
}
测试
composer test
composer lint