Обтекание JSON-ответа абстрактно в PHP / Laravel - PullRequest
1 голос
/ 09 июня 2019

Я создаю REST API, который будет возвращать разные ответы JSON в зависимости от того, какой тип пользователя выполняет вызов.

Существует одна конечная точка: example.com/api/v1/collect, которая использует Аутентификация API Laravel для получения пользовательской модели с $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 !!}
    ]
}

Выводит ответ JSON для пользователя 1 (пример 1)

Решение 2 : Использование макросов ответа Laravel

// 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,
        ]);
    });
}

Этот макрос выведет ответ JSON для пользователя 2 (пример 2)


Оба варианта работают нормально, но мне все еще интересно:

  • Есть еще один ( возможно лучше ) wможно сделать это?
  • Являются ли эти два решения допустимыми или могут быть улучшены?
  • Какое из этих двух решений кажется лучше и почему?

Редактировать: На самом деле $data на самом деле не из красноречивой модели, а скорее из сериализованного столбца JSON ( приведение JSON ) - что означает, что я могуt ресурсы API Laravel

Ответы [ 4 ]

3 голосов
/ 12 июня 2019

Если вы ищете форматирование ответа, вы должны пойти с Laravel API Resources

Исходя из ваших требований (формат данных различен для двух типов пользователей), вы можете создать два разных класса ресурсов API.

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 для ресурса API: https://laravel.com/docs/5.8/eloquent-resources

Отредактировано:

С Laravel API Resource вы не только анализируете модальный объект, вы можете анализировать любой массивный объект.

По сути, это просто простые объекты с одной очень важной работой делать - трансформировать ваши объекты (интересные, я сказал, объекты, а не модели). Чтобы сделать это из коробки, все, что вам нужно сделать, это создать экземпляр Ресурс (коллекционный или индивидуальный) с объектом Arrayable. Если Вы ничего не сделали, кроме генерации стандартного ресурса и передачи в Объект Arrayable, который Ресурс преобразовал бы этот объект автоматически, и потому что модели являются массивами, это где я получил пойман, потому что если вы создаете коллекцию ресурсов и создавать экземпляры это с коллекцией моделей, то модели попадают в Array'd, а не их соответствующий ресурс. Src: https://medium.com/@dinotedesco/laravel-api-resources-what-if-you-want-to-manipulate-your-models-before-transformation-8982846ad22c

Так что в вашем случае, если вы можете просто collect() данные json и передать ресурс API.

1 голос
/ 11 июня 2019

Вы можете использовать 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

0 голосов
/ 14 июня 2019

Я не знаю, если это то, что вы ищете.У меня было что-то похожее несколько месяцев назад, и я исправил это с помощью файлов 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;
    }


}
0 голосов
/ 11 июня 2019

Зависит от того, насколько разные ответы отличаются друг от друга.Я склонен проводить инвентаризацию общих характеристик каждого типа и создавать массив ответов по мере необходимости.Это может быть сделано в контроллере или вспомогательной функции, а затем возвращено с использованием типа ответа LSONvel 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);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...