Api 开发之include机制

2022-09-14 14:59:55 浏览数 (1)

概述

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作用域

代码语言:javascript复制
//调用排序函数
    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

代码语言:javascript复制
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);
    }

可以看到代码精炼了许多

0 人点赞