【问题标题】:Laravel: cannot access resource collection in middlewareLaravel:无法访问中间件中的资源集合
【发布时间】:2019-12-20 18:16:28
【问题描述】:

我正在尝试创建一个中间件,它将返回的响应包装成我的 API 的统一 JSON 响应。这将确保所有 API 响应具有相同的基本结构,如下所示:

{
    "success": true,
    "data": {...}
}

现在这是我目前使用的中间件:

namespace App\Http\Middleware;

use Closure;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Http\Response;

class ApiResponseWrapper
{
    /**
     * Handle an incoming request.
     *
     * @param Request $request
     *
     * @return mixed
     */
    public function handle($request, Closure $next)
    {
        /** @var Response $response */
        $response = $next($request);
        $original = $response->getOriginalContent();

        $content = [
            'success' => $response->isSuccessful(),
            'data' => $original,
        ];

        $jsonContent = json_encode($content, JsonResponse::DEFAULT_ENCODING_OPTIONS);
        $response->setContent($jsonContent);

        return $response;
    }
}

这个中间件的两个关键点是:

  1. $original = $response->getOriginalContent(); 行获取要换行的响应内容。
  2. 内容必须是字符串才能设置为新内容。这就是为什么我 json_encode $content 使用 JSON 响应的默认编码选项。

当我想将分页的ResourceCollection 传递到中间件时,问题就出现了。控制器方法如下所示:

public function index(): AnonymousResourceCollection
{
    /** @var User $user */
    $user = auth()->user();

    return PostResource::collection($user->posts->paginate(20));
}

通常,->paginate(...) 方法不存在于 Laravel 集合中。这是一个取自这里的宏:gist.github.com/simonhamp/549e8821946e2c40a617c85d2cf5af5e

如果我现在点击调用index() 方法的端点,预期的输出是这样的:

{
    "success": true,
    "data": {
        "data": [
            <PostResource as JSON object>
        ],
        "meta": {...},
        "links": {...},
    }
}

但我实际上得到的是:

{
    "success": true,
    "data": [
        <Post Model as array>
    ]
}

我想我可能需要调用一个方法来将$original(类型为Illuminate\Support\Collection)编码为分页资源集合,但我没有找到。

所有其他响应都很好。例如,在另一个控制器方法上,我只是返回一个数组或一个字符串,它们按预期包装。

有人知道这里出了什么问题吗?


编辑:

我还尝试在设置 $content 变量之前添加它:

if (method_exists($original, 'toArray')) {
    $original = $original->toArray();
}

ResourceCollections 的输出结果相同。

【问题讨论】:

  • 如果你使用'data' =&gt; $original-&gt;toArray()会发生什么?
  • @FelippeDuarte 它会产生相同的输出,而不是想要的输出。
  • 旁注:不要在集合上调用-&gt;paginate,您应该能够在该关系的查询构建器上调用它:$user-&gt;posts()-&gt;paginate(20),而不需要任何宏。由于过滤和限制是在数据库级别完成的,而不是在内存对象中,这也会表现得更好
  • @mdexp 通常,你是对的。然而,就我而言,posts 实际上是一个模型访问器:Defining An Accessor。您对性能的看法可能是正确的,我一定会对此进行调查。谢谢!

标签: php json laravel resources middleware


【解决方案1】:

在调查变量类型并查看 Laravel API 后,我发现最简单的解决方案是检查响应是否为 JsonResponse 并从中获取渲染的分页数据:

public function handle($request, Closure $next)
{
    /** @var Response $response */
    $response = $next($request);
    $content = [
        'success' => $response->isSuccessful(),
    ];

    if ($response instanceof JsonResponse) {
        /** @var JsonResponse $response */

        $data = $response->getData(true);

        $content = array_merge($content, $data);
    } else {
        $content['data'] = $response->getOriginalContent();
    }

    $jsonContent = json_encode($content, JsonResponse::DEFAULT_ENCODING_OPTIONS);
    $response->setContent($jsonContent);

    return $response;
}

这会导致我的用例的预期响应:

{
    "success": true,
    "data": [
        <PostResource as JSON object>
    ],
    "meta": {...},
    "links": {...},
}

还请注意,我最初在包装 data 键中指定了 datametalinks 键。 我发现上面的结果更漂亮一些,因为我得到了双重包装 data

希望对遇到这个问题的人有所帮助!

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-09-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多