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 class NameParser {
Parsing a full name into components in PHP
string-parsing
arrays
normalization
Intermediate
8 steps
php
<?php namespace App\Services\Checkout;
Validating coupons with Laravel's Pipeline
pipeline
chain of responsibility
transactions
Intermediate
7 steps
php
<?php namespace App\Services;
How a password strength validator works in PHP
validation
regular-expressions
data-driven
Intermediate
8 steps
php
<?php namespace App\Services;
Building a cached daily leaderboard in Laravel
caching
aggregation
eager-loading
Intermediate
9 steps
php
<?php final class RotatingFileLogger {
How a rotating file logger works in PHP
logging
file-rotation
io
Intermediate
9 steps
php
class TwoFactorController extends Controller { public function show(Request $request): View|RedirectResponse {
Two-factor auth challenge flow in Laravel
authentication
two-factor
middleware
Intermediate
10 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.