Laravel Query Builder:API 请求到 Eloquent 查询的优雅转换器
你是否曾经为构建复杂的 API 查询参数而头疼?是否厌倦了在控制器中编写重复的过滤、排序和关联查询逻辑?Laravel Query Builder 正是为了解决这些痛点而生!
什么是 Laravel Query Builder?
Laravel Query Builder 是一个强大的开源包,它允许你通过简单的 API 请求参数来构建复杂的 Eloquent 查询。这个包遵循 JSON API 规范,提供了优雅的方式来处理过滤、排序、字段选择和关联包含等常见需求。
核心优势一览
| 功能 | 传统方式 | 使用 Query Builder |
|---|---|---|
| 基本过滤 | 手动解析参数并构建 where 条件 | ->allowedFilters('name') |
| 关联查询 | 复杂的 with 和 whereHas 组合 | ->allowedIncludes('posts') |
| 排序功能 | 手动处理 sort 参数 | ->allowedSorts('created_at') |
| 字段选择 | 手动 select 字段 | ->allowedFields(['id', 'name']) |
| 安全性 | 需要手动验证参数 | 内置白名单机制 |
快速开始:5分钟上手
安装配置
composer require spatie/laravel-query-builder
发布配置文件(可选):
php artisan vendor:publish --provider="Spatie\QueryBuilder\QueryBuilderServiceProvider"
基础使用示例
use Spatie\QueryBuilder\QueryBuilder;
class UserController extends Controller
{
public function index()
{
$users = QueryBuilder::for(User::class)
->allowedFilters(['name', 'email', 'status'])
->allowedIncludes(['posts', 'profile'])
->allowedSorts(['created_at', 'name'])
->paginate();
return UserResource::collection($users);
}
}
现在你的 API 支持以下查询:
/users?filter[name]=John- 按名称过滤/users?include=posts,profile- 包含关联数据/users?sort=-created_at- 按创建时间倒序
深度解析:过滤功能详解
过滤类型对比表
| 过滤类型 | 使用方法 | 适用场景 | 示例 URL |
|---|---|---|---|
| 部分匹配 | allowedFilters('name') | 文本搜索 | ?filter[name]=john |
| 精确匹配 | AllowedFilter::exact('status') | 枚举值、ID | ?filter[status]=active |
| 范围过滤 | AllowedFilter::scope('price_range') | 价格区间 | ?filter[price_range]=100,500 |
| 关联过滤 | AllowedFilter::exact('posts.title') | 关联表查询 | ?filter[posts.title]=Laravel |
| 自定义过滤 | AllowedFilter::custom() | 复杂业务逻辑 | 自定义实现 |
过滤功能流程图
高级过滤示例
use Spatie\QueryBuilder\AllowedFilter;
use Spatie\QueryBuilder\Enums\FilterOperator;
$users = QueryBuilder::for(User::class)
->allowedFilters([
// 基本部分匹配
'name',
'email',
// 精确匹配
AllowedFilter::exact('status'),
AllowedFilter::exact('role_id'),
// 范围操作符
AllowedFilter::operator('salary', FilterOperator::GREATER_THAN),
AllowedFilter::operator('age', FilterOperator::LESS_THAN_OR_EQUAL),
// 关联过滤
AllowedFilter::exact('posts.category_id'),
AllowedFilter::partial('posts.title'),
// 作用域过滤
AllowedFilter::scope('created_between'),
AllowedFilter::scope('has_premium_subscription'),
// 自定义回调过滤
AllowedFilter::callback('min_rating', function ($query, $value) {
$query->whereHas('reviews', function ($q) use ($value) {
$q->where('rating', '>=', $value);
});
}),
])
->get();
关联包含:优雅的 Eager Loading
关联包含类型
关联包含示例
$products = QueryBuilder::for(Product::class)
->allowedIncludes([
// 普通关联
'category',
'tags',
'vendor',
// 嵌套关联
'category.parent',
'reviews.user',
// 关联计数
AllowedInclude::count('reviews_count'),
AllowedInclude::count('tags_count'),
// 关联存在检查
AllowedInclude::exists('has_discount'),
// 自定义包含
AllowedInclude::callback('related_products', function ($query) {
$query->with(['relatedProducts' => function ($q) {
$q->where('status', 'active');
}]);
}),
])
->get();
支持的 URL 查询:
/products?include=category,vendor- 包含分类和供应商/products?include=reviews_count- 包含评论计数/products?include=category.parent- 包含分类的父分类
排序功能:灵活的数据排序
排序配置示例
use Spatie\QueryBuilder\AllowedSort;
$users = QueryBuilder::for(User::class)
->allowedSorts([
// 简单字段排序
'name',
'email',
'created_at',
// 自定义排序
AllowedSort::field('registration_date', 'created_at'),
AllowedSort::field('full_name', 'CONCAT(first_name, " ", last_name)'),
// 回调排序
AllowedSort::callback('popularity', function ($query, $direction) {
$query->orderByRaw('(likes_count - dislikes_count) ' . $direction);
}),
// 关联表排序
AllowedSort::field('category_name', 'categories.name'),
])
->get();
排序参数示例
| 排序方式 | URL 示例 | 说明 |
|---|---|---|
| 升序 | ?sort=name | 按名称升序排列 |
| 降序 | ?sort=-created_at | 按创建时间降序 |
| 多字段 | ?sort=category,-price | 先按分类升序,再按价格降序 |
字段选择:优化数据返回
字段选择配置
$users = QueryBuilder::for(User::class)
->allowedFields([
'id',
'name',
'email',
'created_at',
'profile.avatar',
'profile.bio',
])
->get();
支持的 URL 查询:
/users?fields[users]=id,name,email- 只返回用户的基本信息/users?fields[users]=id,name&fields[profiles]=avatar- 返回用户信息和头像
安全最佳实践
白名单机制的重要性
Laravel Query Builder 采用白名单机制,确保只有明确允许的参数才会被处理,这提供了良好的安全性保障。
// 安全配置示例
$query = QueryBuilder::for(User::class)
->allowedFilters([ // 只允许这些过滤字段
'name',
'email',
'status',
AllowedFilter::exact('role_id'),
])
->allowedIncludes([ // 只允许这些关联
'profile',
'posts',
])
->allowedSorts([ // 只允许这些排序字段
'name',
'created_at',
])
->allowedFields([ // 只允许这些字段
'id',
'name',
'email',
]);
配置选项
在 config/query-builder.php 中可以配置:
return [
'parameters' => [
'include' => 'include',
'filter' => 'filter',
'sort' => 'sort',
'fields' => 'fields',
'append' => 'append',
],
'disable_invalid_filter_query_exception' => false,
'disable_invalid_sort_query_exception' => false,
'disable_invalid_include_query_exception' => false,
'disable_invalid_field_query_exception' => false,
];
实际应用场景
电商产品搜索
public function searchProducts(Request $request)
{
$products = QueryBuilder::for(Product::class)
->allowedFilters([
'name',
AllowedFilter::exact('category_id'),
AllowedFilter::exact('brand_id'),
AllowedFilter::scope('price_between'),
AllowedFilter::scope('in_stock'),
AllowedFilter::exact('tags.id'),
])
->allowedIncludes([
'category',
'brand',
'images',
'reviews',
'reviews_count',
])
->allowedSorts([
'price',
'created_at',
'popularity',
'rating',
])
->allowedFields([
'id',
'name',
'price',
'image',
'rating',
'category.name',
])
->paginate($request->per_page ?? 20);
return ProductResource::collection($products);
}
用户管理系统
public function getUsers(Request $request)
{
$users = QueryBuilder::for(User::class)
->allowedFilters([
'name',
'email',
AllowedFilter::exact('status'),
AllowedFilter::exact('department_id'),
AllowedFilter::scope('created_between'),
AllowedFilter::trashed(),
])
->allowedIncludes([
'profile',
'roles',
'department',
'posts_count',
])
->allowedSorts([
'name',
'email',
'created_at',
'last_login_at',
])
->withTrashed() // 包含软删除的用户
->paginate($request->per_page ?? 15);
return UserResource::collection($users);
}
性能优化建议
索引优化
-- 为常用的过滤字段创建索引
CREATE INDEX users_name_index ON users(name);
CREATE INDEX users_email_index ON users(email);
CREATE INDEX users_status_index ON users(status);
-- 为排序字段创建索引
CREATE INDEX users_created_at_index ON users(created_at);
查询优化技巧
- 避免 N+1 问题:使用
allowedIncludes自动处理 eager loading - 限制返回字段:使用
allowedFields减少数据传输量 - 分页处理:始终使用
paginate()而不是get()对于大量数据 - 缓存策略:对频繁查询的结果实施缓存
常见问题解答
Q: 如何处理复杂的业务逻辑过滤?
A: 使用 AllowedFilter::callback() 或 AllowedFilter::custom() 来自定义过滤逻辑
Q: 如何支持多值过滤?
A: 使用逗号分隔的值,如 ?filter[category_id]=1,2,3
Q: 如何禁用异常处理?
A: 在配置文件中设置相应的 disable_*_exception 为 true
Q: 如何处理关联表的复杂查询?
A: 使用点符号,如 AllowedFilter::exact('posts.comments.user_id')
总结
Laravel Query Builder 是一个功能强大且灵活的包,它极大地简化了 API 查询参数的处理。通过白名单机制、丰富的过滤类型、灵活的排序和字段选择功能,它能够满足大多数 API 开发需求。
核心价值总结
- 开发效率:减少重复代码,提高开发速度
- 代码质量:统一的查询处理逻辑,更易维护
- 安全性:白名单机制防止非法参数
- 灵活性:支持各种复杂的查询场景
- 性能:自动优化查询,减少 N+1 问题
无论是简单的 CRUD 应用还是复杂的企业级系统,Laravel Query Builder 都能为你提供优雅的解决方案。开始使用它,让你的 API 开发体验更加愉快!
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考



