weiwenhao/tree-ql

laravel 包含

0.6.3-alpha 2019-06-03 08:35 UTC

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=userandroid/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许可证授权。