RESTful API, API Resources, Versioning
php artisan make:resource PostResource
php artisan make:resource PostCollection --collection
<?php
// app/Http/Resources/PostResource.php
namespace App\Http\Resources;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;
class PostResource extends JsonResource
{
public function toArray(Request $request): array
{
return [
'id' => $this->id,
'title' => $this->title,
'slug' => $this->slug,
'excerpt' => Str::limit($this->content, 150),
'content' => $this->when($request->routeIs('posts.show'), $this->content),
'status' => $this->status,
'published_at' => $this->published_at?->toIso8601String(),
'created_at' => $this->created_at->toIso8601String(),
// Relationships
'author' => new UserResource($this->whenLoaded('author')),
'category' => new CategoryResource($this->whenLoaded('category')),
'tags' => TagResource::collection($this->whenLoaded('tags')),
// Conditional data
'comments_count' => $this->whenCounted('comments'),
'can_edit' => $this->when(
$request->user()?->id === $this->user_id,
true
),
// Links
'links' => [
'self' => route('api.posts.show', $this),
'comments' => route('api.posts.comments.index', $this),
],
];
}
}
// Sử dụng trong Controller
class PostController extends Controller
{
public function index()
{
$posts = Post::with(['author', 'category'])
->withCount('comments')
->published()
->paginate(15);
return PostResource::collection($posts);
}
public function show(Post $post)
{
$post->load(['author', 'category', 'tags', 'comments.user']);
return new PostResource($post);
}
}
<?php
// bootstrap/app.php (Laravel 11+)
use Illuminate\Cache\RateLimiting\Limit;
use Illuminate\Support\Facades\RateLimiter;
->withMiddleware(function (Middleware $middleware) {
RateLimiter::for('api', function (Request $request) {
return Limit::perMinute(60)->by($request->user()?->id ?: $request->ip());
});
// Premium users có rate cao hơn
RateLimiter::for('api-premium', function (Request $request) {
return $request->user()?->isPremium()
? Limit::perMinute(200)->by($request->user()->id)
: Limit::perMinute(60)->by($request->ip());
});
// Rate limit cho uploads
RateLimiter::for('uploads', function (Request $request) {
return Limit::perHour(10)->by($request->user()->id);
});
})
// routes/api.php
Route::middleware(['throttle:api'])->group(function () {
Route::apiResource('posts', PostController::class);
});
Route::middleware(['throttle:uploads'])->group(function () {
Route::post('/uploads', [UploadController::class, 'store']);
});
<?php
// routes/api.php
// API v1
Route::prefix('v1')->group(function () {
Route::apiResource('posts', App\Http\Controllers\Api\V1\PostController::class);
Route::apiResource('users', App\Http\Controllers\Api\V1\UserController::class);
});
// API v2 - Breaking changes
Route::prefix('v2')->group(function () {
Route::apiResource('posts', App\Http\Controllers\Api\V2\PostController::class);
Route::apiResource('articles', App\Http\Controllers\Api\V2\ArticleController::class);
});
// Cấu trúc thư mục
// app/Http/Controllers/Api/V1/PostController.php
// app/Http/Controllers/Api/V2/PostController.php
// app/Http/Resources/V1/PostResource.php
// app/Http/Resources/V2/PostResource.php