fuzz/magic-box

此包已被废弃,不再维护。没有建议的替代包。

将 Laravel 的 Eloquent 模型以可注入、掩码的资源库形式进行神奇实现的包。

1.1.2 2017-01-12 20:20 UTC

README

⛔️ 已弃用 ⛔️

此项目不再受支持或维护。如果您需要与 Laravel 新版本兼容的现代 Magic Box 版本,请考虑使用此项目的精神继承者 — Koala Pouch

Magic Box 将 Fuzz 对 Laravel Eloquent 模型的神奇实现模块化,将其作为可注入、掩码的资源库。

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

安装/设置

  1. composer require fuzz/magic-box

  2. Fuzz\MagicBox\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 类的中间件键下设置您的 MagicBox 资源路由。

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

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

测试

composer install 后,只需运行 phpunit

Eloquent Repository

Fuzz\MagicBox\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 的帖子,该 user_id 属于该用户。无意义的 "nonsense" 属性被简单地忽略,因为它实际上不存在于存储用户的表中。

单独的 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. create
  2. read
  3. update
  4. delete
  5. save,它将根据其输入的状态调用 createupdate
  6. find,它将根据 ID 查找模型
  7. findOrFail,它将根据 ID 查找模型或抛出 \Illuminate\Database\Eloquent\ModelNotFoundException

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

  1. all

筛选

Fuzz\MagicBox\Filter 处理基于通过 filters 参数传入的筛选值对 Eloquent 查询构建器的修改。

标记和用法

标记 描述 示例
^ 字段以...开头 https://api.yourdomain.com/1.0/users?filters[name]=^John
$ 字段以...结尾 https://api.yourdomain.com/1.0/users?filters[name]=$Smith
~ 字段包含 https://api.yourdomain.com/1.0/users?filters[favorite_cheese]=~cheddar
< 字段小于 https://api.yourdomain.com/1.0/users?filters[lifetime_value]=<50
> 字段大于 https://api.yourdomain.com/1.0/users?filters[lifetime_value]=>50
>= 字段大于等于 https://api.yourdomain.com/1.0/users?filters[lifetime_value]=>=50
<= 字段小于等于 https://api.yourdomain.com/1.0/users?filters[lifetime_value]=<=50
= 字段等于 https://api.yourdomain.com/1.0/users?filters[username]==Specific%20Username
!= 字段不等于 https://api.yourdomain.com/1.0/users?filters[username]=!=common%20username
[...] 字段是一个或多个 https://api.yourdomain.com/1.0/users?filters[id]=[1,5,10]
![...] 字段不是其中一个 https://api.yourdomain.com/1.0/users?filters[id]=![1,5,10]
NULL 字段为NULL https://api.yourdomain.com/1.0/users?filters[address]=NULL
NOT_NULL 字段不为NULL https://api.yourdomain.com/1.0/users?filters[email]=NOT_NULL

筛选关系

假设我们有用户和相关表的结构如下

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

我们可以通过 users?filters[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',
          ]	
    ]
]

这个筛选可以读作 select (username为Bobby的用户) OR (用户名Johnny且其profile.favorite_cheese属性为Gouda的用户)

模型设置

模型需要在 MagicBox 允许它们作为 MagicBox 资源公开之前实现 Fuzz\MagicBox\Contracts\MagicBoxResource。这样做是为了使公开过程是显式的,并且不会公开不需要的内容。

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

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

MagicBox 只会修改显式定义的属性/关系。

解析模型

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

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

测试

phpunit :)

待办事项

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