docs(filters): document enhanced QueryParameter syntax on old filters by vinceAmstoutz · Pull Request #2227 · api-platform/docs · GitHub
Skip to content

docs(filters): document enhanced QueryParameter syntax on old filters - #2227

Merged
soyuka merged 3 commits into
api-platform:4.2from
vinceAmstoutz:docs/improve-query-parameter-doc-on-properties
Dec 5, 2025
Merged

soyuka merged 3 commits into
api-platform:4.2from
vinceAmstoutz:docs/improve-query-parameter-doc-on-properties

Conversation

@vinceAmstoutz

@vinceAmstoutz vinceAmstoutz commented Nov 4, 2025 •

Copy link
Copy Markdown
Member

Completes #2203

And here to avoid these issues:

Main changes:

  • Added examples for Date, Boolean, Numeric, Range, Exists, and Order filters using the QueryParameter approach. Included notes on configuring custom strategies, handling null values, and enabling filters for nested properties.
  • Add other warnings to promote the QueryParameter attribute over ApiFilter, as the latter is deprecated and scheduled for removal in API Platform 5.0.
  • Fix several typos

@vinceAmstoutz
vinceAmstoutz requested a review from soyuka November 4, 2025 10:47
@vinceAmstoutz vinceAmstoutz self-assigned this Nov 4, 2025
@vinceAmstoutz vinceAmstoutz changed the title docs(filters): document enhanced QueryParameter syntax and new filter… docs(filters): document enhanced QueryParameter syntax on old filters Nov 4, 2025
@vinceAmstoutz
vinceAmstoutz force-pushed the docs/improve-query-parameter-doc-on-properties branch from 1409cd7 to 4340fda Compare November 4, 2025 10:59
@ttskch

ttskch commented Nov 5, 2025

Copy link
Copy Markdown
Contributor

Comment thread core/doctrine-filters.md Outdated
Comment thread core/doctrine-filters.md Outdated
Comment thread core/doctrine-filters.md Outdated
Comment thread core/doctrine-filters.md Outdated
Comment thread core/doctrine-filters.md Outdated
Comment thread core/doctrine-filters.md Outdated
Comment thread core/doctrine-filters.md Outdated
Comment thread core/doctrine-filters.md Outdated
Comment thread core/doctrine-filters.md Outdated
Comment thread core/doctrine-filters.md Outdated
Comment thread core/doctrine-filters.md Outdated
Comment thread core/doctrine-filters.md Outdated
Comment thread core/doctrine-filters.md Outdated
Comment thread core/doctrine-filters.md Outdated

@aegypius aegypius left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

polish: I made a bunch of suggestions regarding spelling.‏

@soyuka

soyuka commented Dec 4, 2025

Copy link
Copy Markdown
Member
  1. parameters argument using associative array vs. key argument:
    When you define parameters using parameters: ['search[:property]' => new QueryParameter()], the Parameters collection internally uses 'search[:property]' as a key for the Parameter object. However, the QueryParameter object itself (the new QueryParameter()) initially has key: null. The error "A Parameter should have a key" likely occurs if another part of the system attempts to access $parameter->getKey() before the ApiPlatform\Metadata\Resource\Factory\ParameterResourceMetadataCollectionFactory::getDefaultParameters method has a chance to explicitly call $parameter = $parameter->withKey($key) to set the key on the Parameter object itself.
    Resolution: Your observation is correct. Always use new QueryParameter(key: 'search[:property]', /* ... */) to explicitly set the key on the QueryParameter object during its construction.

  2. Directly passing filter instance vs. service ID:
    The ApiPlatform\Doctrine\Orm\Extension\ParameterExtension and ApiPlatform\Doctrine\Odm\Extension\ParameterExtension (and ApiPlatform\Laravel\Eloquent\Extension\FilterQueryExtension) are designed to handle both filter instances and service IDs. The relevant code snippets (e.g., in ParameterExtension::applyFilter):

    // ...
    $filter = match (true) {
        $filterId instanceof FilterInterface => $filterId,
        \is_string($filterId) && $this->filterLocator->has($filterId) => $this->filterLocator->get($filterId),
        default => null,
    };
    // ...

    This match statement explicitly supports $filterId instanceof FilterInterface.
    The issue you're observing stems from how these directly instantiated filters are configured. When a filter is retrieved as a service, its constructor dependencies (like ManagerRegistry, LoggerInterface, or an initial $properties array) are automatically injected by the Dependency Injection Container. When you use new DateFilter(), these dependencies default to null.
    While ApiPlatform\Doctrine\Common\ParameterExtensionTrait::configureFilter attempts to set ManagerRegistry and LoggerInterface if the filter implements the AwareInterface (which AbstractFilter does), and also sets $properties for PropertyAwareFilterInterface, the initial state of the filter and how its getDescription method (ExistsFilterTrait::getDescription, DateFilterTrait::getDescription) uses a potentially null internal $properties array can lead to incorrect behavior, especially in OpenAPI/Hydra documentation generation or when filters expect specific property configurations.
    Resolution: It's best practice to define your filters as services in your services.yaml and pass their service IDs. This ensures proper dependency injection and consistent behavior.

