将 laravel 中的 JsonResponse 进行封装,方便在 api 接口开发时返回固定格式的相应数据。

<?php

namespace AppTraits;

use IlluminateContractsSupportArrayable;
use IlluminateHttpExceptionsHttpResponseException;
use IlluminateHttpJsonResponse;
use IlluminateHttpResourcesJsonJsonResource;
use IlluminateHttpResourcesJsonResourceCollection;
use IlluminatePaginationAbstractPaginator;
use IlluminateSupportArr;
use IlluminateSupportFacadesConfig;

trait JsonResponseTrait
{

    /**
     * 正确
     *
     * @param  string  $message
     * @param  int  $code
     * @param  array  $headers
     * @param  int  $option
     * @return JsonResponse
     */
    public function ok(string $message = '', int $code = 200, array $headers = [], int $option = 0): JsonResponse
    {
        return $this->success([], $message, $code, $headers, $option);
    }
    /**
     *  请求已接受响应 状态码 202
     *
     * @param  null  $data
     * @param  string  $message
     * @param  string  $location
     * @return JsonResponse|JsonResource
     */
    public function accepted($data = null, string $message = '', string $location = '')
    {
        $response = $this->success($data, $message, 202);
        if ($location) {
            $response->header('Location', $location);
        }

        return $response;
    }

    /**
     * 已创建响应 状态码 201
     *
     * @param  null  $data
     * @param  string  $message
     * @param  string  $location
     * @return JsonResponse|JsonResource
     */
    public function created($data = null, string $message = '', string $location = '')
    {
        $response = $this->success($data, $message, 201);
        if ($location) {
            $response->header('Location', $location);
        }

        return $response;
    }

    /**
     * 无内容响应 状态码 204
     *
     * @param  string  $message
     * @return JsonResponse|JsonResource
     */
    public function noContent(string $message = '')
    {
        return $this->success(null, $message, 204);
    }

    /**
     * 错误请求响应 状态码 400
     *
     * @param  string|null  $message
     */
    public function errorBadRequest(?string $message = ''): void
    {
        $this->fail($message, 400);
    }

    /**
     * 请求未认证响应 状态码 401
     *
     * @param  string  $message
     */
    public function errorUnauthorized(string $message = ''): void
    {
        $this->fail($message, 401);
    }

    /**
     * 请求被禁止响应 状态码 403
     *
     * @param  string  $message
     */
    public function errorForbidden(string $message = ''): void
    {
        $this->fail($message, 403);
    }

    /**
     * 资源未找到响应 状态码 404
     *
     * @param  string  $message
     */
    public function errorNotFound(string $message = ''): void
    {
        $this->fail($message, 404);
    }

    /**
     * 方法不允许响应 状态码 405
     *
     * @param  string  $message
     */
    public function errorMethodNotAllowed(string $message = ''): void
    {
        $this->fail($message, 405);
    }

    /**
     * 服务器错误响应 状态码 500
     *
     * @param  string  $message
     */
    public function errorInternal(string $message = ''): void
    {
        $this->fail($message);
    }


    /**
     * 权限认证错误:未注册 状态码 40101
     *
     * @param  string  $message
     */
    public function errorUnregistered(string $message = ''): void
    {
        $this->fail($message, 40101);
    }

    /**
     * 失败响应
     *
     * @param  string  $message
     * @param  int  $code
     * @param  array|null  $errors
     * @param  array  $header
     * @param  int  $options
     *
     * @return JsonResponse
     * @throws HttpResponseException
     */
    public function fail(string $message = '', int $code = 500, mixed $errors = null, array $header = [], int $options = 0)
    {
        $response = $this->response(
            $this->formatData(null, $message, $code, $errors),
            Config::get('response.error_code') ?: $code,
            $header,
            $options
        );

        if (is_null($errors)) {
            $response->throwResponse();
        }

        return $response;
    }

    /**
     * 成功响应
     *
     * @param  JsonResource|array|null|mixed  $data
     * @param  string  $message
     * @param  int  $code
     * @param  array  $headers
     * @param  int  $option
     *
     * @return JsonResponse|JsonResource
     */
    public function success($data = null, string $message = '', int $code = 200, array $headers = [], int $option = 0)
    {
        if ($data instanceof ResourceCollection) {
            return $this->formatResourceCollectionResponse(...func_get_args());
        }

        if ($data instanceof JsonResource) {
            return $this->formatResourceResponse(...func_get_args());
        }

        if ($data instanceof AbstractPaginator) {
            return $this->formatPaginatedResponse(...func_get_args());
        }

        if ($data instanceof Arrayable) {
            $data = $data->toArray();
        }

        return $this->formatArrayResponse(Arr::wrap($data), $message, $code, $headers, $option);
    }

    /**
     * 格式化数组
     *
     * @param  array|null  $data
     * @param  string  $message
     * @param  int  $code
     * @param  array  $headers
     * @param  int  $option
     *
     * @return JsonResponse
     */
    protected function formatArrayResponse(?array $data, string $message = '', $code = 200, array $headers = [], $option = 0): JsonResponse
    {
        return $this->response($this->formatData($data, $message, $code), $code, $headers, $option);
    }

