Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 61dda89

Browse files
committed
improvements
1 parent 743247c commit 61dda89

File tree

1 file changed

+193
-6
lines changed

1 file changed

+193
-6
lines changed

‎core/filters.md‎

Lines changed: 193 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,108 @@ class Book
135135
```
136136

137137
This configuration creates a dynamic parameter. API clients can now filter on any of the properties configured in the `SearchFilter` (in this case, `title` and `description`) by using a URL like `/books?search[title]=Ring` or `/books?search[description]=journey`.
138+
139+
When using the `:property` placeholder, API Platform automatically populates the parameter's `extraProperties` with a `_properties` array containing all the available properties for the filter. Your filter can access this information:
140+
141+
```php
142+
public function apply(QueryBuilder $queryBuilder, QueryNameGeneratorInterface $queryNameGenerator, string $resourceClass, ?Operation $operation = null, array $context = []): void
143+
{
144+
$parameter = $context['parameter'] ?? null;
145+
$properties = $parameter?->getExtraProperties()['_properties'] ?? [];
146+
147+
// $properties contains: ['title' => 'title', 'description' => 'description']
148+
// This allows your filter to know which properties are available for filtering
149+
}
150+
```
151+
152+
### Restricting Properties with `:property` Placeholders
153+
154+
There are two different approaches to property restriction depending on your filter design:
155+
156+
#### 1. Legacy Filters (SearchFilter, etc.) - Not Recommended
157+
158+
> [!WARNING]
159+
> Filters that extend `AbstractFilter` with pre-configured properties are considered legacy. They don't support property restriction via parameters and may be deprecated in future versions. Consider using per-parameter filters instead for better flexibility and performance.
160+
161+
For existing filters that extend `AbstractFilter` and have pre-configured properties, the parameter's `properties` does **not** restrict the filter's behavior. These filters use their own internal property configuration:
162+
163+
```php
164+
<?php
165+
// This does NOT restrict SearchFilter - it processes all its configured properties
166+
'search[:property]' => new QueryParameter(
167+
properties: ['title', 'author'], // Only affects _properties, doesn't restrict filter
168+
filter: new SearchFilter(properties: ['title' => 'partial', 'description' => 'partial'])
169+
)
170+
171+
// To restrict legacy filters, configure them with only the desired properties:
172+
'search[:property]' => new QueryParameter(
173+
filter: new SearchFilter(properties: ['title' => 'partial', 'author' => 'exact'])
174+
)
175+
```
176+
177+
#### 2. Per-Parameter Filters (Recommended)
178+
179+
> [!NOTE]
180+
> Per-parameter filters are the modern approach. They provide better performance (only process requested properties), cleaner code, and full support for parameter-based property restriction.
181+
182+
Modern filters that work on a per-parameter basis can be effectively restricted using the parameter's `properties`:
183+
184+
```php
185+
<?php
186+
// src/Filter/PartialSearchFilter.php
187+
use ApiPlatform\Doctrine\Orm\Filter\FilterInterface;
188+
use ApiPlatform\Doctrine\Orm\Util\QueryNameGeneratorInterface;
189+
use ApiPlatform\Metadata\Operation;
190+
use Doctrine\ORM\QueryBuilder;
191+
192+
final class PartialSearchFilter implements FilterInterface
193+
{
194+
public function apply(QueryBuilder $queryBuilder, QueryNameGeneratorInterface $queryNameGenerator, string $resourceClass, ?Operation $operation = null, array $context = []): void
195+
{
196+
$parameter = $context['parameter'];
197+
$value = $parameter->getValue();
198+
199+
// Get the property for this specific parameter
200+
$property = $parameter->getProperty();
201+
$alias = $queryBuilder->getRootAliases()[0];
202+
$field = $alias.'.'.$property;
203+
204+
$parameterName = $queryNameGenerator->generateParameterName($property);
205+
206+
$queryBuilder
207+
->andWhere($queryBuilder->expr()->like('LOWER('.$field.')', ':'.$parameterName))
208+
->setParameter($parameterName, '%'.strtolower($value).'%');
209+
}
210+
}
211+
```
212+
213+
```php
214+
<?php
215+
// api/src/Resource/Book.php
216+
#[ApiResource(operations: [
217+
new GetCollection(
218+
parameters: [
219+
// This WILL restrict to only title and author properties
220+
'search[:property]' => new QueryParameter(
221+
properties: ['title', 'author'], // Only these properties get parameters created
222+
filter: new PartialSearchFilter()
223+
)
224+
]
225+
)
226+
])]
227+
class Book {
228+
// ...
229+
}
230+
```
231+
232+
**How it works:**
233+
1. API Platform creates individual parameters: `search[title]` and `search[author]` only
234+
2. URLs like `/books?search[description]=foo` are ignored (no parameter exists)
235+
3. Each parameter calls the filter with its specific property via `$parameter->getProperty()`
236+
4. The filter processes only that one property
237+
238+
This approach is recommended for new filters as it's more flexible and allows true property restriction via the parameter configuration.
239+
138240
Note that invalid values are usually ignored by our filters, use [validation](#parameter-validation) to trigger errors for wrong parameter values.
139241

140242
## OpenAPI and JSON Schema
@@ -291,7 +393,7 @@ Parameter Providers are powerful services that can inspect, transform, or provid
291393

292394
### `IriConverterParameterProvider`
293395

294-
This built-in provider takes an IRI string (e.g., `/users/1`) and converts it into the corresponding Doctrine entity object.
396+
This built-in provider takes an IRI string (e.g., `/users/1`) and converts it into the corresponding Doctrine entity object. It supports both single IRIs and arrays of IRIs.
295397

296398
```php
297399
<?php
@@ -307,6 +409,10 @@ use ApiPlatform\State\ParameterProvider\IriConverterParameterProvider;
307409
uriTemplate: '/with_parameters_iris',
308410
parameters: [
309411
'dummy' => new QueryParameter(provider: IriConverterParameterProvider::class),
412+
'related' => new QueryParameter(
413+
provider: IriConverterParameterProvider::class,
414+
extraProperties: ['fetch_data' => true] // Forces fetching the entity data
415+
),
310416
],
311417
provider: [self::class, 'provideDummyFromParameter'],
312418
)
@@ -316,11 +422,22 @@ class WithParameter
316422
public static function provideDummyFromParameter(Operation $operation, array $uriVariables = [], array $context = []): object|array
317423
{
318424
// The value has been transformed from an IRI to an entity by the provider.
319-
return $operation->getParameters()->get('dummy')->getValue();
425+
$dummy = $operation->getParameters()->get('dummy')->getValue();
426+
427+
// If multiple IRIs were provided as an array, this will be an array of entities
428+
$related = $operation->getParameters()->get('related')->getValue();
429+
430+
return $dummy;
320431
}
321432
}
322433
```
323434

435+
#### Configuration Options
436+
437+
The `IriConverterParameterProvider` supports the following options in `extraProperties`:
438+
439+
- **`fetch_data`**: Boolean (default: `false`) - When `true`, forces the IRI converter to fetch the actual entity data instead of just creating a reference.
440+
324441
### `ReadLinkParameterProvider`
325442

326443
This provider fetches a linked resource from a given identifier. This is useful when you need to load a related entity to use later, for example in your own state provider.
@@ -370,14 +487,35 @@ The provider will:
370487
- Optionally use the `uri_template` from `extraProperties` to construct the proper operation for loading the resource
371488
- Return the loaded entity, making it available in your state provider
372489

373-
You can also control error handling by setting `throw_not_found` to `false` in the `extraProperties` to prevent exceptions when the linked resource is not found:
490+
#### Array Support
491+
492+
Both `IriConverterParameterProvider` and `ReadLinkParameterProvider` support processing arrays of values. When you pass an array of identifiers or IRIs, they will return an array of resolved entities:
493+
494+
```php
495+
// For IRI converter: ?related[]=/dummies/1&related[]=/dummies/2
496+
// For ReadLink provider: ?dummies[]=uuid1&dummies[]=uuid2
497+
'items' => new QueryParameter(
498+
provider: ReadLinkParameterProvider::class,
499+
extraProperties: ['resource_class' => Dummy::class]
500+
)
501+
```
502+
503+
#### Configuration Options
504+
505+
You can control the behavior of `ReadLinkParameterProvider` with these `extraProperties`:
506+
507+
- **`resource_class`**: The class of the resource to load
508+
- **`uri_template`**: Optional URI template for the linked resource operation
509+
- **`uri_variable`**: Name of the URI variable to use when building URI variables array
510+
- **`throw_not_found`**: Boolean (default: `true`) - Whether to throw `NotFoundHttpException` when resource is not found
374511

375512
```php
376513
'dummy' => new QueryParameter(
377514
provider: ReadLinkParameterProvider::class,
378515
extraProperties: [
379516
'resource_class' => Dummy::class,
380-
'throw_not_found' => false // Won't throw NotFoundHttpException if resource is missing
517+
'throw_not_found' => false, // Won't throw NotFoundHttpException if resource is missing
518+
'uri_variable' => 'customId' // Use 'customId' as the URI variable name
381519
]
382520
)
383521
```
@@ -445,7 +583,8 @@ final class RegexpFilter implements FilterInterface
445583
{
446584
public function apply(QueryBuilder $queryBuilder, QueryNameGeneratorInterface $queryNameGenerator, string $resourceClass, ?Operation $operation = null, array $context = []): void
447585
{
448-
$value = $context['parameter']?->getValue();
586+
$parameter = $context['parameter'] ?? null;
587+
$value = $parameter?->getValue();
449588

450589
// The parameter may not be present.
451590
// It's recommended to add validation (e.g., `required: true`) on the Parameter attribute
@@ -456,9 +595,15 @@ final class RegexpFilter implements FilterInterface
456595

457596
$alias = $queryBuilder->getRootAliases()[0];
458597
$parameterName = $queryNameGenerator->generateParameterName('regexp_name');
598+
599+
// Access the parameter's property or use the parameter key as fallback
600+
$property = $parameter->getProperty() ?? $parameter->getKey() ?? 'name';
601+
602+
// You can also access filter context if the parameter provides it
603+
$filterContext = $parameter->getFilterContext() ?? null;
459604

460605
$queryBuilder
461-
->andWhere(sprintf('REGEXP(%s.name, :%s) = 1', $alias, $parameterName))
606+
->andWhere(sprintf('REGEXP(%s.%s, :%s) = 1', $alias, $property, $parameterName))
462607
->setParameter($parameterName, $value);
463608
}
464609

@@ -568,3 +713,45 @@ class LogEntry {}
568713
| `hydra` | Hide the parameter from Hydra documentation (`false`). |
569714
| `security` | A [Symfony expression](https://symfony.com/doc/current/security/expressions.html) to control access to the parameter. |
570715

716+
## Parameter Security
717+
718+
You can secure individual parameters using Symfony expression language. When a security expression evaluates to `false`, the parameter will be ignored and treated as if it wasn't provided.
719+
720+
```php
721+
<?php
722+
// api/src/Resource/SecureResource.php
723+
use ApiPlatform\Metadata\ApiResource;
724+
use ApiPlatform\Metadata\GetCollection;
725+
use ApiPlatform\Metadata\HeaderParameter;
726+
use ApiPlatform\Metadata\QueryParameter;
727+
728+
#[ApiResource(operations: [
729+
new GetCollection(
730+
uriTemplate: '/secure_resources',
731+
parameters: [
732+
'name' => new QueryParameter(
733+
security: 'is_granted("ROLE_ADMIN")'
734+
),
735+
'auth' => new HeaderParameter(
736+
security: '"secured" == auth',
737+
description: 'Only accessible when auth header equals "secured"'
738+
),
739+
'secret' => new QueryParameter(
740+
security: '"secured" == secret',
741+
description: 'Only accessible when secret parameter equals "secured"'
742+
)
743+
]
744+
)
745+
])]
746+
class SecureResource
747+
{
748+
// ...
749+
}
750+
```
751+
752+
In the security expressions, you have access to:
753+
- Parameter values by their key name (e.g., `auth`, `secret`)
754+
- Standard security functions like `is_granted()`
755+
- The current user via `user`
756+
- Request object via `request`
757+

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /