Filtering and Sorting

The CMS API provides three complementary mechanisms for querying collections: SearchFilter for exact and partial matching on standard fields, MultilingualSearchFilter for searching across multilingual JSON fields, and OrderFilter for sorting results. All three work through query parameters on collection endpoints.

For Product Managers and Integrators

Filtering and sorting let front-end applications display content exactly how users expect it. A blog page can show the newest articles first, a media library can narrow results by file type, and a category picker can search by name in any language — all without custom API endpoints. Key benefits:
  • Consistent behavior across all entities (articles, pages, categories, tags, media, redirects)
  • Combinable — filters, search, and sorting can be used together in a single request
  • Language-aware — multilingual search finds content regardless of which language it was written in

SearchFilter (Exact and Partial Match)

SearchFilter lets you narrow a collection by matching field values. It supports two match strategies:
  • exact — the value must match exactly (useful for statuses, slugs, IDs)
  • partial — the value can appear anywhere in the field (useful for filenames, paths)

Syntax

GET /admin/{resource}?{property}={value}

Examples

Filter articles by status:
GET /admin/articles?status=published
Filter articles by category UUID:
GET /admin/articles?category.uuid=019505e5-c5d0-7000-8000-000000000010
Filter media by MIME type:
GET /admin/media?mime_type=image/jpeg
Search media by filename (partial match):
GET /admin/media?original_filename=hero-banner
Filter redirects by status code:
GET /admin/redirects?status_code=301
Filter pages by type:
GET /admin/pages?page_type=landing

Multiple Filters

Combine filters with & to narrow results further. All filters are applied with AND logic:
GET /admin/media?mime_type=image/jpeg&visibility=public&status=ready
Multilingual fields in the CMS are stored as JSON objects with locale keys (e.g., {"fr": "Titre", "en": "Title"}). The MultilingualSearchFilter searches across all locale values simultaneously, so a single query finds content regardless of which language it was written in.

Syntax

GET /admin/{resource}?{field}={search_term}

How It Works

When you search for "symfony" in the title field:
  1. The filter extracts all language keys from the JSON column (fr, en, etc.)
  2. It performs a case-insensitive partial match against each language value
  3. Results include any resource where at least one language matches

Examples

Search articles by title (matches in any language):
GET /admin/articles?title=symfony
Search articles by excerpt:
GET /admin/articles?excerpt=tutorial
Search categories by name:
GET /admin/categories?name=technology
Search tags by description:
GET /admin/tags?description=framework
MultilingualSearchFilter uses OR logic across languages but AND logic when combined with other filters. A request like ?title=symfony&status=published returns articles where the title matches in any language AND the status is published.

OrderFilter (Sorting)

OrderFilter lets you control the sort order of collection results. Each entity exposes specific sortable properties with a default direction.

Syntax

GET /admin/{resource}?order[{property}]={direction}
Where {direction} is asc or desc.

Examples

Sort articles by newest first:
GET /admin/articles?order[created_at]=desc
Sort media by filename alphabetically:
GET /admin/media?order[original_filename]=asc
Sort redirects by most popular:
GET /admin/redirects?order[hit_count]=desc

Multiple Sort Criteria

Chain sort parameters to define primary and secondary sort order:
GET /admin/articles?order[status]=asc&order[published_at]=desc
This sorts articles by status first (alphabetically), then by publication date within each status group.

Combining Everything

Filters, search, sorting, and pagination work together:
GET /admin/articles?status=published&title=symfony&order[published_at]=desc&page=1&per_page=10
This request:
  1. Filters to published articles only (SearchFilter)
  2. Searches for “symfony” in the title across all languages (MultilingualSearchFilter)
  3. Sorts by publication date, newest first (OrderFilter)
  4. Returns the first page of 10 results (Pagination)

Entity Filter Reference

The tables below list every available filter property per entity.

Article

PropertyFilter TypeStrategyDescription
statusSearchFilterexactFilter by content status
typeSearchFilterexactFilter by article type
tags.slugSearchFilterexactFilter by tag slug
tags.uuidSearchFilterexactFilter by tag UUID
category.uuidSearchFilterexactFilter by primary category UUID
secondaryCategories.uuidSearchFilterexactFilter by secondary category UUID
titleMultilingualSearchpartialSearch title across all languages
excerptMultilingualSearchpartialSearch excerpt across all languages
body_htmlMultilingualSearchpartialSearch body content across all languages
created_atOrderFilterdescSort by creation date
updated_atOrderFilterdescSort by last update date
published_atOrderFilterdescSort by publication date
slugOrderFilterascSort by slug alphabetically
statusOrderFilterascSort by status
typeOrderFilterascSort by article type

