Skip to content

Spatie Parity Matrix

Every laravel-query-builder v7 feature, the URL it emits, the matching crisp-oquent builder method.

Filtering

Spatie featureURLBuilderSpatie version
Partial filter?filter[name]=john.filter('name', 'john')1.0+
Exact filter?filter[id]=1.filter('id', 1)1.0+
Comma-separated values?filter[id]=1,2,3.filter('id', [1,2,3])1.0+
Operator (DYNAMIC)?filter[salary]=>3000.where('salary', FilterOperator.GREATER_THAN, 3000)6.0+
BelongsTo?filter[post]=1.filter('post', 1)5.x+
Trashed (SoftDeletes)?filter[trashed]=with.withTrashed()1.0+
Trashed only?filter[trashed]=only.onlyTrashed()1.0+
Nullable?filter[deleted_at]=null.whereNull('deleted_at')7.0.1
Nullable not?filter[email]=not-null.whereNotNull('email')7.0.1
Custom array delimiter?filter[id]=1|2|3.delimiter('|') / setFilterDelimiter('|')7.2.0
Filter Group (OR)?filter[q]=John.filterGroup('q', 'John')7.3.0
Filter Group (AND)?filter[strict]=val.filterGroup('strict', 'val')7.3.0
Scope filter?filter[starts_before]=2026-12-31.filter('starts_before', '2026-12-31')1.0+
Custom callback filter?filter[xyz]=....filter('xyz', value)1.0+

Sorting

FeatureURLBuilder
Asc sort?sort=name.sortBy('name')
Desc sort?sort=-created_at.sortByDesc('created_at')
Multi-field?sort=-created_at,name.sortByDesc('created_at').sortBy('name')
Custom sort alias?sort=name-length.sortBy('name-length') (server: AllowedSort::custom)
Sort by aliased column?sort=-street.sortByDesc('street') (server: AllowedSort::field)

Including relations

FeatureURLBuilderSpatie version
Relation?include=posts.include('posts')1.0+
Multiple?include=posts,permissions.include('posts', 'permissions')1.0+
Nested?include=posts.comments.include('posts.comments')1.0+
Count aggregate?include=postsCount.includeCount('posts')4.x+
Exists aggregate?include=postsExists.includeExists('posts')5.x+
Sum aggregate?include=postsViewsSum.includeSum('postsViewsSum')7.0.0
Avg aggregate?include=postsViewsAvg.includeAvg('postsViewsAvg')7.0.0
Min aggregate?include=postsViewsMin.includeMin('postsViewsMin')7.0.0
Max aggregate?include=postsViewsMax.includeMax('postsViewsMax')7.0.0
Include alias?include=profile.include('profile') (server: AllowedInclude::relationship)4.x+
Custom include?include=....include(name) (server: AllowedInclude::custom)1.0+

Sparse fieldsets

FeatureURLBuilder
Per-resource fields?fields[users]=id,name.fields('users', 'id', 'name')
Fields on included?fields[authors]=id,name.fields('authors', 'id', 'name')

Append accessors

FeatureURLBuilder
Append accessor?append=full_name.append('full_name')
Multiple?append=full_name,avatar_url.append('full_name', 'avatar_url')

Pagination

FeatureURLBuilder
Page + size?page=2&per_page=25.paginate(2, 25)
Single page?page=1&per_page=15.paginate() (defaults)
All pagesn/a.all() (walks until exhausted)

What's not in crisp-oquent (yet)

  • Full JSON:API Fancy Filters URL syntax (?filter[g1][conjunction]=OR&filter[g1][conditions][...]) — listed as a follow-up in Spatie #1060. When the upstream PR lands, the corresponding builder DSL will follow.
  • groupNot / nested groups — also out-of-scope of #1060, planned for a follow-up.