php
43 lines · 8 steps
Filtered, paginated JSON APIs in Laravel
An Eloquent query builder collects filters conditionally, then an API Resource shapes each row into clean JSON.
Explained by
highlit
1class PostController extends Controller
2{
3 public function index(Request $request): AnonymousResourceCollection
4 {
5 $posts = Post::query()
6 ->with(['author', 'tags'])
7 ->withCount('comments')
8 ->when($request->filled('search'), function ($query) use ($request) {
9 $query->where('title', 'like', "%{$request->string('search')}%");
10 })
11 ->when($request->filled('tag'), function ($query) use ($request) {
12 $query->whereHas('tags', fn ($q) => $q->where('slug', $request->string('tag')));
13 })
14 ->latest('published_at')
15 ->paginate($request->integer('per_page', 15))
16 ->withQueryString();
17
18 return PostResource::collection($posts);
19 }
20}
21
22class PostResource extends JsonResource
23{
24 public function toArray(Request $request): array
25 {
26 return [
27 'id' => $this->id,
28 'title' => $this->title,
29 'slug' => $this->slug,
30 'excerpt' => Str::limit(strip_tags($this->body), 160),
31 'comments_count' => $this->comments_count,
32 'author' => [
33 'name' => $this->author->name,
34 'avatar_url' => $this->author->avatar_url,
35 ],
36 'tags' => $this->tags->pluck('name'),
37 'published_at' => $this->published_at?->toIso8601String(),
38 'links' => [
39 'self' => route('posts.show', $this->slug),
40 ],
41 ];
42 }
43}
01 / 01
STEP 01
‹ swipe to step through ›
Walkthrough
Space play
←→ step
click any line
Three takeaways
- 1Conditional query methods like when() let you build filters from request input without scattering if-statements.
- 2Eager loading with with() and withCount() prevents N+1 queries when the response touches related records.
- 3API Resources separate database shape from response shape, giving you a stable, deliberate JSON contract.
Related explainers
php
<?php namespace App\Support;
Locale-aware formatting with PHP's intl extension
internationalization
encapsulation
constructor-injection
Intermediate
7 steps
javascript
const express = require('express'); const v1 = express.Router();
Versioning an API with Express Routers
api versioning
routing
modularity
Intermediate
10 steps
php
<?php namespace App\Support;
Merging query params onto a URL in PHP
url-parsing
query-strings
immutability
Intermediate
8 steps
python
import csv import io from datetime import datetime
Streaming a CSV export in Flask
streaming
generators
csv
Intermediate
9 steps
php
<?php class ImageUploadService {
Validating file uploads safely in PHP
file-upload
input-validation
security
Intermediate
8 steps
php
<?php namespace App\View;
Building a safe HTML escaper in PHP
security
xss
escaping
Intermediate
6 steps
Share this explainer
Here's the card — post it anywhere.
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code
Embed this explainer
Drop the interactive walkthrough into a blog or docs. Views never cost a credit.
<iframe src="https://highlit.co/explainers/filtered-paginated-json-apis-in-laravel-explained-php-63db/embed?autoplay=1" width="100%" height="520" loading="lazy" style="border:0"></iframe>
Autoplay is on by default — add ?autoplay=0 to start paused.