Page

PropertyFilter TypeStrategyDescription
statusSearchFilterexactFilter by content status
page_typeSearchFilterexactFilter by page type
tags.slugSearchFilterexactFilter by tag slug
tags.uuidSearchFilterexactFilter by tag UUID
titleMultilingualSearchpartialSearch title across all languages
excerptMultilingualSearchpartialSearch excerpt across all languages
body_htmlMultilingualSearchpartialSearch body content across all languages
created_atOrderFilterdescSort by creation date
updated_atOrderFilterdescSort by last update date
published_atOrderFilterdescSort by publication date
slugOrderFilterascSort by slug alphabetically
statusOrderFilterascSort by status
page_typeOrderFilterascSort by page type

Category

PropertyFilter TypeStrategyDescription
slugSearchFilterexactFilter by slug
nameMultilingualSearchpartialSearch name across all languages
descriptionMultilingualSearchpartialSearch description across all languages
created_atOrderFilterdescSort by creation date
slugOrderFilterascSort by slug alphabetically

Tag

PropertyFilter TypeStrategyDescription
slugSearchFilterexactFilter by slug
nameMultilingualSearchpartialSearch name across all languages
descriptionMultilingualSearchpartialSearch description across all languages
created_atOrderFilterdescSort by creation date
slugOrderFilterascSort by slug alphabetically

Media

PropertyFilter TypeStrategyDescription
mime_typeSearchFilterexactFilter by MIME type (e.g., image/jpeg)
statusSearchFilterexactFilter by upload status
visibilitySearchFilterexactFilter by visibility
original_filenameSearchFilterpartialSearch by original filename
created_atOrderFilterdescSort by creation date
updated_atOrderFilterdescSort by last update date
original_filenameOrderFilterascSort by filename alphabetically
mime_typeOrderFilterascSort by MIME type
sizeOrderFilterdescSort by file size

Redirect

PropertyFilter TypeStrategyDescription
source_pathSearchFilterpartialSearch by source path
target_pathSearchFilterpartialSearch by target path
statusSearchFilterexactFilter by redirect status
status_codeSearchFilterexactFilter by HTTP status code (301, 302)
created_atOrderFilterdescSort by creation date
hit_countOrderFilterdescSort by number of hits
source_pathOrderFilterascSort by source path alphabetically
status_codeOrderFilterascSort by status code

Technical Details

How Filters Are Implemented

Filters are declared as PHP attributes on entity classes using API Platform’s #[ApiFilter] attribute:
#[ApiFilter(SearchFilter::class, properties: [
    'status' => 'exact',
    'mime_type' => 'exact',
    'original_filename' => 'partial',
])]
#[ApiFilter(OrderFilter::class, properties: [
    'created_at' => 'DESC',
    'updated_at' => 'DESC',
])]
The properties array maps field names to their default behavior:
  • For SearchFilter: the value is the match strategy (exact or partial)
  • For OrderFilter: the value is the default sort direction (ASC or DESC), which clients can override in query parameters

MultilingualSearchFilter Internals

The custom MultilingualSearchFilter extends API Platform’s AbstractFilter. It queries PostgreSQL JSON columns using CAST and LIKE operators to search across all locale values stored in a JSON field. When no properties are configured (as on Article and Page), it defaults to searching title, excerpt, and body_html. When properties are explicitly set (as on Category and Tag), it uses only those fields:
// Defaults to title, excerpt, body_html
#[ApiFilter(MultilingualSearchFilter::class)]

// Searches only name and description
#[ApiFilter(MultilingualSearchFilter::class, properties: ['name' => 'partial', 'description' => 'partial'])]

Default Sort Direction

The direction specified in the OrderFilter configuration is the default when a client does not explicitly provide one. Clients can always override it:
# Uses default (DESC for created_at)
GET /admin/articles?order[created_at]

# Explicitly overrides to ascending
GET /admin/articles?order[created_at]=asc

OpenAPI Integration

All filters are automatically documented in the OpenAPI specification. The /openapi.json endpoint includes filter parameters for every collection operation, making them discoverable in tools like Swagger UI and Postman.