webstack/api-platform-extensions-bundle
A Symfony bundle that extends API Platform with reusable helpers for resource configuration, filtering, serialization, and common API patterns, helping teams build consistent REST and GraphQL APIs with less boilerplate.
This is a small, single-purpose Symfony bundle that drops three custom API Platform/Doctrine ORM filters into your project: a UUID filter, an OR search filter, and a global search filter. The goal is to fill gaps in API Platform's stock filter set (which historically lacked first-class UUID matching and multi-property OR search). Treat it as a thin convenience layer, not a framework.
Install
composer require webstacknl/api-platform-extensions-bundle
Symfony Flex should register Webstack\ApiPlatformExtensionsBundle\ApiPlatformExtensionsBundle. If not, add it manually to config/bundles.php:
return [
// ...
Webstack\ApiPlatformExtensionsBundle\ApiPlatformExtensionsBundle::class => ['all' => true],
];
Check compatibility first. Last release was 2023-03-07, which puts it in the API Platform 2.7 / early 3.0 era. Inspect composer.json for the api-platform/core constraint before wiring it into a 3.x project — the metadata vs. annotation namespace moved in 3.0.
Where to look first. Open src/ and config/services*.yaml. You want to see:
src/Filter/)api_platform.filter service tags and their idsManagerRegistry plus $properties and $name like the core filters do)First use case: expose a UUID lookup. On the resource class, import whichever ApiFilter annotation/attribute matches your API Platform version and attach the bundle's UUID filter:
use ApiPlatform\Metadata\ApiFilter;
use Webstack\ApiPlatformExtensionsBundle\Filter\UuidFilter;
#[ApiFilter(UuidFilter::class, properties: ['id' => null, 'owner' => null])]
#[ApiResource]
class Order { /* ... */ }
Hit GET /api/orders?id=018d… and confirm you get an exact match rather than a type error. If the class name differs, copy it from src/Filter/ — don't guess.
Per-resource attribute vs. service id. API Platform resolves ApiFilter by class name or by service id. Because this bundle ships prefixed service ids, the reliable pattern is to reference the service id that appears in config/services.yaml (e.g. api_platform.filter.uuid), which avoids autowiring surprises:
#[ApiFilter('api_platform.filter.uuid', properties: ['id' => null])]
Global search. Apply the global-search filter at the resource level with no properties and treat the query param as a free-text term. Useful for an /api/products?search=red+shoes endpoint in an admin or storefront search box:
#[ApiFilter(GlobalSearchFilter::class, properties: ['name' => 'partial', 'sku' => 'partial', 'category.name' => 'partial'])]
Scope it deliberately: global search across every string column will become the slowest endpoint in your API once tables grow.
OR search for multi-field lookups. The OR filter is what you want when a client has one input field but three candidate columns (e.g. ?q= matching firstName, lastName, email). Combine it with ApiPlatform\Doctrine\Orm\Filter\OrderFilter so the result set stays sortable.
Stacking with pagination and other filters. All three compose normally with PaginationFilter and SearchFilter because they extend the Doctrine ORM filter base. Order your attributes so PaginationFilter is present when the search filter can return large sets.
Global config instead of attributes. If you manage filters centrally, register them in config/packages/api_platform.yaml:
api_platform:
mapping:
paths: ['%kernel.project_dir%/src/Entity']
…and attach filters per resource via XML/YAML filters: blocks that reference the bundle's service ids. Prefer this when you can't touch entity code (vendor entities, generated code).
Custom operation controllers. Filters only run on collection operations that go through the Doctrine CollectionProvider. If you've written a hand-rolled controller, you're bypassing this bundle entirely — implement matching there or re-route through a standard collection operation.
bin/console debug:container --tag=api_platform.filter and confirm the bundle's filters are listed. If they're missing, the bundle's services.yaml may not have auto-loaded (older bundles sometimes require explicit Extension::load() — check DependencyInjection/).ApiPlatform\Core\Annotation\ApiFilter vs ApiPlatform\Metadata\ApiFilter is the single most common break. Attribute imports must match your installed major version; the bundle itself won't reconcile this for you.uuid/guid/binary columns. If your entity uses a binary UuidType, ensure the input string is normalized (lowercase, hyphenated) before it reaches the filter, or you'll get "not found" on values that clearly exist.LIKE %term%. On MySQL the default collation is case-insensitive; on PostgreSQL it isn't. Use ILIKE-friendly column types or LOWER() expressions if cross-database parity matters.% and _ in a search term will behave as wildcards. Sanitize if that's not intended — none of these filters will do it for you.LIKE predicate; no index covers %term%. Add trigram indexes (pg_trgm) or a full-text column for anything beyond small tables.hydra:search when properties are declared. A properties: [] config makes the filter invisible in the OpenAPI docs even though it functions — worth knowing when clients complain the parameter "doesn't exist".bin/console cache:clear after editing filter services; debug:router and the OpenAPI endpoint cache aggressively and will otherwise show stale filter documentation.SearchFilter/UuidFilter (core added UuidFilter around 3.x) — on modern API Platform you may not need this bundle at all.How can I help you explore Laravel packages today?