api-resource-patterns
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseAPI Resource Patterns
API Resource 模式
Basic Resource Structure
基础资源结构
php
<?php
namespace App\Http\Resources;
use Illuminate\Http\Resources\Json\JsonResource;
class PostResource extends JsonResource
{
public function toArray($request): array
{
return [
'id' => $this->id,
'title' => $this->title,
'content' => $this->content,
'created_at' => $this->created_at->toISOString(),
'updated_at' => $this->updated_at->toISOString(),
];
}
}php
<?php
namespace App\Http\Resources;
use Illuminate\Http\Resources\Json\JsonResource;
class PostResource extends JsonResource
{
public function toArray($request): array
{
return [
'id' => $this->id,
'title' => $this->title,
'content' => $this->content,
'created_at' => $this->created_at->toISOString(),
'updated_at' => $this->updated_at->toISOString(),
];
}
}Conditional Attributes
条件属性
php
public function toArray($request): array
{
return [
'id' => $this->id,
'title' => $this->title,
// Only include if loaded
'author' => new UserResource($this->whenLoaded('user')),
// Only include if condition is true
'content' => $this->when($request->user()?->can('view', $this->resource), $this->content),
// Only include if not null
'comments_count' => $this->when($this->comments_count !== null, $this->comments_count),
// Merge conditionally
$this->mergeWhen($request->user()?->isAdmin(), [
'internal_notes' => $this->internal_notes,
]),
];
}php
public function toArray($request): array
{
return [
'id' => $this->id,
'title' => $this->title,
// Only include if loaded
'author' => new UserResource($this->whenLoaded('user')),
// Only include if condition is true
'content' => $this->when($request->user()?->can('view', $this->resource), $this->content),
// Only include if not null
'comments_count' => $this->when($this->comments_count !== null, $this->comments_count),
// Merge conditionally
$this->mergeWhen($request->user()?->isAdmin(), [
'internal_notes' => $this->internal_notes,
]),
];
}Nested Relationships
嵌套关联
php
public function toArray($request): array
{
return [
'id' => $this->id,
'title' => $this->title,
// Single relationship
'author' => new UserResource($this->whenLoaded('user')),
// Collection relationship
'comments' => CommentResource::collection($this->whenLoaded('comments')),
// Nested relationships
'category' => new CategoryResource($this->whenLoaded('category')),
];
}php
public function toArray($request): array
{
return [
'id' => $this->id,
'title' => $this->title,
// Single relationship
'author' => new UserResource($this->whenLoaded('user')),
// Collection relationship
'comments' => CommentResource::collection($this->whenLoaded('comments')),
// Nested relationships
'category' => new CategoryResource($this->whenLoaded('category')),
];
}Resource Collections
资源集合
php
<?php
namespace App\Http\Resources;
use Illuminate\Http\Resources\Json\ResourceCollection;
class PostCollection extends ResourceCollection
{
public function toArray($request): array
{
return [
'data' => $this->collection,
'meta' => [
'total' => $this->total(),
'count' => $this->count(),
'per_page' => $this->perPage(),
'current_page' => $this->currentPage(),
'total_pages' => $this->lastPage(),
],
'links' => [
'self' => $request->url(),
'first' => $this->url(1),
'last' => $this->url($this->lastPage()),
'prev' => $this->previousPageUrl(),
'next' => $this->nextPageUrl(),
],
];
}
}php
<?php
namespace App\Http\Resources;
use Illuminate\Http\Resources\Json\ResourceCollection;
class PostCollection extends ResourceCollection
{
public function toArray($request): array
{
return [
'data' => $this->collection,
'meta' => [
'total' => $this->total(),
'count' => $this->count(),
'per_page' => $this->perPage(),
'current_page' => $this->currentPage(),
'total_pages' => $this->lastPage(),
],
'links' => [
'self' => $request->url(),
'first' => $this->url(1),
'last' => $this->url($this->lastPage()),
'prev' => $this->previousPageUrl(),
'next' => $this->nextPageUrl(),
],
];
}
}Adding Links
添加链接
php
public function toArray($request): array
{
return [
'id' => $this->id,
'title' => $this->title,
'links' => [
'self' => route('posts.show', $this->id),
'author' => route('users.show', $this->user_id),
'comments' => route('posts.comments.index', $this->id),
],
];
}php
public function toArray($request): array
{
return [
'id' => $this->id,
'title' => $this->title,
'links' => [
'self' => route('posts.show', $this->id),
'author' => route('users.show', $this->user_id),
'comments' => route('posts.comments.index', $this->id),
],
];
}Resource Response Customization
资源响应自定义
php
// In controller
public function store(Request $request)
{
$post = Post::create($request->validated());
return (new PostResource($post))
->response()
->setStatusCode(201)
->header('Location', route('posts.show', $post));
}php
// In controller
public function store(Request $request)
{
$post = Post::create($request->validated());
return (new PostResource($post))
->response()
->setStatusCode(201)
->header('Location', route('posts.show', $post));
}Pivot Data in Resources
资源中的中间表数据
php
public function toArray($request): array
{
return [
'id' => $this->id,
'name' => $this->name,
'assigned_at' => $this->whenPivotLoaded('role_user', function () {
return $this->pivot->created_at;
}),
'expires_at' => $this->whenPivotLoadedAs('assignment', 'role_user', function () {
return $this->assignment->expires_at;
}),
];
}php
public function toArray($request): array
{
return [
'id' => $this->id,
'name' => $this->name,
'assigned_at' => $this->whenPivotLoaded('role_user', function () {
return $this->pivot->created_at;
}),
'expires_at' => $this->whenPivotLoadedAs('assignment', 'role_user', function () {
return $this->assignment->expires_at;
}),
];
}Wrapping and Unwrapping
包裹与取消包裹
php
// Disable wrapping in AppServiceProvider
use Illuminate\Http\Resources\Json\JsonResource;
public function boot()
{
JsonResource::withoutWrapping();
}
// Or per resource
public static $wrap = 'post';php
// Disable wrapping in AppServiceProvider
use Illuminate\Http\Resources\Json\JsonResource;
public function boot()
{
JsonResource::withoutWrapping();
}
// Or per resource
public static $wrap = 'post';With Additional Data
添加额外数据
php
public function with($request): array
{
return [
'version' => '1.0.0',
'timestamp' => now()->toISOString(),
];
}
public function withResponse($request, $response)
{
$response->header('X-Value', 'True');
}php
public function with($request): array
{
return [
'version' => '1.0.0',
'timestamp' => now()->toISOString(),
];
}
public function withResponse($request, $response)
{
$response->header('X-Value', 'True');
}Best Practices
最佳实践
Always Use whenLoaded for Relationships
始终为关联使用whenLoaded
php
// ✅ Prevents N+1 queries
'author' => new UserResource($this->whenLoaded('user')),
// ❌ Will cause N+1 queries
'author' => new UserResource($this->user),php
// ✅ Prevents N+1 queries
'author' => new UserResource($this->whenLoaded('user')),
// ❌ Will cause N+1 queries
'author' => new UserResource($this->user),Use Type Hints
使用类型提示
php
use Illuminate\Http\Request;
public function toArray(Request $request): array
{
// ...
}php
use Illuminate\Http\Request;
public function toArray(Request $request): array
{
// ...
}Keep Resources Focused
保持资源专注
php
// ✅ Create separate resources for different contexts
class PostResource extends JsonResource { }
class PostListResource extends JsonResource { }
class PostDetailResource extends JsonResource { }
// ❌ Don't make one resource do everythingphp
// ✅ Create separate resources for different contexts
class PostResource extends JsonResource { }
class PostListResource extends JsonResource { }
class PostDetailResource extends JsonResource { }
// ❌ Don't make one resource do everythingUse Resource Collections
使用资源集合
php
// ✅ Use collection class
return new PostCollection(Post::paginate());
// ✅ Or collection method
return PostResource::collection(Post::all());php
// ✅ Use collection class
return new PostCollection(Post::paginate());
// ✅ Or collection method
return PostResource::collection(Post::all());Controller Usage
控制器使用示例
php
class PostController extends Controller
{
public function index()
{
$posts = Post::with(['user', 'category'])
->withCount('comments')
->paginate(15);
return new PostCollection($posts);
}
public function show(Post $post)
{
$post->load(['user', 'comments.user', 'tags']);
return new PostResource($post);
}
public function store(StorePostRequest $request)
{
$post = Post::create($request->validated());
return (new PostResource($post))
->response()
->setStatusCode(201);
}
}php
class PostController extends Controller
{
public function index()
{
$posts = Post::with(['user', 'category'])
->withCount('comments')
->paginate(15);
return new PostCollection($posts);
}
public function show(Post $post)
{
$post->load(['user', 'comments.user', 'tags']);
return new PostResource($post);
}
public function store(StorePostRequest $request)
{
$post = Post::create($request->validated());
return (new PostResource($post))
->response()
->setStatusCode(201);
}
}Checklist
检查清单
- Resources transform models consistently
- Relationships loaded with whenLoaded()
- Conditional attributes use when()
- Collections include pagination metadata
- Links included for HATEOAS
- Type hints used
- Proper HTTP status codes
- No N+1 queries
- Consistent date formatting
- Appropriate wrapping strategy
- 资源一致地转换模型
- 关联使用whenLoaded()加载
- 条件属性使用when()
- 集合包含分页元数据
- 包含HATEOAS链接
- 使用类型提示
- 正确的HTTP状态码
- 无N+1查询问题
- 一致的日期格式
- 合适的包裹策略