承接 koala-labs/pouch 相关项目开发

从需求分析到上线部署,全程专人跟进,保证项目质量与交付效率

邮箱:yvsm@zunyunkeji.com | QQ:316430983 | 微信:yvsm316

koala-labs/pouch

Composer 安装命令:

composer require koala-labs/pouch

包简介

A magical implementation of Laravel's Eloquent models as injectable, masked resource repositories.

README 文档

README

Koala Pouch is a fork of Fuzz Production's Magic Box

Koala Pouch has two goals:
  1. To create a two-way interchange format, so that the JSON representations of models broadcast by APIs can be re-applied back to their originating models for updating existing resources and creating new resources.
  2. Provide an interface for API clients to request exactly the data they want in the way they want.

Installation/Setup

  1. composer require koala/pouch

  2. Use or extend Koala\Pouch\Middleware\RepositoryMiddleware into your project and register your class under the $routeMiddleware array in app/Http/Kernel.php. RepositoryMiddleware contains a variety of configuration options that can be overridden

  3. If you're using fuzz/api-server, you can use magical routing by updating app/Providers/RouteServiceProvider.php, RouteServiceProvider@map, to include:

    /**
     * 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. Set up your Pouch resource routes under the middleware key you assign to your chosen RepositoryMiddleware class

  5. Set up a YourAppNamespace\Http\Controllers\ResourceController, here is what a ResourceController might look like .

  6. Set up models according to Model Setup section

Testing

Just run phpunit after you composer install.

Eloquent Repository

Koala\Pouch\EloquentRepository implements a CRUD repository that cascades through relationships, whether or not related models have been created yet.

Consider a simple model where a User has many Posts. EloquentRepository's basic usage is as follows:

Create a User with the username Steve who has a single Post with the title Stuff.

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

$user = $repository->save();

When $repository->save() is invoked, a User will be created with the username "Steve", and a Post will be created with the user_id belonging to that User. The nonsensical "nonsense" property is simply ignored, because it does not actually exist on the table storing Users.

A limitation on Eloquent HasManyThrough relationships:

Inputs that define a model related through a HasManyThrough relationship must refer to an already existing model. In this example, a User has many Reactions through a Post. The Reaction model must exist and be related to a Post in order to correctly relate to the User.

$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();

A workaround for new Reaction and Post models attached to a User involves nesting the Reaction model data under the related 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();

Support for new models defined through a HasThroughMany relationship will come soon.

By itself, EloquentRepository is a blunt weapon with no access controls that should be avoided in any public APIs. It will clobber every relationship it touches without prejudice. For example, the following is a BAD way to add a new Post for the user we just created.

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

This will delete poor Steve's first post—not the intended effect. The safe(r) way to append a Post would be either of the following:

$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();

Generally speaking, the latter is preferred and is less likely to explode in your face.

The public API methods that return models from a repository are:

  1. create
  2. read
  3. update
  4. delete
  5. save, which will either call create or update depending on the state of its input
  6. find, which will find a model by ID
  7. findOrFail, which will find a model by ID or throw \Illuminate\Database\Eloquent\ModelNotFoundException

The public API methods that return an \Illuminate\Database\Eloquent\Collection are:

  1. all

Filtering

Koala\Pouch\Filter handles Eloquent Query Builder modifications based on filter values passed through the filters parameter.

Tokens and usage:

Token Description Example
^ Field starts with https://api.yourdomain.com/1.0/users?filters[name]=^John
$ Field ends with https://api.yourdomain.com/1.0/users?filters[name]=$Smith
~ Field contains https://api.yourdomain.com/1.0/users?filters[favorite_cheese]=~cheddar
< Field is less than https://api.yourdomain.com/1.0/users?filters[lifetime_value]=<50
> Field is greater than https://api.yourdomain.com/1.0/users?filters[lifetime_value]=>50
>= Field is greater than or equals https://api.yourdomain.com/1.0/users?filters[lifetime_value]=>=50
<= Field is less than or equals https://api.yourdomain.com/1.0/users?filters[lifetime_value]=<=50
= Field is equal to https://api.yourdomain.com/1.0/users?filters[username]==Specific%20Username
!= Field is not equal to https://api.yourdomain.com/1.0/users?filters[username]=!=common%20username
[...] Field is one or more of https://api.yourdomain.com/1.0/users?filters[id]=[1,5,10]
![...] Field is not one of https://api.yourdomain.com/1.0/users?filters[id]=![1,5,10]
NULL Field is null https://api.yourdomain.com/1.0/users?filters[address]=NULL
NOT_NULL Field is not null https://api.yourdomain.com/1.0/users?filters[email]=NOT_NULL

Filtering by relations

Assuming we have users and their related tables resembling the following structure:

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

We can filter users by their hobbies with users?filters[profile.hobbies.name]=^Cook.

This filter can be read as select users with whose profile.hobbies.name begins with "Cook"

Relationships can be of arbitrary depth.

Filter conjunctions

We can use AND and OR statements to build filters such as users?filters[username]==Bobby&filters[or][username]==Johnny&filters[and][profile.favorite_cheese]==Gouda. The PHP array that's built from this filter is:

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

and this filter can be read as select (users with username Bobby) OR (users with username Johnny whose profile.favorite_cheese attribute is Gouda).

Other Parameters

Pick

We can limit the amount of data that comes back with your query by adding pick to the URL. Usage:

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

Model Setup

Models need to implement Koala\Pouch\Contracts\PouchResource before Pouch will allow them to be exposed as a Pouch resource. This is done so exposure is an explicit process and no more is exposed than is needed.

Models also need to define their own $fillable array including attributes and relations that can be filled through this model. For example, if a User has many posts and has many comments but an API consumer should only be able to update comments through a user, the $fillable array would look like:

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

Pouch will only modify attributes/relations that are explicitly defined.

Resolving models

Pouch is great and all, but we don't want to resolve model classes ourselves before we can instantiate a repository...

If you've configured a RESTful URI structure with pluralized resources (i.e. https://api.mydowmain.com/1.0/users maps to the User model), you can use Koala\Pouch\Utility\Modeler to resolve a model class name from a route name.

Testing

phpunit :)

TODO

  1. Route service provider should be pre-setup
  2. Support more relationships (esp. polymorphic relations) through cascading saves.
  3. Support paginating nested relations

koala-labs/pouch 适用场景与选型建议

koala-labs/pouch 是一款 基于 PHP 开发的 Composer 扩展包,目前已累计 17.59k 次下载、GitHub Stars 达 7, 最近一次更新时间为 2021 年 11 月 23 日, 在 PHP 生态内属于活跃度较高的组件。

我们在过去多个企业项目中使用过 koala-labs/pouch 或与其功能相近的方案,如果你在选型或落地过程中遇到问题,例如 版本兼容、二次改造、私有化封装、与内部系统对接、生产 BUG 排查,欢迎联系我们协助评估。

围绕 koala-labs/pouch 我们能提供哪些服务?
定制开发 / 二次开发

基于 koala-labs/pouch 在你已有业务上做功能扩展、字段裁剪、UI 适配、与内部账号 / 权限 / 日志系统的深度对接。

BUG 修复 & 性能优化

线上偶发问题、内存泄漏、慢查询、并发异常等排查修复;针对高流量场景做缓存、队列、索引层面的调优。

项目外包 & 长期维护

承接完整的项目从需求 → 设计 → 开发 → 上线 → 长期运维;也可按月提供技术保姆服务。

yvsm@zunyunkeji.com QQ:316430983 微信:yvsm316 西安尊云信息科技 · 专注 PHP / Go / 分布式系统研发

统计信息

  • 总下载量: 17.59k
  • 月度下载量: 0
  • 日度下载量: 0
  • 收藏数: 7
  • 点击次数: 10
  • 依赖项目数: 0
  • 推荐数: 0

GitHub 信息

  • Stars: 7
  • Watchers: 0
  • Forks: 4
  • 开发语言: PHP

其他信息

  • 授权协议: MIT
  • 更新时间: 2021-11-23