【问题标题】:Wrapping JSON response in an abstracted way in PHP / Laravel在 PHP / Laravel 中以抽象的方式包装 JSON 响应
【发布时间】:2019-10-23 22:13:19
【问题描述】:

我正在制作一个 REST API,它将根据正在调用的用户类型返回不同的 JSON 响应。

有一个端点:example.com/api/v1/collect,它使用Laravel's API authentication 来获取带有$user = auth()->guard('api')->user(); 的用户模型。

每个User 都将属于一个Type

如果User 1 (type_id 1) 拨打电话,响应将如下所示:

{
    "example": 1,
    "success": true,
    "data" : [
        ...
    ]
}

如果User 2 (type_id 2) 拨打电话,响应可能会有所不同,具体取决于用户的类型。它可能看起来像:

{
    "example": 2,
    "response" : [
        ...
    ],
    "custom": "string",
    "success": 200
}

... 是我们发回的数据(例如帖子标题列表),它始终是相同的,但它周围的“信封”(或包装)将特定于每个用户(或用户类型)。

到目前为止,我已经找到了两种解决方案来以抽象的方式包装 ...

解决方案 1:使用Laravel Blade

// Api\V1\ApiController.php

$data = $user->posts->pluck('title');

// Each type of user will have a different blade filename
// There could be around a 100 types which will result in a 100 blade files
// The filename is stored in the database
$filename = $user->type->filename; // returns 'customBladeTemplate'

// Return a JSON response after passing the $data to the view
return response()->json([
    view($filename, compact('data'))->render(),
]);

为每种类型的用户使用刀片文件允许我像这样包装数据:

// resources/views/customBladeTemplate.blade.php
// This filename has to match the one in the database column
{
    "example": 1,
    "success": true,
    "data" : [
        {!! $data !!}
    ]
}

这将为用户 1 输出 JSON 响应(示例 1)

解决方案 2:使用Laravel response macros

// Api\V1\ApiController.php

$data = $user->posts->pluck('title');

// Each type of user will have a different macro name
// There could be around a 100 types which will result in a 100 different macros
// The macro name is stored in the database
$macroName = $user->type->macro_name; // returns 'customMacroName'

return response()->{macroName}($data);

使用数据库中的宏名称为每种类型的用户创建一个宏:

// App\Providers\AppServiceProvider.php

use Illuminate\Http\Response;

public function boot()
{
    Response::macro('customMacroName', function ($data) {
        return Response::json([
            'example' => 2,
            'response' => $data,
            'custom' => 'string',
            'success' => 200,
        ]);
    });
}

该宏将为用户 2 输出 JSON 响应(示例 2)


这两个选项都很好,但我仍然想知道:

  • 还有其他(可能更好)方法吗?
  • 这两个解决方案是否有效或可以增强?
  • 这两种解决方案中哪一种似乎更好,为什么?

编辑: $data 实际上并不是来自一个雄辩的模型,而是来自一个序列化的 JSON 列 (JSON casting) - 这意味着我不能使用 Laravel API resources

【问题讨论】:

  • 一开始为什么要这样做?提供基于谁请求的结构似乎有点不自然(而且不是非常 RESTfull)。听起来也像是一场文档噩梦……
  • 我已经简化了可读性的问题,但该应用程序实际上是一个 iPaaS(集成平台),它将来自外部 API(即 Magento)的数据映射到其他 API(即 QuickBooks)。每个 API 都有不同的 JSON 输出/输入,可以是 REST、SOAP 或 WebHooks。我必须使 JSON 输出适应每个连接器,同时尽量避免对有效负载进行硬编码,而是尽可能保持“抽象”。这就是为什么我首先选择刀片模板,但我同意,它并不是真正的 RESTful!我只是想不出任何其他解决方案。

标签: php json laravel response laravel-blade


【解决方案1】:

如果您正在寻找响应格式,您应该使用Laravel API Resources

根据您的需求(两种用户的数据格式不同),您可以创建两个不同的 Api Resource 类。

AdminResource & UserResource.

在这里,您可以更灵活地控制字段或组织数据。

下面是定义资源类的方法:

<?php

namespace App\Http\Resources;

use Illuminate\Http\Resources\Json\Resource;

class UserResource extends Resource
{
    /**
     * Transform the resource into an array.
     *
     * @param  \Illuminate\Http\Request
     * @return array
     */
    public function toArray($request)
    {
        return [
            'id' => $this->id,
            'name' => $this->name,
            'email' => $this->email,
            'created_at' => $this->created_at,
            'updated_at' => $this->updated_at,
        ];
    }
}

您可以将其用作:

use App\User;
use App\Http\Resources\UserResource;

Route::get('/user', function () {
    return new UserResource(User::find(1));
});

如果您想在用户类型中包含条件检查,您可以创建一个名为 renderJson($userType, $data) 的通用函数并将其放置在您的父类中,或者可以使用特征包装,这一切都取决于您的应用程序架构。

在这里你可以找到 API 资源的 laravel 文档:https://laravel.com/docs/5.8/eloquent-resources

已编辑:

使用Laravel API Resource,你不仅可以解析模态对象,还可以解析任意数组对象。

