koala-labs/pouch

将 Laravel 的 Eloquent 模型作为可注入、掩码资源仓库的神奇实现。

3.2.1 2023-04-25 18:14 UTC

This package is auto-updated.

Last update: 2024-09-25 21:48:05 UTC


README

Koala Pouch 是 Fuzz Production 的 Magic Box 的分支。

Koala Pouch 有两个目标
  1. 创建一个双向交换格式,以便 API 传输的模型的 JSON 表示可以重新应用到原始模型上,以更新现有资源和创建新资源。
  2. 为 API 客户端提供一个接口,以便它们可以以他们想要的方式请求确切的数据。

安装/设置

  1. composer require koala/pouch

  2. Koala\Pouch\Middleware\RepositoryMiddleware 用于您的项目,并在 app/Http/Kernel.php 中的 $routeMiddleware 数组下注册您的类。RepositoryMiddleware 包含可以覆盖的各种配置选项。

  3. 如果您正在使用 fuzz/api-server,可以通过更新 app/Providers/RouteServiceProvider.php 中的 RouteServiceProvider@map 来使用魔法路由,包括:

    /**
     * Define the routes for the application.
     *
     * @param  \Illuminate\Routing\Router $router
     * @return void
     */
    public function map(Router $router)
    {
        // Register a handy macro for registering resource routes
        $router->macro('restful', function ($model_name, $resource_controller = 'ResourceController') use ($router) {
            $alias = Str::lower(Str::snake(Str::plural(class_basename($model_name)), '-'));
    
            $router->resource($alias, $resource_controller, [
                'only' => [
                    'index',
                    'store',
                    'show',
                    'update',
                    'destroy',
                ],
            ]);
        });
    
        $router->group(['namespace' => $this->namespace], function ($router) {
            require app_path('Http/routes.php');
        });
    }
  4. 在分配给您的 RepositoryMiddleware 类的中间件键下设置您的 Pouch 资源路由。

  5. 设置一个 YourAppNamespace\Http\Controllers\ResourceController这里是一个 ResourceController 的示例

  6. 根据 Model Setup 部分设置模型。

测试

在执行 composer install 后,只需运行 phpunit

Eloquent 仓库

Koala\Pouch\EloquentRepository 实现了一个 CRUD 仓库,它通过关系级联,无论相关模型是否已创建。

考虑一个简单的模型,其中用户有多个帖子。EloquentRepository 的基本用法如下:

创建一个用户,用户名为 Steve,他有一个标题为 Stuff 的帖子。

$repository = (new EloquentRepository)
    ->setModelClass('User')
    ->setInput([
        'username' => 'steve',
        'nonsense' => 'tomfoolery',
        'posts'    => [
            'title' => 'Stuff',
        ],
    ]);

$user = $repository->save();

当调用 $repository->save() 时,将创建一个用户,用户名为 "Steve",并创建一个具有属于该用户的 user_id 的帖子。无意义的 "nonsense" 属性被简单地忽略,因为它实际上并不存在于存储用户的表中。

Eloquent HasManyThrough 关系的限制

定义通过 HasManyThrough 关系关联的模型的输入必须引用一个已经存在的模型。在这个例子中,一个 User 通过一个 Post 有多个 Reaction。为了正确关联到 UserReaction 模型必须存在并且与一个 Post 关联。

$repository = (new EloquentRepository)
    ->setModelClass('User')
    ->setInput([
        'username' => 'steve',        
        'posts'    => [
            'title' => 'Stuff',
        ],
        'reactions' => [
            [
                'id': 1 //The Reaction model must already exist, and relate to a Post
            ]
        ]
    ]);

$user = $repository->save();

对于附加到 User 的新 ReactionPost 模型的解决方案是,将 Reaction 模型数据嵌套在相关的 Post 下。

$repository = (new EloquentRepository)
    ->setModelClass('User')
    ->setInput([
        'username' => 'steve',        
        //The Post is related to the User, and the Reaction is related to the Post. User Reactions are related through the Post.
        'posts'    => [
            'title' => 'Stuff',
            'reactions' => [ 
                [
                    'name' => 'John Doe',
                    'icon' => 'thumbs-up'
                ]
            ]
        ],
        
    ]);

