apielf / hyperf-query-builder
Build Hyperf database queries from API requests
Requires
- php: >=8.1
- hyperf/collection: ^3.0
- hyperf/config: ^3.0
- hyperf/database: ^3.0
- hyperf/di: ^3.0
- hyperf/http-server: ^3.0
- hyperf/stringable: ^3.0
README
基于 API 请求构建 Hyperf 数据库查询。这个包允许你基于请求过滤、排序和包含 Eloquent 关联。QueryBuilder
继承了 Hyperf 的默认 Eloquent 构建器,这意味着你所有喜欢的方法和宏仍然可用。查询参数名称尽可能地遵循JSON API 规范。
目录
安装
你可以通过 composer 安装此包:
composer require apielf/hyperf-query-builder
发布配置文件:
php bin/hyperf.php vendor:publish apielf/hyperf-query-builder
配置
配置文件位于config/autoload/query-builder.php
,你可以在其中自定义查询参数名称:
return [ /* * 默认查询参数名称 */ 'parameters' => [ 'include' => 'include', 'filter' => 'filter', 'sort' => 'sort', 'fields' => 'fields', 'append' => 'append', ], /* * 是否禁用无效过滤器查询异常 */ 'disable_invalid_filter_query_exception' => false, ];
基本用法
基于请求过滤查询:/users?filter[name]=John
use ApiElf\QueryBuilder\QueryBuilder; $users = QueryBuilder::for(User::class) ->allowedFilters('name') ->get(); // 所有名称中包含"John"的`User`
基于请求包含关联:/users?include=posts
$users = QueryBuilder::for(User::class) ->allowedIncludes('posts') ->get(); // 所有`User`及其`posts`关联
基于请求排序查询:/users?sort=id
$users = QueryBuilder::for(User::class) ->allowedSorts('id') ->get(); // 所有`User`按id升序排序
选择查询字段:/users?fields[users]=id,email
$users = QueryBuilder::for(User::class) ->allowedFields(['id', 'email']) ->get(); // 获取的`User`只会有id和email字段
过滤
基本过滤
默认情况下,filter
参数将使用部分匹配(LIKE 查询)进行过滤:
// GET /users?filter[name]=John $users = QueryBuilder::for(User::class) ->allowedFilters('name') ->get(); // 等同于 $query->where('name', 'LIKE', '%John%')
你可以指定多个允许的过滤器:
$users = QueryBuilder::for(User::class) ->allowedFilters('name', 'email', 'age') ->get();
精确过滤
如果你需要精确匹配而不是部分匹配,可以使用exact
过滤器:
use ApiElf\QueryBuilder\AllowedFilter; // GET /users?filter[email]=john@example.com $users = QueryBuilder::for(User::class) ->allowedFilters(AllowedFilter::exact('email')) ->get(); // 等同于 $query->where('email', 'john@example.com')
你也可以为过滤器指定一个内部名称,这在字段名称与过滤器名称不同时很有用:
// GET /users?filter[email_address]=john@example.com $users = QueryBuilder::for(User::class) ->allowedFilters(AllowedFilter::exact('email_address', 'email')) ->get(); // 等同于 $query->where('email', 'john@example.com')
部分匹配过滤
虽然默认过滤器已经是部分匹配,但你也可以显式使用partial
过滤器:
// GET /users?filter[name]=John $users = QueryBuilder::for(User::class) ->allowedFilters(AllowedFilter::partial('name')) ->get(); // 等同于 $query->where('name', 'LIKE', '%John%')
范围过滤
你可以使用模型上定义的查询范围进行过滤:
// 在User模型中定义范围 public function scopePopular($query) { return $query->where('votes', '>', 100); } // GET /users?filter[popular]=true $users = QueryBuilder::for(User::class) ->allowedFilters(AllowedFilter::scope('popular')) ->get();
自定义过滤器
你可以使用回调函数创建完全自定义的过滤器:
// GET /users?filter[name]=John $users = QueryBuilder::for(User::class) ->allowedFilters(AllowedFilter::callback('name', function ($query, $value) { $query->where('first_name', 'LIKE', "%{$value}%") ->orWhere('last_name', 'LIKE', "%{$value}%"); })) ->get();
软删除过滤
如果你的模型使用了软删除,你可以使用trashed
过滤器:
// GET /users?filter[trashed]=with $users = QueryBuilder::for(User::class) ->allowedFilters(AllowedFilter::trashed()) ->get(); // 可能的值: // 'with': withTrashed() // 'only': onlyTrashed() // 'without': withoutTrashed()
默认过滤器
你可以为过滤器设置默认值,当请求中没有提供该过滤器时使用:
// 当请求中没有filter[name]参数时,默认过滤name为'John' $users = QueryBuilder::for(User::class) ->allowedFilters(AllowedFilter::partial('name')->default('John')) ->get();
忽略特定值
你可以配置过滤器忽略特定值:
// 忽略空字符串和null值 $users = QueryBuilder::for(User::class) ->allowedFilters(AllowedFilter::exact('name')->ignore(['', null])) ->get();
排序
基本排序
你可以使用sort
参数对结果进行排序:
// GET /users?sort=name $users = QueryBuilder::for(User::class) ->allowedSorts('name') ->get(); // 升序排序 // GET /users?sort=-name $users = QueryBuilder::for(User::class) ->allowedSorts('name') ->get(); // 降序排序(注意前缀'-')
你可以指定多个允许的排序字段:
$users = QueryBuilder::for(User::class) ->allowedSorts('name', 'email', 'created_at') ->get();
默认排序
当请求中没有指定排序参数时,你可以设置默认排序:
// 默认按创建时间降序排序 $users = QueryBuilder::for(User::class) ->allowedSorts('name', 'email', 'created_at') ->defaultSort('-created_at') ->get();
你也可以使用AllowedSort
对象设置默认排序:
use ApiElf\QueryBuilder\AllowedSort; // 默认按创建时间降序排序 $users = QueryBuilder::for(User::class) ->allowedSorts('name', 'email', 'created_at') ->defaultSort( AllowedSort::field('created_at') ->defaultDirection(AllowedSort::DESCENDING) ) ->get();
你可以设置多个默认排序:
// 默认先按状态升序,再按创建时间降序排序 $users = QueryBuilder::for(User::class) ->allowedSorts('name', 'email', 'status', 'created_at') ->defaultSorts('status', '-created_at') ->get();
自定义排序
你可以使用自定义排序逻辑:
use ApiElf\QueryBuilder\AllowedSort; use ApiElf\QueryBuilder\Sorts\Sort; class RandomSort implements Sort { public function __invoke(QueryBuilder $query, bool $descending, string $property) { $query->inRandomOrder(); } } // GET /users?sort=random $users = QueryBuilder::for(User::class) ->allowedSorts(AllowedSort::custom('random', new RandomSort())) ->get();
你也可以使用回调函数创建自定义排序:
use ApiElf\QueryBuilder\AllowedSort; // GET /users?sort=full_name $users = QueryBuilder::for(User::class) ->allowedSorts( AllowedSort::callback('full_name', function ($query, $descending, $property) { $direction = $descending ? 'desc' : 'asc'; $query->orderBy('first_name', $direction) ->orderBy('last_name', $direction); }) ) ->get();
包含关联
基本关联
你可以使用include
参数加载关联:
// GET /users?include=posts $users = QueryBuilder::for(User::class) ->allowedIncludes('posts') ->get(); // 等同于 $query->with('posts')
你可以指定多个允许的关联:
$users = QueryBuilder::for(User::class) ->allowedIncludes('posts', 'permissions', 'roles') ->get();
嵌套关联
你可以使用点符号包含嵌套关联:
// GET /users?include=posts.comments $users = QueryBuilder::for(User::class) ->allowedIncludes('posts.comments') ->get(); // 等同于 $query->with('posts.comments')
关联计数
你可以使用count
方法包含关联计数:
use ApiElf\QueryBuilder\AllowedInclude; // GET /users?include=posts_count $users = QueryBuilder::for(User::class) ->allowedIncludes(AllowedInclude::count('posts_count', 'posts')) ->get(); // 等同于 $query->withCount('posts')
自定义关联
你可以创建自定义的关联包含逻辑:
use ApiElf\QueryBuilder\AllowedInclude; use ApiElf\QueryBuilder\Includes\IncludeInterface; class LatestPostsInclude implements IncludeInterface { public function __invoke(QueryBuilder $query) { $query->with(['posts' => function ($query) { $query->latest()->limit(5); }]); } } // GET /users?include=latest_posts $users = QueryBuilder::for(User::class) ->allowedIncludes(AllowedInclude::custom('latest_posts', new LatestPostsInclude())) ->get();
选择字段
你可以使用fields
参数选择要返回的字段:
// GET /users?fields[users]=id,name,email $users = QueryBuilder::for(User::class) ->allowedFields(['id', 'name', 'email']) ->get(); // 只返回id、name和email字段
你也可以为关联选择字段:
// GET /users?fields[users]=id,name&fields[posts]=title,content $users = QueryBuilder::for(User::class) ->allowedFields(['id', 'name', 'posts.title', 'posts.content']) ->allowedIncludes('posts') ->get();
追加属性
你可以使用append
参数追加模型访问器:
// 在User模型中定义访问器 public function getFullNameAttribute() { return $this->first_name . ' ' . $this->last_name; } // GET /users?append=full_name $users = QueryBuilder::for(User::class) ->allowedAppends('full_name') ->get();
分页
QueryBuilder 与 Hyperf 的分页功能完全兼容:
// GET /users?page=2&page_size=10 $users = QueryBuilder::for(User::class) ->paginate($request->input('page_size', 15)); // 或者使用简单分页 $users = QueryBuilder::for(User::class) ->simplePaginate($request->input('page_size', 15));
高级用法
与现有查询结合
你可以将 QueryBuilder 与现有查询结合使用:
$query = User::where('active', true); $users = QueryBuilder::for($query) // 从现有的Builder实例开始 ->withTrashed() // 使用你现有的作用域 ->allowedIncludes('posts', 'permissions') ->where('score', '>', 42) // 链接任何Hyperf查询构建器方法 ->get();
条件查询
你可以根据条件添加过滤器、排序和包含:
$users = QueryBuilder::for(User::class) ->when($request->has('filter_active'), function ($query) { $query->allowedFilters('active'); }) ->when($request->has('include_posts'), function ($query) { $query->allowedIncludes('posts'); }) ->get();
异常处理
默认情况下,当请求包含未允许的过滤器、排序或包含时,QueryBuilder 会抛出异常。你可以在配置文件中禁用这些异常:
// config/autoload/query-builder.php return [ // ... 'disable_invalid_filter_query_exception' => true, ];
或者你可以在运行时禁用它们:
$users = QueryBuilder::for(User::class) ->allowedFilters('name') ->allowedSorts('name') ->allowedIncludes('posts') ->disableInvalidFilterQuery() ->get();
最佳实践
在控制器中使用
class UserController extends AbstractController { public function index() { return QueryBuilder::for(User::class) ->allowedFilters(['name', 'email', AllowedFilter::exact('type')]) ->allowedSorts(['name', 'created_at']) ->allowedIncludes(['posts', 'permissions']) ->allowedFields(['id', 'name', 'email', 'created_at']) ->paginate() ->toArray(); } }
创建专用的查询构建器类
对于复杂的查询,你可以创建专用的查询构建器类:
class UserQueryBuilder extends QueryBuilder { public function __construct() { parent::__construct(User::query()); $this->allowedFilters(['name', 'email', AllowedFilter::exact('type')]) ->allowedSorts(['name', 'created_at']) ->allowedIncludes(['posts', 'permissions']) ->allowedFields(['id', 'name', 'email', 'created_at']); } } // 在控制器中使用 public function index() { return (new UserQueryBuilder())->paginate(); }
所有功能未完全经过测试,如果遇到 bug 请到 github 中提交 issues
致谢
这个包是基于spatie/laravel-query-builder改写的,感谢原作者的出色工作。