Builder Macros
Registered on Illuminate\Database\Eloquent\Builder by the service provider. Available on all Eloquent queries.
initializer
->initializer(bool $orderBy = true): BuilderApply request-based filters, sorting, and scopes.
How it works:
- Reads
?filters={"scope":"value"}— decodes JSON, converts keys to StudlyCase, calls matchingscope{Name}methods - Reads
?sortBy=column&descending=true— applies ordering - If model implements
Sortableand no sort params given, usessortByDefaults() - Default sort:
iddescending
// Standard usage
$query = Post::query()->initializer();
// Disable sorting
$query = Post::query()->initializer(orderBy: false);Filter examples:
?filters={"active":1} → $query->active(1)
?filters={"queryFilter":"test"} → $query->queryFilter("test")
?filters={"my_scope":1} → $query->myScope(1) (StudlyCase conversion)Invalid JSON in filters is silently ignored. Filters with null values are skipped. Unknown scope names are skipped.
likeWhere
->likeWhere(array $attributes, ?string $searchTerm = null): BuilderMulti-column LIKE search with relation support.
// Simple columns
Post::query()->likeWhere(['name', 'desc'], 'laravel');
// WHERE (name LIKE '%laravel%' OR desc LIKE '%laravel%')
// Relation columns (colon syntax: 'relation:col1,col2')
Post::query()->likeWhere(['name', 'user:name,email'], 'test');
// WHERE (name LIKE '%test%' OR EXISTS (
// SELECT * FROM users WHERE ... AND (name LIKE '%test%' OR email LIKE '%test%')
// ))
// Null/empty search returns query unchanged
Post::query()->likeWhere(['name'], null); // No-op
Post::query()->likeWhere(['name'], ''); // No-oppaginates
->paginates(array $columns = ['*'], string $pageName = 'page', ?int $page = null): PaginatorLength-aware pagination using rowsPerPage request parameter.
Post::query()->paginates(); // Default: 15 per page
Post::query()->paginates(['id', 'name']); // Specific columns
Post::query()->paginates(['*'], 'p', 2); // Custom page name & pageBehavior:
- Reads
rowsPerPagefrom request query string - Respects
fast-api.pagination.max_per_page(clamps to max) - When
rowsPerPage=0andfast-api.pagination.allow_all=true: counts all records and uses that as per-page (effectively returns all) - Negative values fall back to
default_per_page
simplePaginates
->simplePaginates(array $columns = ['*'], string $pageName = 'page', ?int $page = null): PaginatorSimple pagination (no total count query). Same parameters and behavior as paginates() but uses simplePaginate() internally.
cursorPaginates
->cursorPaginates(array $columns = ['*'], ?string $cursorName = null, ?Cursor $cursor = null): CursorPaginatorCursor-based pagination. Best for large datasets and infinite scroll.
Post::query()->cursorPaginates();
Post::query()->cursorPaginates(['id', 'name'], 'cursor');Uses rowsPerPage request parameter. Negative values fall back to default. Values above max are clamped.
withAggregates
->withAggregates(array $aggregates): BuilderApply multiple aggregate functions in a single call.
Post::query()->withAggregates([
'comments' => 'id', // withAggregate('comments', 'id')
'ratings' => ['score', 'avg'], // withAggregate('ratings', 'score', 'avg')
'views' => ['count', 'sum'], // withAggregate('views', 'count', 'sum')
]);Array format:
'relation' => 'column'— callswithAggregate($relation, $column)'relation' => ['column', 'function']— callswithAggregate($relation, $column, $function)
Empty array is a no-op.
withCountWhereHas
->withCountWhereHas(
string $relation,
?Closure $callback = null,
string $operator = '>=',
int $count = 1
): BuilderAdds withCount AND whereHas in one call — counts the relation and filters to only include models that have matching related records.
// Posts with at least 1 comment, including comment count
Post::query()->withCountWhereHas('comments');
// Posts with approved comments
Post::query()->withCountWhereHas('comments', function ($q) {
$q->where('approved', true);
});
// Posts with 5+ comments
Post::query()->withCountWhereHas('comments', null, '>=', 5);
// Posts with exactly 3 tags
Post::query()->withCountWhereHas('tags', null, '=', 3);Supports colon syntax for relation with constraining: 'comments:approved'.
orWithCountWhereHas
->orWithCountWhereHas(
string $relation,
?Closure $callback = null,
string $operator = '>=',
int $count = 1
): BuilderOR variant — uses orWhereHas instead of whereHas.
// Posts that have comments OR tags
Post::query()
->withCountWhereHas('comments')
->orWithCountWhereHas('tags');