omaressaouaf / query-builder-criteria
Define reusable query criteria for filtering, sorting, search, field selection, and includes in Laravel Eloquent models
Requires
- php: ^8.2|^8.3|^8.4
- illuminate/contracts: ^10.0||^11.0||^12.0
- spatie/laravel-query-builder: ^6.0
Requires (Dev)
- laravel/pint: ^1.21
- orchestra/testbench: ^8.0
- phpunit/phpunit: ^10.5
This package is not auto-updated.
Last update: 2025-05-10 13:36:28 UTC
README
Introduction:
Query Builder Criteria is a Laravel package that extends Spatie's Laravel Query Builder, providing a structured way to define query filters, sorting, includes, and search functionality using reusable criteria classes.
With this package, you can:
- ✅ Define query logic in a clean, structured manner
- ✅ Apply filters, sorts, includes, and field selections effortlessly
- ✅ Support search and full-text search and custom query aliases
- ✅ Merge multiple criteria dynamically for flexible querying
Built on top of Spatie’s Query Builder, this package removes repetitive query logic, keeping your controllers and models clean and maintainable. 🚀
🔗 Example:
Instead of manually handling query parameters, just define a Criteria Class:
class PostCriteria extends Criteria { protected array $filters = ['title', 'slug', 'user']; protected array $sorts = ['published_at', 'created_at']; protected array $includes = ['user']; }
And apply it with one line of code:
$posts = Post::query()->queryByCriteria(PostCriteria::class)->get();
✨ No more manual query handling – just define once and reuse!
Table of Contents
- Installation
- Get Started
- Criteria configuration
- Applying Criteria to a Model
- Querying Data
- Passing Criteria to Scope
- Credits
- License
Installation
Install via Composer:
composer require omaressaouaf/query-builder-criteria
Publishing the Configuration
After installation, you can publish the package configuration file using:
php artisan vendor:publish --provider="Omaressaouaf\QueryBuilderCriteria\QueryBuilderCriteriaServiceProvider"
This will create a configuration file in config/query-builder-criteria.php
, allowing you to customize the default behavior.
<?php return [ /** * The name of the search_query_parameter inside the query string * For example: GET /users?search_query=john */ 'search_query_parameter' => 'search_query', /** * Split the search query into an array of terms to compare to * This is applied for all criteria unless overridden inside the criteria class */ 'split_search_into_terms' => false, /** * Define a default sorts for all criteria * This is applied for all criteria unless overridden inside the criteria class * * Possible value types: array, string, null */ 'default_sorts' => null, ];
Get Started
This package allows you to define query criteria for your models, enabling filtering, sorting, field selection, and more.
Example: Post Criteria
Let's say we have a Post
model. We want to allow users to:
- Filter by
title
,id
, andslug
. - Include related
user
model. - Sort by
published_at
. - Select specific fields like
title
andbody
.
We define these rules in a criteria class:
namespace App\Criteria; use Omaressaouaf\QueryBuilderCriteria\Criteria as BaseCriteria; class PostCriteria extends BaseCriteria { protected array $filters = ['title']; protected array $exactFilters = ['id', 'slug']; protected array $sorts = ['published_at']; protected array $includes = ['user']; protected array $fields = ['id', 'title', 'body', 'published_at', 'user_id']; }
Now, in our Post
model:
namespace App\Models; use Illuminate\Database\Eloquent\Model; use Omaressaouaf\QueryBuilderCriteria\QueryableByCriteria; use Omaressaouaf\QueryBuilderCriteria\Tests\Criteria\PostCriteria; class Post extends Model { use QueryableByCriteria; protected function defaultQueryBuilderCriteria(): array|string { return PostCriteria::class; } }
Then we can call the scope
Post::query()->queryByCriteria()->get();
Now we can query posts like this:
GET /posts?filter[title]=Hello&include=user&sort=-published_at&fields[posts]=id,title
Criteria configuration
Filters
URL Query Example:
GET /posts?filter[title]=Hello World&filter[id]=1&filter[user]=5&filter[trashed]=with
Configuration:
protected array $filters = ['title']; protected array $exactFilters = ['id', 'slug']; protected array $belongsToFilters = ['user']; protected array $scopeFilters = ['published_before']; protected string|bool|array|null $trashedFilter = true;
- filters: Allows filtering by
title
. - exactFilters: Enables exact filtering on
id
andslug
. - belongsToFilters: Supports filtering by related
user
model. - scopeFilters: Uses model scopes like
published_before
. - trashedFilter: Enables soft-deleted filter. (Possible values :
with
,only
)
Sorting
URL Query Example:
GET /posts?sort=-published_at
Configuration:
protected array|string $defaultSorts = '-created_at'; protected array $sorts = ['published_at', 'created_at'];
- defaultSorts: Defaults to sorting by
created_at
in descending order. - sorts: Allows sorting by
published_at
andcreated_at
.
Includes
URL Query Example:
GET /posts?include=user,commentsCount,commentsExists
Configuration:
protected array $includes = ['user']; protected array $countIncludes = ['comments']; protected array $existsIncludes = ['comments'];
- includes: Enables including related models (
user
). - countIncludes: Enables including only count of related records (
comments_count
). - existsIncludes: Enables including only whether related models exist (
has_comments
).
Field Selection
URL Query Example:
GET /posts?fields[posts]=id,slug,title,body,published_at
Configuration:
protected array $defaultFields = ['id', 'slug', 'title']; protected array $fields = ['id', 'slug', 'title', 'body', 'published_at', 'user_id', 'created_at', 'updated_at'];
- defaultFields: Default fields returned in queries when nothing specified in query param.
- fields: Lists all fields that can be selected.
Search
URL Query Example:
GET /posts?filter[search_query]=hello world
Configuration:
protected array $searches = ['slug', 'title', 'user.name']; protected array $fullTextSearches = ['body', 'user.bio']; protected ?bool $splitSearchIntoTerms = false;
- searches: Supports searching in
slug
,title
, anduser.name
. - fullTextSearches: Full-text search columns
body
anduser.bio
. - splitSearchIntoTerms: Enable or Disable term splitting in searches.
Aliases
You can define an alias for a filter to keep your database structure hidden and make URLs more readable. For example, if your users table has a user_passport_full_name
column, exposing it directly in the API isn't ideal. Instead, you can assign a more user-friendly alias:
URL Query Example:
GET /posts?filter[name]=John
Configuration:
protected array $filters = [ 'name' => 'user_passport_full_name' ];
This pattern is applicable for all filters, sorts and includes
Advanced Features
Advanced Filters, Sorting, and Includes
For more complex filters, sorts, and includes like specifying ignored and default values, custom callback filters and more you can override the following methods in your Criteria
class, these will be merged with the declared properties
Example Configuration
use Spatie\QueryBuilder\AllowedFilter; use Spatie\QueryBuilder\AllowedSort; use Spatie\QueryBuilder\AllowedInclude; protected function advancedFilters(): array { return [ AllowedFilter::callback('cb_filter_1', fn () => ''), ]; } protected function advancedDefaultSorts(): array { return [ AllowedSort::callback('cb_sort_1', fn () => ''), ]; } protected function advancedSorts(): array { return [ AllowedSort::callback('cb_sort_1', fn () => ''), ]; } protected function advancedIncludes(): array { return [ AllowedInclude::callback('cb_include', fn () => ''), ]; }
Refer to the Spatie Query Builder Documentation for more details on defining advanced filters, sorts, and includes.
Applying Criteria to a Model
Use the QueryableByCriteria
trait in your model:
namespace App\Models\Models; use Illuminate\Database\Eloquent\Model; use Omaressaouaf\QueryBuilderCriteria\QueryableByCriteria; use App\Criteria\PostCriteria; class Post extends Model { use QueryableByCriteria; protected function defaultQueryBuilderCriteria(): array|string { return PostCriteria::class; } }
Querying Data
return Post::query()->queryByCriteria()->get();
Passing Criteria to Scope
You can also pass a criteria class directly to the queryByCriteria
scope. This allows you to merge additional criteria with the default criteria for flexibility and reusability:
return Post::query()->queryByCriteria(CustomCriteria::class, AnotherCriteria::class)->get();
Credits
This package is based on Spatie's Laravel Query Builder.
Testing
Run unit tests:
composer test
License
This package is open-source and licensed under the MIT License.