    /**
     * 格式化数据
     *
     * @param  JsonResource|array|null  $data
     * @param $message
     * @param $code
     * @param  null  $errors
     * @return array
     */
    protected function formatData($data, $message, &$code, $errors = null): array
    {
        $originalCode = $code;
        $code = (int) substr($code, 0, 3); // notice
        if ($code >= 400 && $code <= 499) {// client error
            $status = 'error';
        } elseif ($code >= 500 && $code <= 599) {// service error
            $status = 'fail';
        } else {
            $status = 'success';
        }

        if (! $message && class_exists($enumClass = Config::get('response.enum'))) {
            $message = $enumClass::fromValue($originalCode)->description;
        }

        return [
            'status' => $status,
            'code' => $originalCode,
            'message' => $message,
            'data' => $data ?: (object) $data,
            'error' => $errors ?: (object) [],
        ];
    }

    /**
     * 格式化分页信息
     *
     * @param  AbstractPaginator  $resource
     * @param  string  $message
     * @param  int  $code
     * @param  array  $headers
     * @param  int  $option
     *
     * @return mixed
     */
    protected function formatPaginatedResponse($resource, string $message = '', $code = 200, array $headers = [], $option = 0)
    {
        $paginated = $resource->toArray();

        $paginationInformation = $this->formatPaginatedData($paginated);

        $paginationDataField = Config::get('response.format.paginated_resource.data_field', 'items');
        $data = array_merge_recursive([$paginationDataField => $paginated['data']], $paginationInformation);

        return $this->response($this->formatData($data, $message, $code), $code, $headers, $option);
    }

    /**
     * 格式化分页数据
     *
     * @param  array  $paginated
     *
     * @return array
     */

    protected function formatPaginatedData(array $paginated)
    {
        $count = $paginated['total'] ?? null;
        $totalPages = $paginated['last_page'] ?? null;
        $previous = $paginated['prev_page_url'] ?? null;
        $next = $paginated['next_page_url'] ?? null;

        $paginationInformation = [
            'meta' => [
                'pagination' => [
                    'count' => $paginated['to'] ?? null,
                    'per_page' => $paginated['per_page'] ?? null,
                    'current_page' => $paginated['current_page'] ?? null,
                ],
            ],
        ];


        if (!is_null($count)) {
            $paginationInformation['meta']['pagination']['total'] = $count;
        }

        if ($totalPages) {
            $paginationInformation['meta']['pagination']['total_pages'] = $totalPages;
        }

        if ($previous || $next) {
            $paginationInformation['meta']['pagination']['links'] = [
                'previous' => $previous,
                'next' => $next,
            ];
        }

        return $paginationInformation;
    }

    /**
     * 格式化集合
     *
     * @param  JsonResource  $resource
     * @param  string  $message
     * @param  int  $code
     * @param  array  $headers
     * @param  int  $option
     *
     * @return mixed
     */
    protected function formatResourceCollectionResponse($resource, string $message = '', $code = 200, array $headers = [], $option = 0)
    {
        $dataField = 'items';

        $data = array_merge_recursive([$dataField => $resource->resolve(request())], $resource->with(request()), $resource->additional);
        if ($resource->resource instanceof AbstractPaginator) {
            $paginated = $resource->resource->toArray();
            $paginationInformation = $this->formatPaginatedData($paginated);

            $data = array_merge_recursive($data, $paginationInformation);
        }

        return tap(
            $this->response($this->formatData($data, $message, $code), $code, $headers, $option),
            function ($response) use ($resource) {
                $response->original = $resource->resource->map(
                    function ($item) {
                        return is_array($item) ? Arr::get($item, 'resource') : $item->resource;
                    }
                );

                $resource->withResponse(request(), $response);
            }
        );
    }

    /**
     * 格式化Resource
     *
     * @param  JsonResource  $resource
     * @param  string  $message
     * @param  int  $code
     * @param  array  $headers
     * @param  int  $option
     *
     * @return mixed
     */
    protected function formatResourceResponse($resource, string $message = '', $code = 200, array $headers = [], $option = 0)
    {
        $resourceData = array_merge_recursive($resource->resolve(request()), $resource->with(request()), $resource->additional);

        return tap(
            $this->response($this->formatData($resourceData, $message, $code), $code, $headers, $option),
            function ($response) use ($resource) {
                $response->original = $resource->resource;

                $resource->withResponse(request(), $response);
            }
        );
    }

    /**
     * 基本响应
     *
     * @param  mixed  $data
     * @param  int  $status
     * @param  array  $headers
     * @param  int  $options
     * @return JsonResponse
     */
    protected function response($data = [], $status = 200, array $headers = [], $options = 0): JsonResponse
    {
        return new JsonResponse($data, $status, $headers, $options);
    }
}