About the other issues we refactored the code for the :property placeholder it should solve most of the problems, note that now there's a key for parameters and a property for each of these parameters.

Comment thread core/doctrine-filters.md Outdated
Comment thread core/doctrine-filters.md
Comment thread core/doctrine-filters.md Outdated
Comment thread core/doctrine-filters.md Outdated
Comment thread core/doctrine-filters.md
vinceAmstoutz and others added 3 commits December 5, 2025 12:16
… usage

- Updated documentation to promote the `QueryParameter` attribute over `ApiFilter`, as the latter is deprecated and scheduled for removal in API Platform 5.0.

- Added examples for Date, Boolean, Numeric, Range, Exists, and Order filters using the `QueryParameter` approach. Included notes on configuring custom strategies, handling null values, and enabling filters for nested properties.
Co-authored-by: Nicolas LAURENT <aegypius@users.noreply.github.com>
@soyuka
soyuka force-pushed the docs/improve-query-parameter-doc-on-properties branch from 34bec11 to 557c93e Compare December 5, 2025 11:16
@soyuka

soyuka commented Dec 5, 2025

Copy link
Copy Markdown
Member

Many thanks for the work on this! I'll fix linting issues in another PR!

@soyuka
soyuka merged commit 3011261 into api-platform:4.2 Dec 5, 2025
1 check failed
@ttskch

ttskch commented Dec 29, 2025

Copy link
Copy Markdown
Contributor

@soyuka

#2227 (comment)

  1. Directly passing filter instance vs. service ID:

Thank you for the detailed explanation.

I finally understand the conditions for reproduction. I declare parameters as follows in the apiResource() method instead of using class attributes. This was the direct cause.

#[ORM\Entity(repositoryClass: ArticleRepository::class)]
class Article
{
    // ...

    public static function apiResource(): array
    {
        return [
            new GetCollection(
                parameters: [
                    new QueryParameter(filter: new DateFilter(), key: ':property', properties: ['date']),
                ],
            ),
        ];
    }
}

If we declare it as follows using class attributes, the output will be correct.

#[GetCollection(
    parameters: [
        new QueryParameter(filter: new DateFilter(), key: ':property', properties: ['date']),
    ],
)]
#[ORM\Entity(repositoryClass: ArticleRepository::class)]
class Article
{
    // ...
}

Incidentally, even if we declare it as follows using a PHP resource file, OpenAPI/Hydra still does not output correctly.

<?php

use ApiPlatform\Doctrine\Orm\Filter\DateFilter;
use ApiPlatform\Metadata\ApiResource;
use ApiPlatform\Metadata\GetCollection;
use ApiPlatform\Metadata\Operations;
use ApiPlatform\Metadata\QueryParameter;
use App\Entity\Article;

return (new ApiResource())
    ->withClass(Article::class)
    ->withOperations(new Operations([
        new GetCollection(
            parameters: [
                new QueryParameter(filter: new DateFilter(), key: ':property', properties: ['date']),
            ],
        ),
    ]))
;

Do you know what the cause is?

@ttskch

ttskch commented Jan 7, 2026

Copy link
Copy Markdown
Contributor

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants