Loading source
Pulling the file list, source metadata, and syntax-aware rendering for this listing.
Source from repo
Build Laravel 10+ apps with Eloquent, Sanctum auth, Horizon queues, Livewire, and RESTful API resources.
Files
Skill
Size
Entrypoint
Format
Open file
Syntax-highlighted preview of this file as included in the skill package.
references/routing.md
1# Routing & API Resources23## Route Patterns45```php6// routes/web.php7use App\Http\Controllers\PostController;8use Illuminate\Support\Facades\Route;910// Resource routes11Route::resource('posts', PostController::class);1213// API resource (excludes create/edit)14Route::apiResource('posts', PostController::class);1516// Partial resource17Route::resource('posts', PostController::class)->only(['index', 'show']);18Route::resource('posts', PostController::class)->except(['destroy']);1920// Nested resources21Route::resource('posts.comments', CommentController::class);2223// Route groups24Route::prefix('admin')->middleware('auth')->group(function () {25Route::get('/dashboard', [DashboardController::class, 'index']);26Route::resource('users', UserController::class);27});2829// Named routes30Route::get('/posts/{post}', [PostController::class, 'show'])->name('posts.show');3132// Route model binding33Route::get('/posts/{post:slug}', [PostController::class, 'show']);3435// Multiple bindings36Route::get('/users/{user}/posts/{post:slug}', function (User $user, Post $post) {37return view('posts.show', compact('user', 'post'));38});39```4041## API Routes4243```php44// routes/api.php45use App\Http\Controllers\Api\V1\PostController;4647Route::prefix('v1')->group(function () {48// Public routes49Route::get('/posts', [PostController::class, 'index']);50Route::get('/posts/{post}', [PostController::class, 'show']);5152// Protected routes53Route::middleware('auth:sanctum')->group(function () {54Route::post('/posts', [PostController::class, 'store']);55Route::put('/posts/{post}', [PostController::class, 'update']);56Route::delete('/posts/{post}', [PostController::class, 'destroy']);57});58});5960// Rate limiting61Route::middleware('throttle:60,1')->group(function () {62Route::apiResource('posts', PostController::class);63});64```6566## Controllers6768```php69namespace App\Http\Controllers\Api;7071use App\Http\Controllers\Controller;72use App\Http\Requests\StorePostRequest;73use App\Http\Requests\UpdatePostRequest;74use App\Http\Resources\PostResource;75use App\Http\Resources\PostCollection;76use App\Models\Post;77use Illuminate\Http\Response;7879class PostController extends Controller80{81public function index()82{83$posts = Post::with('user')84->published()85->paginate(15);8687return new PostCollection($posts);88}8990public function store(StorePostRequest $request)91{92$post = Post::create($request->validated());9394return new PostResource($post);95}9697public function show(Post $post)98{99$post->load(['user', 'comments.user']);100101return new PostResource($post);102}103104public function update(UpdatePostRequest $request, Post $post)105{106$post->update($request->validated());107108return new PostResource($post);109}110111public function destroy(Post $post)112{113$post->delete();114115return response()->noContent();116}117}118```119120## Form Requests121122```php123namespace App\Http\Requests;124125use Illuminate\Foundation\Http\FormRequest;126use Illuminate\Validation\Rule;127128class StorePostRequest extends FormRequest129{130public function authorize(): bool131{132return true; // Or check user permissions133}134135public function rules(): array136{137return [138'title' => ['required', 'string', 'max:255'],139'slug' => ['required', 'string', 'unique:posts,slug'],140'content' => ['required', 'string'],141'category_id' => ['required', 'exists:categories,id'],142'tags' => ['array'],143'tags.*' => ['exists:tags,id'],144'published_at' => ['nullable', 'date', 'after:now'],145];146}147148public function messages(): array149{150return [151'title.required' => 'Please provide a post title',152'slug.unique' => 'This slug is already taken',153];154}155156// Prepare data before validation157protected function prepareForValidation(): void158{159$this->merge([160'slug' => str($this->title)->slug(),161]);162}163}164165class UpdatePostRequest extends FormRequest166{167public function rules(): array168{169return [170'title' => ['sometimes', 'string', 'max:255'],171'slug' => [172'sometimes',173'string',174Rule::unique('posts', 'slug')->ignore($this->post)175],176'content' => ['sometimes', 'string'],177];178}179}180```181182## API Resources183184```php185namespace App\Http\Resources;186187use Illuminate\Http\Request;188use Illuminate\Http\Resources\Json\JsonResource;189190class PostResource extends JsonResource191{192public function toArray(Request $request): array193{194return [195'id' => $this->id,196'title' => $this->title,197'slug' => $this->slug,198'excerpt' => $this->excerpt,199'content' => $this->when($request->route()->named('posts.show'), $this->content),200'published_at' => $this->published_at?->toISOString(),201'created_at' => $this->created_at->toISOString(),202203// Relationships204'author' => new UserResource($this->whenLoaded('user')),205'comments' => CommentResource::collection($this->whenLoaded('comments')),206'comments_count' => $this->when($this->comments_count !== null, $this->comments_count),207208// Conditional fields209'is_published' => $this->when($request->user()?->isAdmin(), $this->isPublished()),210211// Pivot data212'role' => $this->whenPivotLoaded('role_user', function () {213return $this->pivot->role_name;214}),215216// Links217'links' => [218'self' => route('api.posts.show', $this->id),219],220];221}222223public function with(Request $request): array224{225return [226'meta' => [227'version' => '1.0.0',228],229];230}231}232```233234## Resource Collections235236```php237namespace App\Http\Resources;238239use Illuminate\Http\Request;240use Illuminate\Http\Resources\Json\ResourceCollection;241242class PostCollection extends ResourceCollection243{244public function toArray(Request $request): array245{246return [247'data' => $this->collection,248'meta' => [249'total' => $this->total(),250'current_page' => $this->currentPage(),251'last_page' => $this->lastPage(),252],253'links' => [254'self' => $request->url(),255],256];257}258}259260// Or use anonymous collection261return PostResource::collection($posts);262```263264## Middleware265266```php267namespace App\Http\Middleware;268269use Closure;270use Illuminate\Http\Request;271272class EnsureUserIsAdmin273{274public function handle(Request $request, Closure $next)275{276if (!$request->user()?->isAdmin()) {277abort(403, 'Unauthorized action.');278}279280return $next($request);281}282}283284// Register in app/Http/Kernel.php285protected $middlewareAliases = [286'admin' => \App\Http\Middleware\EnsureUserIsAdmin::class,287];288289// Use in routes290Route::middleware('admin')->group(function () {291Route::resource('users', UserController::class);292});293```294295## Response Helpers296297```php298// JSON responses299return response()->json(['data' => $posts], 200);300301// Created response302return response()->json($post, 201);303304// No content305return response()->noContent();306307// Custom headers308return response()->json($data)->header('X-Custom-Header', 'Value');309310// Download311return response()->download($pathToFile);312313// Stream314return response()->streamDownload(function () {315echo 'CSV content...';316}, 'export.csv');317```318319## Route Caching320321```bash322# Generate route cache323php artisan route:cache324325# Clear route cache326php artisan route:clear327328# List all routes329php artisan route:list330331# Filter routes332php artisan route:list --name=api333php artisan route:list --path=posts334```335336## API Versioning337338```php339// routes/api.php340Route::prefix('v1')->name('v1.')->group(function () {341Route::apiResource('posts', \App\Http\Controllers\Api\V1\PostController::class);342});343344Route::prefix('v2')->name('v2.')->group(function () {345Route::apiResource('posts', \App\Http\Controllers\Api\V2\PostController::class);346});347```348349## CORS Configuration350351```php352// config/cors.php353return [354'paths' => ['api/*', 'sanctum/csrf-cookie'],355'allowed_methods' => ['*'],356'allowed_origins' => ['http://localhost:3000'],357'allowed_headers' => ['*'],358'exposed_headers' => [],359'max_age' => 0,360'supports_credentials' => true,361];362```363