API Platform version(s) affected: 4.3.17 (api-platform/laravel)
Description
ApiPlatform\Laravel\Eloquent\Filter\BooleanFilter::apply() accepts four string values for a boolean query parameter: 'true', 'false', '1', '0' (see BOOLEAN_VALUES map in the filter itself).
However, ApiPlatform\Laravel\Metadata\ParameterValidationResourceMetadataCollectionFactory::addSchemaValidation() auto-generates a Laravel validation constraint from the parameter's JSON schema. Because BooleanFilter::getSchema() returns ['type' => 'boolean'], the factory adds Laravel's built-in boolean validation rule to the parameter.
Laravel's boolean rule (Illuminate\Validation\Concerns\ValidatesAttributes::validateBoolean()) only accepts true, false, 1, 0, '1', '0' — it does not accept the strings 'true' / 'false'.
Since query string parameters always arrive as strings, this means:
?enabled=1 / ?enabled=0 → passes Laravel's boolean rule → filter applies correctly
?enabled=true / ?enabled=false → fails Laravel's boolean rule with a 422 Validation Error, before the request ever reaches BooleanFilter::apply()
So two out of the four values the filter's own code explicitly supports are rejected upstream by validation added by the framework itself, with an error message ("The enabled field must be true or false.") that is misleading given the actual literal values true/false are the ones being rejected.
How to reproduce
Any Eloquent resource with a BooleanFilter-backed QueryParameter:
#[GetCollection(
parameters: [
'enabled' => new QueryParameter(key: 'enabled', filter: BooleanFilter::class),
]
)]
class PaymentMethod extends Model
{
protected function casts(): array
{
return ['enabled' => 'boolean'];
}
}
GET /api/payment_methods?enabled=true
→ 422 {"detail":"The enabled field must be true or false.","violations":[{"propertyPath":"enabled","message":"The enabled field must be true or false."}]}
GET /api/payment_methods?enabled=1
→ 200 OK (filter applies as expected)
Possible Solution
Either:
- Change
ParameterValidationResourceMetadataCollectionFactory::addSchemaValidation() to use a custom in:true,false,1,0 rule (or a dedicated rule object) instead of Laravel's native boolean rule when the schema type is boolean, so the generated validation constraint matches exactly what BooleanFilter::apply() actually accepts.
- Or narrow
BooleanFilter::BOOLEAN_VALUES to only the values Laravel's boolean rule accepts ('1', '0'), and document that 'true'/'false' are not supported — though this is a more visible behavior change for existing users relying on 'true'/'false'.
Option 1 seems preferable since it fixes the validator to match the filter's documented/implemented contract rather than silently narrowing filter behavior.
Additional Context
BooleanFilter: vendor/api-platform/laravel/Eloquent/Filter/BooleanFilter.php
- Validation factory:
vendor/api-platform/laravel/Metadata/ParameterValidationResourceMetadataCollectionFactory.php
- Workaround applied in our app: send
1/0 instead of true/false from the frontend query builder (no backend change needed, since 1/0 already pass Laravel's boolean rule and are in BooleanFilter::BOOLEAN_VALUES).
- This was initially misdiagnosed as an authorization/permissions bug (403 from
AccessCheckerProvider) during application-level testing before being traced to this validation-layer 422/type mismatch — worth noting for anyone else debugging a similarly confusing failure mode, since the actual HTTP status code and body received during triage can vary depending on which stack layer surfaces the error first.
API Platform version(s) affected: 4.3.17 (
api-platform/laravel)Description
ApiPlatform\Laravel\Eloquent\Filter\BooleanFilter::apply()accepts four string values for a boolean query parameter:'true','false','1','0'(seeBOOLEAN_VALUESmap in the filter itself).However,
ApiPlatform\Laravel\Metadata\ParameterValidationResourceMetadataCollectionFactory::addSchemaValidation()auto-generates a Laravel validation constraint from the parameter's JSON schema. BecauseBooleanFilter::getSchema()returns['type' => 'boolean'], the factory adds Laravel's built-inbooleanvalidation rule to the parameter.Laravel's
booleanrule (Illuminate\Validation\Concerns\ValidatesAttributes::validateBoolean()) only acceptstrue, false, 1, 0, '1', '0'— it does not accept the strings'true'/'false'.Since query string parameters always arrive as strings, this means:
?enabled=1/?enabled=0→ passes Laravel'sbooleanrule → filter applies correctly?enabled=true/?enabled=false→ fails Laravel'sbooleanrule with a 422 Validation Error, before the request ever reachesBooleanFilter::apply()So two out of the four values the filter's own code explicitly supports are rejected upstream by validation added by the framework itself, with an error message ("The enabled field must be true or false.") that is misleading given the actual literal values
true/falseare the ones being rejected.How to reproduce
Any Eloquent resource with a
BooleanFilter-backedQueryParameter:#[GetCollection( parameters: [ 'enabled' => new QueryParameter(key: 'enabled', filter: BooleanFilter::class), ] )] class PaymentMethod extends Model { protected function casts(): array { return ['enabled' => 'boolean']; } }Possible Solution
Either:
ParameterValidationResourceMetadataCollectionFactory::addSchemaValidation()to use a customin:true,false,1,0rule (or a dedicated rule object) instead of Laravel's nativebooleanrule when the schema type isboolean, so the generated validation constraint matches exactly whatBooleanFilter::apply()actually accepts.BooleanFilter::BOOLEAN_VALUESto only the values Laravel'sbooleanrule accepts ('1','0'), and document that'true'/'false'are not supported — though this is a more visible behavior change for existing users relying on'true'/'false'.Option 1 seems preferable since it fixes the validator to match the filter's documented/implemented contract rather than silently narrowing filter behavior.
Additional Context
BooleanFilter:vendor/api-platform/laravel/Eloquent/Filter/BooleanFilter.phpvendor/api-platform/laravel/Metadata/ParameterValidationResourceMetadataCollectionFactory.php1/0instead oftrue/falsefrom the frontend query builder (no backend change needed, since1/0already pass Laravel'sbooleanrule and are inBooleanFilter::BOOLEAN_VALUES).AccessCheckerProvider) during application-level testing before being traced to this validation-layer 422/type mismatch — worth noting for anyone else debugging a similarly confusing failure mode, since the actual HTTP status code and body received during triage can vary depending on which stack layer surfaces the error first.