$user = $repository->save();

将很快支持通过 HasThroughMany 关系定义的新模型。

仅就其本身而言,EloquentRepository 是一把没有访问控制的钝器,在任何公共 API 中都应该避免使用。它会无偏见地覆盖它接触到的每一个关系。例如,以下是一个向刚刚创建的用户添加新帖子的不良方式。

$repository
    ->setInput([
        'id' => $user->id,
        'posts'    => [
            ['title' => 'More Stuff'],
        ],
    ])
    ->save();

这将删除可怜的 Steve 的第一条帖子——这不是预期的效果。安全(更安全)地添加帖子的一种方式是以下两种之一

$repository
    ->setInput([
        'id' => $user->id,
        'posts'    => [
            ['id' => $user->posts->first()->id],
            ['title' => 'More Stuff'],
        ],
    ])
    ->save();
$post = $repository
    ->setModelClass('Post')
    ->setInput([
        'title' => 'More Stuff',
        'user' => [
            'id' => $user->id,
        ],
    ])
    ->save();

一般来说,后者更受欢迎,并且不太可能在你面前爆炸。

从仓库返回模型的公共 API 方法有

  1. 创建
  2. 读取
  3. 更新
  4. 删除
  5. save,根据输入状态将调用createupdate
  6. find,通过ID查找模型
  7. findOrFail,通过ID查找模型,如果未找到则抛出\Illuminate\Database\Eloquent\ModelNotFoundException

返回\Illuminate\Database\Eloquent\Collection的公共API方法有

  1. all

过滤

Koala\Pouch\Filter根据通过filters参数传递的过滤值处理Eloquent查询构建器的修改。

令牌和用法

通过关系过滤

假设我们有用户及其相关的表格,结构如下

[
    'username'         => 'Bobby',
    'profile' => [
        'hobbies' => [
            ['name' => 'Hockey'],
            ['name' => 'Programming'],
            ['name' => 'Cooking']
        ]
    ]
]

我们可以使用users?filters[profile.hobbies.name]=^Cook通过爱好过滤用户。

此过滤器可以读作选择其profile.hobbies.name以"Cook"开头的用户

关系可以是任意深度。

过滤连接词

我们可以使用ANDOR语句构建过滤器,例如users?filters[username]==Bobby&filters[or][username]==Johnny&filters[and][profile.favorite_cheese]==Gouda。从此过滤器构建的PHP数组是

[
    'username' => '=Bobby',
    'or'       => [
          'username' => '=Johnny',
          'and'      => [
              'profile.favorite_cheese' => '=Gouda',
          ]
    ]
]

此过滤器可以读作选择(具有用户名Bobby的用户)或(具有用户名Johnny且其profile.favorite_cheese属性为Gouda的用户)

其他参数

选择

我们可以在URL中添加pick来限制查询返回的数据量。用法

  • https://api.yourdomain.com/1.0/users?pick=id,username,occupation
  • https://api.yourdomain.com/1.0/users?pick[]=id&pick[]=username&pick[]=occupation

模型设置

模型需要在Pouch允许它们作为Pouch资源暴露之前实现Koala\Pouch\Contracts\PouchResource接口。这样做是为了使暴露成为一个显式的过程,并且不会暴露比所需更多的内容。

模型还需要定义自己的$fillable数组,包括可以通过此模型填充的属性和关系。例如,如果用户有多个帖子且有多个评论,但API消费者应该只能通过用户更新评论,则$fillable数组将如下所示

protected $fillable = ['username', 'password', 'name', 'comments'];

Pouch将仅修改显式定义的属性/关系。

解析模型

Pouch很棒,但我们不想在实例化存储库之前自己解析模型类...

如果您已配置了一个具有复数资源的RESTful URI结构(即https://api.mydowmain.com/1.0/users映射到User模型),则可以使用Koala\Pouch\Utility\Modeler从路由名称解析模型类名称。

测试

phpunit :)

待办事项

  1. 路由服务提供商应预先设置
  2. 通过级联保存支持更多关系(特别是多态关系)
  3. 支持分页嵌套关系