本质上它们只是简单的对象,具有一项非常重要的工作 to do — 变换你的对象(有趣的是我说的是对象而不是 楷模)。要开箱即用,您所要做的就是实例化 具有 Arrayable 对象的资源(集合或个人)。如果 你除了生成一个标准资源并传入一个 Resource 将转换该对象的 Arrayable 对象 自动,因为模型是可排列的,这就是我得到的地方 因为如果你创建一个资源集合并实例化 它带有一组模型,然后模型得到 toArray'd 而不是 他们相应的资源。 源:https://medium.com/@dinotedesco/laravel-api-resources-what-if-you-want-to-manipulate-your-models-before-transformation-8982846ad22c

所以在你的情况下,如果你可以 collect() json 数据并传递给 api 资源。

【讨论】:

  • $data 实际上并不是来自一个雄辩的模型,而是来自一个序列化的 JSON 列——这意味着我不能使用 Laravel API 资源。不过,这是一个很好的电话。我已经编辑了问题。
  • @PhilMarc 在这里,您可以使用 API 资源来解析任何 Arrable 对象,无论是模型还是任何其他标准对象。所以我觉得,你还是可以考虑这个的。
【解决方案2】:

您可以使用middlewares 来更改响应的外观。

使用中间件,您可以在执行常规代码后更改响应,而无需在控制器本身中考虑这一点。使用下面的代码,您可以在响应执行后修改响应。

<?php

namespace App\Http\Middleware;

use Closure;

class AfterMiddleware
{
    public function handle($request, Closure $next)
    {   
        // Calls the controller and processes the request. 
        $response = $next($request); 

        // Here you can retrieve the user and modify the response however you want. 

        // Some example code: 
        $user = Auth::user();

        if ($user->type == 1) {
           ... //Change response for user type 1
        }
        if ($user->type == 2) {
           ... //Change response for user type 2
        }
        // Etc...

        return $response;
    }
}

参考:https://laravel.com/docs/5.8/middleware

【讨论】:

  • 这是一个很好的解决方案。但它不包括 JSON 响应的包装,因此它与第二种解决方案非常相似:每个用户类型有一个宏 / 有一个 if 语句。
  • 这样你就不需要把那个检查放在控制器中了。所以如果在某个时候系统有更多的路由,你只需添加这个中间件就可以了。
【解决方案3】:

取决于响应之间的差异程度。我倾向于盘点每种类型的共同特征,并酌情构建一个响应数组。这可以在控制器或辅助函数中完成,然后使用 Laravel 的 JSON 响应类型返回。

$response = [];

// results common to all types
$response['example'] = $example;
$response['success'] = $success;

// customized results for specific types
if (in_array($type, [1, 3, 4, 5, ...])) {
    $response['data'] = $dotdotdot;
}
if (in_array($type, [2, 6, 7, 8, ...])) {
    $response['response'] = $dotdotdot;
    $response['custom'] = $custom;
}

return response()->json($response);

【讨论】:

    【解决方案4】:

    我不知道这是否是你要找的。几个月前我有类似的东西,并用 json 文件修复了它。由于 json 速度惊人,您可以创建数千种类型。

    抱歉我的英语不好,我会在周末后修复它:-)

    让我们开始吧。

    首先用户使用 laravel 护照或 api 路由登录。 其次,api 调用一个控制器。(类)。我会根据你的信息创建一个类。

    假设 api 调用 ApiController 和方法 handle

    
    use Illuminate\Http\Request;
    
    class ApiController
    {
    
    
        public function __construct()
        {
    
        }
    
        /**
         * Handle the incoming request
         * 
         * @param Request $request
         */
        public function handle(Request $request)
        {
    
            //first let's find the correct format
            $type = $requets->user()->type; //lets say type_1
    
            $config = $this->getUserType($type);
    
            //i don't know where you data comes from but let's say $data is your data.
            $data = json_encode(['some' => "data", 'to' => "return"]);
    
            //lets fill the data
            $response = $this->fillDataInConfig($data, $config);
    
            return response()->json($response);
        }
    
        /**
         * Find the user type config by type name
         * 
         * @param string $type
         * @return object
         */
        private function getUserType(string $type) : string
        {
            //let store them in the local storage
            $config = \Storage::disk('local')->get("api_types/$type.json");
    
            return json_decode($config);
        }
    
        /**
         * Fill the data
         * 
         * @param mixed $data
         * @param object $config
         * @return object
         */
        private function fillDataInConfig($data, object $config) : object
        {
            //as for your example. The reusl//        
    //      {
    //            "example": 2,
    //            "response" : *responseData*, <===== where the response should be
    //            "custom": "string",
    //            "success": 200
    //        }
    
            foreach($config as $index => $value){
                if($value === '*responseData*'){
                    $config->{$idnex} = $data;
                }
            }
            //the data is filled in the response index
    
            return $config;
        }
    
    
    }
    

    【讨论】:

      猜你喜欢
      • 2015-04-13
      • 1970-01-01
      • 1970-01-01
      • 2014-07-27
      • 2017-06-12
      • 2022-01-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多