Advanced Usage
Intercepting and Modifying Rules
When your FormRequest requires custom validation alongside the package’s
generated rules, use the $this->queryParamRules() mixin method to merge them
seamlessly.
#[MapQueryEngine(User::class)]
class IndexUserRequest extends FormRequest
{
public function rules(): array
{
return array_merge(
$this->queryParamRules(),
[
'custom_header' => 'required|string',
]
);
}
}
Global Registry (Alternative Resolution)
If you cannot modify the FormRequest class directly (e.g., in a third-party package), register the request-model mapping globally:
use Victormgomes\LaravelQueryEngine\Support\ModelRegistry;
ModelRegistry::register(IndexUserRequest::class, User::class);
Automatic Type Casting
Values are automatically cast into native PHP types based on your schema before hitting Eloquent.
| Database Type | Cast | Example |
|---|---|---|
integer, bigint |
(int) |
100 |
float, decimal |
(float) |
9.99 |
boolean |
filter_var() |
true |
date, datetime |
Carbon::parse() |
Carbon instance |
Frontend Dynamic Filters (Metadata)
The package provides methods to export the schema for frontend applications building dynamic filter UIs.
use Victormgomes\LaravelQueryEngine\Support\Resource;
// Deduplicated schema (Recommended for Frontend integration)
return Resource::getFilterSchema(User::class);
// Full exhaustive schema with all aliases and syntax mapping
return Resource::getQueryGuide(User::class);
The #[QueryOptions] Attribute
The core configuration for what is exposed over the API happens via the
#[QueryOptions] attribute directly on your Eloquent Model. It acts as a
gatekeeper, leveraging a “Whitelist vs Blacklist” architecture.
use Victormgomes\LaravelQueryEngine\Attributes\QueryOptions;
#[QueryOptions(
// Features can be disabled entirely
filters: true,
// Blacklisting (Auto-discovery enabled, these are disabled)
disableFilters: ['password', 'secret_token'],
disableFields: ['internal_id'],
// Whitelisting (Auto-discovery disabled, STRICTLY these are allowed)
// allowedFilters: ['id', 'name'],
// Security Opt-Ins (Disabled by default)
allowedScopes: ['active', 'ofRole'],
allowedAggregations: ['posts_count', 'comments_avg_rating'],
)]
class User extends Model
{
// ...
}
1. Filtering by Local Scopes
Scopes must be explicitly registered in allowedScopes. Once registered, they
act as virtual boolean/string filters.
// In Model: public function scopeActive($query) { ... }
// URL: ?filters[active]=true
If the scope accepts parameters (e.g. scopeOfRole($query, $role)):
// URL: ?filters[ofRole][eq]=admin
2. Selecting Accessors (Appends)
Accessors (getFirstNameAttribute or firstName(): Attribute) and properties
in the $appends array are automatically discovered and can be selected via the
fields parameter. You can block them using disableFields or strictly control
them via allowedFields.
// URL: ?fields[]=id&fields[]=name&fields[]=first_name
3. Requesting Aggregations (Counts & Sums)
Aggregations can cause performance issues if exposed globally. Therefore, you
must explicitly opt-in via allowedAggregations. The package supports _count,
_exists, _sum_column, _avg_column, _min_column, and _max_column.
// QueryOptions(allowedAggregations: ['posts_count', 'orders_sum_total'])
// URL: ?fields[]=id&fields[]=name&fields[]=posts_count&fields[]=orders_sum_total
4. Overriding Global Operators
Sometimes you want to globally allow a certain operator in
config/query-engine.php (like like or fts), but restrict it on a specific
model for performance reasons. You can use disableOperators or
allowedOperators.
#[QueryOptions(
// Disable expensive operators just for this model
disableOperators: ['fts', 'like'],
)]
class LogEntry extends Model
{
// ...
}