概述
include英译:包含,包括,使成为...的一部分
我们在进行API开发时可能会将数据以及与该数据相关的数据全部发送给客户端,如 文章数据,相关的数据有“用户”,“分类”...
如下例
代码语言:javascript复制public function index(Request $request, Topic $topic)
{
$query = $topic->query();
if ($categoryId = $request->category_id) {
$query->where('category_id', $categoryId);
}
$topics = $query
->with('user', 'category')
->withOrder($request->order)
->paginate();
return TopicResource::collection($topics);
}
返回的结果 可以看到,除了返回文章相关数据外,还返回了用户相关数据
这样做不太友好,因为我们不知道客户端是否需要某些数据,如这里的“用户”。 没必要的数据增加了数据库操作,增加了数据响应。
正确的做法应该是,服务端返回基础数据,在根据客户端传参返回其相关联的数据
如何实现?
通过laravel第三方扩展包 spatie/laravel-query-builder 官方文档:https://docs.spatie.be/laravel-query-builder/v2/introduction/
1.composer 引入
代码语言:javascript复制composer require spatie/laravel-query-builder
2.控制器使用
代码语言:javascript复制use SpatieQueryBuilderQueryBuilder;
use SpatieQueryBuilderAllowedFilter;
public function index(Request $request, Topic $topic)
{
$topics = QueryBuilder::for(Topic::class)
->allowedIncludes('user','category')//可以被include的参数
->allowedFilters([//允许过滤搜索的字段
'title',//模糊搜索title
AllowedFilter::exact('category_id'),//精确搜索category_id字段
AllowedFilter::scope('withOrder')->default('recentReplied'),//本地作用域,传递默认参数
])
->paginate();
return TopicResource::collection($topics);
}
这里主要设计到两个方法 allowedIncludes
:指定可被include的参数 客户端输入 include=user
可动态返回文章,用户的信息 include=user,category
返回文章,用户,分类的信息
allowedFilters
:指定允许被过滤的字段,可以用作搜索 直接键入title
表示模糊搜索
客户端 fillter[title]=none
模糊查找title包含none的文章
AllowedFilter::exact('category_id')
:表示精确过滤的字段 我们还可以键入某个scope
(查询作用域)对数据进一步过滤,本文使用了定义好的withOrder
作用域
//调用排序函数
public function scopewithOrder($query,$order)
{
switch($order){
case "recent":
$query->recent();
break;
default :
$query->RecentReplied();
}
}
利用postMan调试接口 1.基础数据
2.关联数据
上面的方式已经很好的解决了我们的问题,但是代码不够精炼,试想如果我们每个资源都要使用include机制那每个方法都会键入类似如下代码
代码语言:javascript复制$topics = QueryBuilder::for(Topic::class)
->allowedIncludes('user','category')//可以被include的参数
->allowedFilters([//允许过滤搜索的字段
'title',//模糊搜索title
AllowedFilter::exact('category_id'),//精确搜索category_id字段
AllowedFilter::scope('withOrder')->default('recentReplied'),//本地作用域,传递默认参数
])
->paginate();
解决办法:为每个模型类添加一个Queries
类 在这个Queries
类里面键入上面逻辑,控制器使用时只需将该类依赖注入即可 1.新键Queries
类
mkdir app/Http/Queries
touch app/Http/Queries/TopicQuery.php
2.在TopicQuery.php键入如下代码
代码语言:javascript复制<?php
namespace AppHttpQueries;
use AppModelsTopic;
use SpatieQueryBuilderQueryBuilder;
use SpatieQueryBuilderAllowedFilter;
class TopicQuery extends QueryBuilder
{
public function __construct()
{
parent::__construct(Topic::query());
$this->allowedIncludes('user', 'category')
->allowedFilters([
'title',
AllowedFilter::exact('category_id'),
AllowedFilter::scope('withOrder')->default('recentReplied'),
]);
}
}
控制器使用依赖注入
代码语言:javascript复制public function index(Request $request, TopicQuery $query)
{
$topics = $query->paginate();
return TopicResource::collection($topics);
}
可以看到代码精炼了许多