
Laravel的API Resources提供了一种将Eloquent模型及其关系转换为JSON结构化响应的便捷方式。它主要包含两种类型:
当需要返回一个包含分页信息的集合时,Laravel推荐使用 ResourceCollection。它会自动将分页数据(如当前页码、总页数、分页链接等)与实际数据一起封装在JSON响应中。
顶层集合分页示例:
假设我们有一个 User 模型,并希望以分页形式返回用户列表。
// app/Http/Resources/UserResource.php
namespace App\Http\Resources;
use Illuminate\Http\Resources\Json\JsonResource;
class UserResource extends JsonResource
{
public function toArray($request)
{
return [
'id' => $this->id,
'name' => $this->name,
'email' => $this->email,
// ... 其他用户字段
];
}
}
// app/Http/Resources/UserCollection.php
namespace App\Http\Resources;
use Illuminate\Http\Resources\Json\ResourceCollection;
class UserCollection extends ResourceCollection
{
/**
* Transform the resource collection into an array.
* ResourceCollection 的默认 toArray 方法会自动处理分页信息。
*
* @param \Illuminate\Http\Request $request
* @return array
*/
public function toArray($request)
{
// 如果你需要对集合中的每个资源进行额外转换,可以这样做,
// 但通常情况下,ResourceCollection 会自动使用关联的 JsonResource。
// return parent::toArray($request); // 默认行为即可
return [
'data' => $this->collection->map(function ($user) {
return new UserResource($user);
}),
'links' => $this->resource->links(), // 手动添加分页链接
'meta' => [
'current_page' => $this->resource->currentPage(),
'from' => $this->resource->firstItem(),
'last_page' => $this->resource->lastPage(),
'path' => $this->resource->path(),
'per_page' => $this->resource->perPage(),
'to' => $this->resource->lastItem(),
'total' => $this->resource->total(),
],
];
}
}注意: 在UserCollection中,如果仅需默认的分页结构,toArray方法可以省略或只返回parent::toArray($request);。Laravel 9+ 默认生成的 ResourceCollection 甚至没有 toArray 方法,因为它会通过 collects 属性自动关联 JsonResource 并生成标准分页结构。
在路由或控制器中:
use App\Http\Resources\UserCollection;
use App\Models\User;
use Illuminate\Support\Facades\Route;
Route::get('/users', function () {
return new UserCollection(User::paginate(15));
});这将生成类似如下的JSON响应:
{
"data": [
// ... 用户数据
],
"links": {
"first": "http://example.com/users?page=1",
"last": "http://example.com/users?page=N",
"prev": null,
"next": "http://example.com/users?page=2"
},
"meta": {
"current_page": 1,
"from": 1,
"last_page": N,
"path": "http://example.com/users",
"per_page": 15,
"to": 15,
"total": M
}
}在问题描述中,用户试图在一个 SectionResource 中包含一个分页的 items 集合:
// app/Http/Resources/SectionResource.php (用户原始代码片段)
public function toArray($request)
{
return [
// ... 其他字段
"items" => new ItemCollection($this->items()->paginate(20)),
];
}
// app/Http/Resources/ItemCollection.php (用户原始代码片段)
public function toArray($request)
{
return $this->collection->map(function ($item) {
return [
"id" => $item->id ,
"name" => $item->name ,
// ... 其他 item 字段
];
});
}这里的核心问题在于 ItemCollection 的 toArray 方法。尽管 new ItemCollection($this->items()->paginate(20)) 确实将一个分页器实例传递给了 ItemCollection,但 ItemCollection 内部的 toArray 方法却完全覆盖了 ResourceCollection 默认处理分页信息的能力。它仅仅返回了一个由 map 方法生成的普通数组,导致分页链接和元数据丢失。
ResourceCollection 默认的 toArray 方法会智能地检测其内部的 resource 属性(即传入的分页器实例),并自动构建包含 data、links 和 meta 的完整JSON结构。一旦你自定义了 toArray 并只返回 map 结果,这个自动机制就被破坏了。
要正确地在父资源中显示嵌套集合的分页链接,我们需要遵循以下步骤:
首先,为 Item 模型创建一个单独的 JsonResource。这将负责将单个 Item 模型转换为其对应的JSON结构。
// app/Http/Resources/ItemResource.php
namespace App\Http\Resources;
use Illuminate\Http\Resources\Json\JsonResource;
class ItemResource extends JsonResource
{
/**
* Transform the resource into an array.
*
* @param \Illuminate\Http\Request $request
* @return array
*/
public function toArray($request)
{
return [
"id" => $this->id ,
"name" => $this->name ,
"slug" => $this->slug ,
"image" => imageGenerate("items" , $this->image) , // 假设 imageGenerate 是一个辅助函数
"code" => $this->code ,
"category" => $this->category->name ?? "" // 使用 null 合并运算符处理可能不存在的 category
];
}
}ItemCollection 应该继承 ResourceCollection,并且不应重写其 toArray 方法以至于丢失分页信息。最简单且推荐的做法是,如果 ItemCollection 仅用于包装 ItemResource 并提供分页功能,则其 toArray 方法可以保持默认行为或明确调用 parent::toArray($request)。
// app/Http/Resources/ItemCollection.php
namespace App\Http\Resources;
use Illuminate\Http\Resources\Json\ResourceCollection;
class ItemCollection extends ResourceCollection
{
/**
* The resource that this resource collects.
* 定义此集合收集的单个资源类型。
* 这使得 ResourceCollection 能够自动处理集合中每个元素的转换。
*
* @var string
*/
public $collects = ItemResource::class; // Laravel 9+ 推荐用法
/**
* Transform the resource collection into an array.
*
* @param \Illuminate\Http\Request $request
* @return array
*/
public function toArray($request)
{
// 保持 ResourceCollection 的默认行为,它会自动生成 data, links, meta 结构
return parent::toArray($request);
}
}通过设置 $collects 属性,ResourceCollection 会自动使用 ItemResource 来转换集合中的每个模型。toArray 方法只需调用 parent::toArray($request) 即可获取完整的包含分页信息的响应结构。
现在,SectionResource 可以安全地实例化 ItemCollection 并传入分页后的 items 数据。ItemCollection 将负责生成包含分页链接的正确JSON结构。
// app/Http/Resources/SectionResource.php
namespace App\Http\Resources;
use Illuminate\Http\Resources\Json\JsonResource;
class SectionResource extends JsonResource
{
/**
* Transform the resource into an array.
*
* @param \Illuminate\Http\Request $request
* @return array
*/
public function toArray($request)
{
return [
"name" => $this->name ,
"slug" => $this->slug ,
"bg_image" => imageGenerate("sections" , $this->bg_image) ,
"bg_color" => $this->bg_color ,
// 使用 ItemCollection 包装分页后的 items 集合
"items" => new ItemCollection($this->items()->paginate(20)) ,
];
}
}假设你的路由如下:
// routes/api.php
use App\Http\Resources\SectionResource;
use App\Models\Section; // 假设 Section 是你的模型
Route::get('/sections/{section}', function (Section $section) {
return new SectionResource($section);
});当访问 /api/sections/{section_id} 时,你将获得一个包含嵌套 items 集合,并且该集合带有完整分页信息的JSON响应:
{
"data": {
"name": "Section Name",
"slug": "section-slug",
"bg_image": "http://example.com/images/sections/bg.jpg",
"bg_color": "#FFFFFF",以上就是在Laravel Resource中优雅地处理嵌套集合的分页链接的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号