为定义你的 Web 服务增添优雅

0.0.2 2016-08-02 14:35 UTC

This package is auto-updated.

Last update: 2024-08-29 04:19:53 UTC


README

Build Status

用于有效地结合不同的数据源以创建可管理的 Web 服务响应。

实验性

这个库非常实验性,请在自己的风险下使用!

问题

在管理 RESTful API 时,你希望响应是一致的并且可组合的,例如获取一个用户

{
    "id": 1,
    "name": "John",
    "friend_count": 30
}

这通常需要获取用户并添加 friend_count。虽然对于单个用户来说这是可管理的,但当你想在其他对象中包含一个 user 对象时,你必须确保这些字段被包括以保持一致性。

随着你的 API 的增长,更难看到每个对象是如何组合的,这正是 Dug 发挥作用的地方。

Dug 允许你指向提供某些数据的来源,每个来源都可以相互引用并组合他们的数据。

基本用法

首先,我们设置一个 dug

$dug = new Dug();

然后注册你的第一个数据获取来源,它将从数据库中检索一些用户。注意,$path[1] 包含一个与 /[0-9]+/ 匹配的结果数组,因此你可以组合查询。

$source = Source::build(['users', '/[0-9]+/'], function($path) {
    $users = User::whereIn('id', $path[1])->get();
    
    $result = [];
    foreach($users as $user) {
        $result[] = Data::build(
            ['users', $user->getId()],
            [
               'id' => $user->getId(),
               'name' => $user->getName()
            ]
        );
    }
    
    return $result;
});

要获取数据,你可以从一个来源请求数据

$dug->fetch(['users', [1,2]]);

这将导致以下数组

[
    [
        "id' => 1,
        "name' => "Name of user 1",
    ],
    [
        "id' => 2,
        "name' => "Name of user 2",
    ]
]

组合

让我们以基本用法的例子为基础,但在这个例子中我们添加了未读计数

$source = Source::build(['users', '/[0-9]+/'], function($path) {
    $users = User::whereIn('id', $path[1])->get();
    
    $result = [];
    foreach($users as $user) {
        $result[] = Data::build(
            ['users', $user->getId()],
            [
               'id' => $user->getId(),
               'name' => $user->getName()
           ]
       );
    }
    
    $unreadCounts = Counters::whereIn('user_id', $path[1])->get();
    foreach($unreadCounts as $unreadCount) {
        $result[] = Data::build(
            ['users', $unreadCount->getUserId()],
            [
               'unread_count' => $unreadCount->getValue()
            ]
        );
    }
    
    return $result;
});

由于 Data::build 的第一个参数(来源)对于获取用户数据和获取未读计数都是相同的,例如 ['users', 1]。Dug 知道它可以组合这些结果。

[
    [
        "id' => 1,
        "name" => "Name of user 1",
        "unread_count" => 123
    ],
    [
        "id' => 2,
        "name" => "Name of user 2",
        "unread_count" => 0
    ]
]

引用

现在假设我们有属于公司的用户,因此你将有一个名为 companies/1 的端点来获取单个公司,但你也将有一个名为 users/2 的端点来获取单个用户。由于每个用户只有一个公司,且公司的规范变化不大,你可能希望将公司直接包含在用户对象中,如下所示

[
    "id" => 2,
    "name" => "John",
    "company" => {
        "id" => 1,
        ...
    }
]

现在出现了一个问题,你有一个在你的 API 中公司外观的定义,但你想在 companies/1users/1 端点之间保持它们同步。这就是引用发挥作用的地方。

$source = Source::build(['users', '/[0-9]+/'], function($path) {
    $users = User::whereIn('id', $path[1])->get();
    
    $result = [];
    foreach($users as $user) {
        $result[] = Data::build(
            ['users', $user->getId()],
            [
               'id' => $user->getId(),
               'name' => $user->getName(),
               'company' => new ReferenceToSingle(['companies', $user->getCompany()])
           ]
    }
   
    return $result;
});

这假设你已经注册了另一个来源 ['companies', '/[0-9]+/'],它将返回公司。Dug 将通过从这个来源获取数据并合并到用户对象中解决引用。

由于 Dug 首先合并所有数据,它还会合并所有引用以检查是否可以组合对 ['companies', '/[0-9]+/'] 来源的调用,假设你获取了多个用户和公司。这意味着它只会对 ['companies', '/[0-9]+/'] 来源进行一次往返!

依赖注入

当你的 API 增长时,你不想在闭包中完成所有事情,为此我们有 DataProvider 类。例如

class UserProvider implements DataProvider {
    public function handle(array $path):array
        //Your magic
    }
}

你可以通过类名将其注册为一个来源

Source::build(['users', '/[0-9]+/'], UserProvider::class);

默认情况下,Dug 将简单地创建这个类的实例,但是如果你有一个依赖注入框架,你可能想使用它来初始化类并向构造函数注入内容。这可以通过 ClassInitializer 来完成

class LaravelClassInitializer implements ClassInitializer {
    public function inititialize(string $className) {
        return app($className); 
    }
}

$dug = new Dug();
$dug->setClassInitializer(new LaravelClassInitializer());

许可证

版权 2016 Label305 B.V.

根据 Apache 许可证 2.0 版(“许可证”);除非遵守本许可证,否则您不得使用此文件。您可以在以下位置获取许可证副本:

https://apache.ac.cn/licenses/LICENSE-2.0

除非适用法律要求或书面同意,否则根据本许可证分发的软件按“现状”分发,不提供任何形式的明示或暗示保证。有关许可证的权限和限制的具体语言,请参阅许可证。