weiwenhao / tree-ql
laravel 包含
Requires
- php: >=7.1.0
- illuminate/support: 5.5.*|5.6.*|5.7.*|5.8.*
This package is auto-updated.
Last update: 2024-09-14 05:21:53 UTC
README
英文 | 简体中文
什么是tree-ql?
tree-ql是一个通过简单的配置来构建高度描述性、可读性、高性能API的laravel扩展,没有任何冗余。
-
不会侵入laravel,可以随时集成到现有的laravel项目中。
-
不会侵入RESTful,并基于RESTful进一步改进API的描述性。
-
无论API描述多么复杂,都不会出现N + 1问题。
-
Tree-ql不是一组API规范,而是一个可以提高开发效率的生产环境解决方案。
-
所见即所得,客户端可以根据自己的需求包含所需资源,无需冗余。
基本描述
快速描述
复杂描述
安装
确保您的laravel版本高于5.5,并在项目根目录下执行
composer require weiwenhao/tree-ql
当前版本号 < 1.0.0,属于alpha版本。
文档
包含
您可以看到tree-ql的核心是包含,那么包含的语法规则是什么?
您可以看到包含的语法更简单,只需要添加两个点。
-
点和花括号
{}都可以表示层次嵌套关系,当只嵌套一个字段在花括号内时,它们是语法糖。如果需要嵌套多个字段,则使用花括号{}。 -
层次嵌套在理论上是无限的(由URL长度限制),当然,只要遵循tree-ql的语法,无论嵌套多深,都不会出现性能问题。
简单配置
我已经介绍了tree-ql的功能和外部性能。现在让我介绍一下tree-ql的内部配置。首先,让我们看看配置入口文件。
# app/Resources/PostResource <?php namespace App\Resources; use Weiwenhao\TreeQL\Resource; class PostResource extends Resource { protected $default = [ 'id', 'title', 'user_id' ]; protected $columns = [ 'id', 'title', 'user_id' ]; protected $relations = [ 'comments', 'user' ]; protected $custom = [ 'liked' ]; protected $meta = [ ]; }
从前端的角度来看,描述了不可区分的字段,如用户、内容、点赞等,但从后端的角度来看,我们将包含字段分为四种类型。即上述代码段中的四个属性column/relations/custom/meta,涵盖了我们在日常API编写过程中涉及到的所有字段。
columns
是数据库中的列。
何时需要包含列?
从SQL语句的角度来看,select *是一种非常不谨慎的写法,它有不可控的性能问题和字段冗余问题。最常见的例子是我们的帖子内容。通常在获取帖子列表的SQL列表中间,我们不会获取内容。只有在显示帖子详情时才会获取内容。
在这种情况下,过去有一种做法是test.com/api/posts/{post}?fields=id,title,content。但在tree-ql中可以通过include来控制,避免了select *的问题,同时更加一致。
relations
关系是laravel中的关系,对应于Model中的关联方法。
何时需要包含关系?
在帖子列表的场景中。在pc端,由于足够的放置空间,作者信息通常会显示出来。然而,在android/ios端显示位置较小,作者信息通常不会显示。这里的作者属于帖子,即属于用户关系的关联关系。
过去,你可以写一个通用的API(总是携带作者信息)来适应两端,或者写两个API来适应PC和安卓/iOS。现在我们有一个更好的方法,让客户端根据实际情况来决定。你需要携带作者信息吗?
比如:pc: test.com/api/posts?include=user,android/ios: test.com/api/posts
注意:由于用户属于关系,你需要为用户定义相关的资源。默认情况下,PostsResource将关联app/Resources/UserResource。当然,你可以指定用户对应的资源。
protected $relations = [ 'comments', 'user' => CustomUserResource::class, ];
用户资源定义
# UserResource.php <?php namespace App\Resources; use Weiwenhao\TreeQL\Resource; class UserResource extends Resource { protected $default = ['id', 'nickname', 'avatar']; protected $columns = ['id', 'nickname', 'avatar', 'password']; }
以及对应的关系
# Post.php <?php namespace App\Models; class Post extends Model { public function user() { return $this->belongsTo(User::class); } }
通过在线定义,我们可以有一个快乐的include=user
当关系定义中的字段包含多个单词时,
$relations = ['custom_relation']定义了下划线词分割。关联方法使用小驼峰命名法,即customRelation()总结来说,只要在关系中定义了字段,就需要相应的资源和模型关系支持。
自定义
它不存储在数据库中,列是通过一些规则计算得出的。
何时需要包含自定义?
在某些帖子列表的场景中,会显示当前用户是否点赞了该帖子,这是一个性能密集型的计算。
{
"id": 1,
"title": "...",
"liked": true,
}
客户端喜欢这种易于绑定的数据结构,但帖子表中没有喜欢的字段来记录你是否喜欢该帖子。
这时自定义就派上用场了,如上面定义的$custom。但更糟糕的是什么?
protected $custom = [ 'liked' => function ($post) { // logic return $bool; } ];
是的,我们仍然有一个回调和当前登录用户是否喜欢帖子进行判断。但是PHP不支持上述操作。所以tree-ql需要将回调提取出来。
protected $custom = [ 'liked' ]; public function liked($post, $params) { // logic // return $bool; }
这就是自定义的使用,以及回调的使用,客户端只需要传递test.com/api/posts?include=liked就会触发回调,并返回相应的结果。
当自定义定义中的字段包含多个单词时,
$custom = ['custom_test']定义了下划线词分割。回调方法使用小驼峰命名法,即customTest($item, $params)
元数据
必须解释的一点是,tree-ql将数据和元数据包裹在数据的最外层。我认为这是必要的,例如,在分页的场景中,我们可以将分页信息放在元数据中。
{
"data": [
{
"id": 1,
"title": "Aperiam quisquam porro fugiat et in itaque",
"user_id": 1
},
],
"meta": {
"pagination": {
"per_page": 15,
"total": 100,
"current": 1,
"next": "http://test.com/api/posts?page=2",
"previous": null,
"last": 7
}
}
}
同样,我们可以根据业务需求在元数据中定义一些附加信息,并设置回调(与自定义回调操作一致,但回调参数只有params),让客户端包含。
这里的分页不需要主动包含,tree-ql会为你维护这些数据。
当元数据定义中的字段由多个单词组成时,
$meta = ['custom_meta']定义了下划线词分割。回调方法使用小驼峰命名法,即customMeta($params)
默认
默认定义从columns/relations/custom/meta中获取,并且默认包含它,不需要在url中显式包含。
资源嵌套
虽然我们的Resource有4种配置类型,但只有关系类型的字段允许使用.和{}语法进行资源嵌套。
test.com/api/posts?include=comments{user}
参数
这是参数的语法。如上所述,自定义和元数据都有对应的回调函数,因此参数也传递给回调函数。
protected $custom = [ 'liked' ]; protected $meta = [ 'test' ]; /** * @param $post * @param $params ["key1" => "value1", "key2" => "value2"] */ public function liked($post, $params) { // logic // return } /** * @param $params ["key1" => "value1", "key2" => "value2"] */ public function test($params) { // logic // return }
也存在一种关系参数的情况。关系加载由tree-ql负责,没有自定义回调函数。但在加载中,保留相应的回调。
例如,test.com/api/posts/{post}?include=comments(sort_by:floor)然后相应的回调是
# CommentResource.php <?php namespace App\Resources; use Weiwenhao\TreeQL\Resource; class CommentResource extends Resource { protected $default = ['id', 'content', 'user_id', 'floor']; protected $columns = ['id', 'content', 'user_id', 'floor']; protected $relations = [ 'user', 'replies' => [ 'resource' => CommentReplyResource::class, ] ]; /** * test.com/api/posts/{post}?include=comments(sort_by:floor) * * sound code ↓ ↓ ↓ * * $posts->load(['comments' => function ($builder) { * $this->loadConstraint($builder, ['sort_by' => 'floor']); * }); * * ↓ ↓ ↓ * * @param $builder * @param array $params */ public function loadConstraint($builder, $params) { isset($params['sort_by']) && $builder->orderBy($params['sort_by'], 'desc'); } }
已经介绍了include的语法规则以及tree-ql的所有配置规则。尽管介绍过程耗时较长,但大部分空间都用于介绍使用场景,实际的配置非常简单。
使用方法
看一下PostController,tree-ql的使用一目了然。
/** * test.com/api/posts?include=xxx * Just pass the posts to PostResource, and the definitions and configurations are activated!! * * @return \Weiwenhao\TreeQL\Resource */ public function index() { // $posts = Post::columns()->latest()->get(); Same support $posts = Post::columns()->latest()->paginate(); // Equivalent to return PostResource::make($post, request('include')) return PostResource::make($posts); } /** * @param \App\Models\Post $post * @return \Weiwenhao\TreeQL\Resource */ public function show($post) { return $resource = PostResource::make($post); }
这里需要解释的只有column()查询构造函数,这是由tree-ql提供的。它将根据PostResource中定义的列和默认值,结合实际的include,向Builder添加适当的select(),而不是使用select *
许可证
本项目采用MIT许可证授权。






