feat(symfony): api:upgrade-filter codemod + filter fixture migration#8344
Merged
Conversation
soyuka
commented
Jun 24, 2026
| php-version: ${{ matrix.php }} | ||
| extensions: intl, bcmath, curl, openssl, mbstring | ||
| ini-values: memory_limit=-1 | ||
| tools: composer:2.9.8 |
Member
Author
There was a problem hiding this comment.
why version constraint on this?
| $entityClass = $this->getStateOptionsClass($operation, $operation->getClass()); | ||
|
|
||
| if ($resourceFilters) { | ||
| trigger_deprecation('api-platform/core', '4.4', \sprintf('Declaring filters on the "%s" operation through "Operation::$filters" is deprecated, use the "%s" attribute instead. It will be removed in 6.0.', $operation->getShortName(), QueryParameter::class)); |
Member
Author
There was a problem hiding this comment.
problem here: use parameters instead ? no point mentioning attribute here. i think we should only deprecate during metadata revert changes in this class.
|
|
||
| // Collapse identical filter deprecations to one warning per (filter, shortName) per | ||
| // generation: a filter exposing N parameters must not emit the same message N times. | ||
| $warned = []; |
Member
Author
There was a problem hiding this comment.
remove this these are old deprecations we'll remove these in 5.0 anyways
| $filtersDeprecations = array_values(array_filter($deprecations, static fn (string $m): bool => str_contains($m, 'Operation::$filters'))); | ||
| $this->assertCount(1, $filtersDeprecations, 'Declaring filters through Operation::$filters must trigger one deprecation per operation.'); | ||
| $this->assertStringContainsString('QueryParameter', $filtersDeprecations[0]); | ||
| } |
| * Resources whose filters cannot be expressed as distinct QueryParameters (e.g. an exact and a | ||
| * range filter on the same property) are reported and skipped. | ||
| */ | ||
| #[AsCommand(name: 'api:upgrade-filter', description: 'Upgrades legacy #[ApiFilter] declarations to QueryParameter')] |
Member
Author
There was a problem hiding this comment.
we should note that this command will be removed in 6.0
soyuka
commented
Jun 24, 2026
soyuka
commented
Jun 24, 2026
| return $v; | ||
| } | ||
|
|
||
| if (\is_string($v) && ('' === $v || 'null' === strtolower($v))) { |
Member
Author
There was a problem hiding this comment.
lets not cast null a default value should make this work, 'null' is not supported by new filter
soyuka
commented
Jun 25, 2026
1fb41fb to
7518b39
Compare
Add an `api:upgrade-filter` command that rewrites legacy `#[ApiFilter]` attributes into parameter-based filters via a php-parser visitor. Includes the mapper/resolver/visitor pipeline, collision/skip/name-conversion handling, DI wiring, service config and unit tests. CI runs the codemod with --force before the functional suite.
Run api:upgrade-filter over the test fixtures to move them onto parameter filters, and relocate the un-migrated #[ApiFilter] fixtures (Boolean, Numeric, Enum, Order, Search, Date/Range/Exists, ExceptionToStatus) into a Legacy/ suite that keeps covering the deprecated path.
Emit a deprecation for the #[ApiFilter] attribute and for Operation::$filters consumption, surfaced through the metadata factory and OpenAPI factory with de-duplication. Baseline the resulting deprecations.
ValueCaster now casts empty/null boolean params to false and empty/null numeric params consistently, and rejects empty values cast to a scalar native type with a 400 via BadRequestException (wired through the Laravel error renderer).
Co-authored-by: Antoine Bluchet <soyuka@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Migrates the functional-test fixtures off the legacy
#[ApiFilter]/ multi-strategy filters onto the canonicalQueryParameterset (4.4 deliverable §7), and ships theapi:upgrade-filtercodemod that performs the migration (deliverable §9, beyond the planned stub).The migration is dogfooded: the codemod runs over our own fixtures, the suite stays green on both paths, and the converted fixtures are committed.
Fixture migration (Option A)
Per family, fixtures move to the canonical filters; a focused
Legacy/regression suite keeps the deprecated paths alive:ExactFilter(+nativeType,castToNativeType)SortFilter(order[:property])Exact/Partial/Start/End/WordStart(+caseSensitive), relation →IriFilterLegacy/#[ApiFilter]anchorsCodemod:
api:upgrade-filterA php-parser, format-preserving command that rewrites class- and property-level
#[ApiFilter]intoQueryParameterentries on#[ApiResource](cs-fixer post-pass).--dry-run(default) prints a diff;--forcewrites.Resolution highlights:
IriFiltervia the property's native type resolving to a resource (gated by the resource-class resolver, so value objects like\DateTimestay search filters).filterContext: DateFilter::INCLUDE_NULL_*(read from the filter'spropertiesmap, not the description).i-variant strategies →caseSensitive(the new search filters are case-insensitive by default);iexact → ExactFilter.GroupFilters) both survive; nested keys (colors.prop) emit an explicitproperty.filters:service filter sharing a query key (would shadow it)Sweep result: 10 resources migrated, 6 skipped (5 name-conversion + 1 service/
#[ApiFilter]key overlap), kept as-is.Tests / CI
upgrade-filterjob:--forcethen the functional suite (guards both the migrated andLegacy/paths).Functional/{Doctrine,Parameters,JsonApi,GraphQl}green.Not in this PR (remaining 4.4 filter work)
#[ApiFilter](AttributeFilterPass), F6Operation::$filters(OpenApiFactory+ParameterResourceMetadataCollectionFactory). The 6 skipped resources still carry#[ApiFilter]outsideLegacy/and must move there (or get#[IgnoreDeprecations]) before F5 lands, to keep theno deprecationsjob green.@internal+ documentBackwardCompatibleFilterDescriptionTrait(§5.6).docs/core/filters.md, adddocs/upgrade/filters.md, CHANGELOG (§5.8).main, e.g.colors(prop:)).