Laravel 5.6: настройка мета-страницы коллекции ресурсов и атрибутов ссылок

Как я могу настроить метаданные Laravel ResourceCollection и информацию о ссылках.

Ссылки должны включать только prev,next и self вместо first,last,prev,next, то есть по умолчанию.

Мета должна включать информацию пагинации, такую ​​как: current_page, total_items, items_per_page, total_pages вместо current_page, from, last_page, path, per_page, to, total.

Вот как мета и информация о ссылках теперь выглядит в ответе JSON:

"meta": {
    "currentPage": 2,
    "current_page": 1,
    "from": 1,
    "last_page": 3,
    "path": "http://localhost:8000/api",
    "per_page": 5,
    "to": 5,
    "total": 14
},
"links": {
    "self": "http://localhost:8000/api",
    "first": "http://localhost:8000/api?page=1",
    "last": "http://localhost:8000/api?page=3",
    "prev": null,
    "next": "http://localhost:8000/api?page=2"
}

.. Я хочу, чтобы это было что-то вроде:

"meta": {
    "current_page": 1,
    "total_items": 15,
    "per_page": 5,
    "total_pages": 3
},
"links": {
    "prev": null,
    "next": "http://localhost:8000/api?page=2"
    "self": "http://localhost:8000/api",
}

1 ответ

Я не был фанатом того, как в Laravel реализованы нумераторы страниц и ресурсы, так как сложно делать определенные вещи, такие как проблема, которую вы упомянули.

Внутренности

Прежде чем вы сможете настроить ваши ответы так, как вы хотите, вам сначала нужно понять, как ResourceCollections преобразуются в ответы.

Оригинал toResponse Метод для сбора ресурсов выглядит так:

public function toResponse($request)
{
    return $this->resource instanceof AbstractPaginator
                ? (new PaginatedResourceResponse($this))->toResponse($request)
                : parent::toResponse($request);
}

Если вы посмотрите дальше в PaginatedResourceResponse класс вы увидите следующий код.

...
protected function paginationLinks($paginated)
{
    return [
        'first' => $paginated['first_page_url'] ?? null,
        'last' => $paginated['last_page_url'] ?? null,
        'prev' => $paginated['prev_page_url'] ?? null,
        'next' => $paginated['next_page_url'] ?? null,
    ];
}
...

protected function meta($paginated)
{
    return Arr::except($paginated, [
        'data', 
        'first_page_url',
        'last_page_url',
        'prev_page_url',
        'next_page_url',
    ]);
}

Я рекомендую прочитать Illuminate\Http\Resources\Json\PaginatedResourceResponse а также Illuminate\Http\Resources\Json\ResourceResponse чтобы полностью понять, что происходит.

Решение 1. Создайте пользовательский PaginatedResourceResponse.

Одним из решений является создание нового класса, который расширяет PaginatedResourceResponseи переопределить paginationLinks метод.

Так это выглядит примерно так:

use Illuminate\Http\Resources\Json\PaginatedResourceResponse;

class CustomPaginatedResourceResponse extends PaginatedResourceResponse
{
    protected function paginationLinks($paginated)
    {
        return [
            'prev' => $paginated['prev_page_url'] ?? null,
            'next' => $paginated['next_page_url'] ?? null,
        ];
    }

    protected function meta($paginated)
    {
        $metaData = parent::meta($paginated);
        return [
            'current_page' => $metaData['current_page'] ?? null,
            'total_items' => $metaData['total'] ?? null,
            'per_page' => $metaData['per_page'] ?? null,
            'total_pages' => $metaData['total'] ?? null,
        ];
    }
}

Тогда вы можете переопределить ваш toResponse способ выглядеть примерно так:

public function toResponse($request)
{
    return $this->resource instanceof AbstractPaginator
                ? (new CustomPaginatedResourceResponse($this))->toResponse($request)
                : parent::toResponse($request);
}

Вы можете рассмотреть возможность переопределения других методов, если хотите дополнительно настроить свой ответ.

Решение 2: переопределить toResponse в ResourceCollection

Вместо переопределения PaginatedResourceResponseВы можете просто переопределить toResponse метод в ResourceCollection с облегченной версией аналогичного кода, например, так:

public function toResponse($request)
{
    $data = $this->resolve($request);
    if ($data instanceof Collection) {
        $data = $data->all();
    }

    $paginated = $this->resource->toArray();
    // perform a dd($paginated) to see how $paginated looks like

    $json = array_merge_recursive(
        [
            self::$wrap => $data
        ],
        [
            'links' => [
                'first' => $paginated['first_page_url'] ?? null,
                'last' => $paginated['last_page_url'] ?? null,
                'prev' => $paginated['prev_page_url'] ?? null,
                'next' => $paginated['next_page_url'] ?? null,
            ],
            'meta' => [
                'current_page' => $metaData['current_page'] ?? null,
                'total_items' => $metaData['total'] ?? null,
                'per_page' => $metaData['per_page'] ?? null,
                'total_pages' => $metaData['total'] ?? null,
            ],
        ],
        $this->with($request),
        $this->additional
    );

    $status = $this->resource instanceof Model && $this->resource->wasRecentlyCreated ? 201 : 200;

    return response()->json($json, $status);
}

Решение 3: переопределить withResponse метод

Более простой, но, возможно, менее мощный вариант - просто переопределить withResponse в коллекции ресурсов вот так:

public function withResponse($request, $response)
{
    $data = $response->getData(true);
    $prev = $data['links']['prev'];
    $next = $data['links']['next'];
    $self = $data['links']['self'];
    $data['links'] = compact('prev', 'next', 'self');
    $response->setData($data);
}
Другие вопросы по тегам