在 PHP / Laravel 中以抽象的方式包装 JSON 响应



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

有一个端点:example.com/api/v1/collect使用 Laravel 的 API 身份验证来获取具有$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:使用拉拉维尔刀片

// ApiV1ApiController.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 响应宏

// ApiV1ApiController.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);

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

// AppProvidersAppServiceProvider.php
use IlluminateHttpResponse;
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强制转换) - 这意味着我无法使用LaravelAPI资源

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

根据您的要求(两种类型的用户的数据格式不同),您可以创建两个不同的 API 资源类。

AdminResourceUserResource.

在这里,您在控制字段或组织数据方面具有更多的灵活性。

下面介绍了如何定义资源类:

<?php
namespace AppHttpResources;
use IlluminateHttpResourcesJsonResource;
class UserResource extends Resource
{
/**
* Transform the resource into an array.
*
* @param  IlluminateHttpRequest
* @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 AppUser;
use AppHttpResourcesUserResource;
Route::get('/user', function () {
return new UserResource(User::find(1));
});

如果要将条件签入包含在用户类型中,可以创建一个名为renderJson($userType, $data)的通用函数并将其放在父类中,也可以用 traits 包装,这一切都取决于您的应用程序体系结构。

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

编辑:

使用Laravel API 资源,您不仅可以解析模态对象,还可以解析任何可数组对象。

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

因此,在您的情况下,如果您只需collect()json 数据并传递给 api 资源。

您可以使用中间件来更改响应的外观。

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

<?php
namespace AppHttpMiddleware;
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

取决于响应之间的差异。 我倾向于清点每种类型的共同特征,并根据需要构建响应数组。 这可以在控制器或帮助程序函数中完成,然后使用 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);

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

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

让我们开始吧。

首先,用户使用拉拉维尔护照或 api 路由登录。 其次,API 调用控制器。(类)。我将根据您的信息创建一个类。

假设 API 调用ApiController,方法handle


use IlluminateHttpRequest;
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;
}

}

最新更新