Index Filtering
Total CMS provides a powerful IndexFilter service for filtering index objects based on include/exclude criteria. This filtering system is used throughout the CMS for sitemaps, RSS feeds, API endpoints, and custom implementations.
Overview
Section titled “Overview”The IndexFilter service provides a flexible way to filter collection objects using simple property-based criteria:
- Include filters - Object must match ALL specified criteria
- Exclude filters - Object is excluded if it matches ANY criteria
- Boolean & String values - Automatic type conversion
- Shorthand syntax - Property name defaults to
true
Filter Syntax
Section titled “Filter Syntax”Include (Include Only)
Section titled “Include (Include Only)”Include only objects where specified properties match ALL values:
include=property:value # Single include filterinclude=property1:value1,property2:value2 # Multiple include filters (AND logic)include=property # Shorthand for property:trueLogic: ALL conditions must be true for the object to be included.
Exclude (Remove)
Section titled “Exclude (Remove)”Exclude objects where specified properties match ANY value:
exclude=property:value # Single exclusionexclude=property1:value1,property2:value2 # Multiple exclusions (OR logic)exclude=property # Shorthand for property:trueLogic: If ANY condition matches, the object is excluded.
Precedence
Section titled “Precedence”Exclude takes precedence over include. If an object matches an exclude filter, it will be removed even if it also matches an include filter.
// Object: ['published' => true, 'draft' => true]// Filters: include=published:true, exclude=draft:true// Result: EXCLUDED (draft:true matches exclude)Value Types
Section titled “Value Types”The system automatically converts common values:
| Value | Type | Comparison | Example |
|---|---|---|---|
true | Boolean | Strict (===) | published:true |
false | Boolean | Strict (===) | draft:false |
| Other | String | Case-insensitive | status:active |
Comparison behavior:
- Boolean values - Fast strict comparison for optimal performance
- String values - Case-insensitive matching for flexibility
- Array fields - Case-insensitive search within array
?include=featured:true # Boolean true (strict match)?include=status:published # String "published" (matches "Published", "PUBLISHED", etc.)?exclude=draft:false # Boolean false (strict match)?exclude=category:news # String "news" (matches "News", "NEWS", etc.)Array Field Support
Section titled “Array Field Support”When a field contains an array, the filter checks if the value exists within the array. String comparisons are case-insensitive for better usability. This is particularly useful for fields like tags, categories, or any multi-value properties.
Example with tags:
?include=tags:travel # Matches if "travel" is in the tags array (case-insensitive)?exclude=tags:archived # Excludes if "archived" is in the tags array (case-insensitive)Sample data:
['id' => '1', 'tags' => ['Travel', 'Adventure', 'Europe']]Filter behavior (case-insensitive):
include=tags:travel→ ✓ Matches (“travel” matches “Travel”)include=tags:ADVENTURE→ ✓ Matches (“ADVENTURE” matches “Adventure”)include=tags:food→ ✗ No match (food not in array)exclude=tags:archived→ ✓ Included (archived not in array)exclude=tags:europe→ ✗ Excluded (“europe” matches “Europe”)
Combined with scalar fields:
// Include published posts with "travel" tag?include=published:true,tags:travel
// Exclude drafts or posts with "archived" tag?exclude=draft:true,tags:archivedShorthand Syntax
Section titled “Shorthand Syntax”When no value is provided, the property defaults to :true:
?include=featured # Same as ?include=featured:true?exclude=draft # Same as ?exclude=draft:true?include=published # Same as ?include=published:trueUsage in Code
Section titled “Usage in Code”Basic Filtering
Section titled “Basic Filtering”IndexFilter is available via dependency injection — it requires both an IndexReader and ObjectFilter, so let the DI container handle construction:
use TotalCMS\Domain\Index\Service\IndexFilter;
// Inject via constructorpublic function __construct(private IndexFilter $filter) {}
// Fetch and filter in one call$objects = $this->filter->fetchFilteredIndex('blog', [ 'include' => 'published:true', 'exclude' => 'draft:true']);Get Filtered IndexData
Section titled “Get Filtered IndexData”// Returns IndexData with filtered objects$indexData = $filter->fetchFilteredIndexData('blog', [ 'include' => 'featured:true', 'exclude' => 'archived:true']);
// Access filtered objectsforeach ($indexData->objects as $object) { // Process filtered objects}Filter Existing Array
Section titled “Filter Existing Array”// If you already have objects$objects = [ ['id' => '1', 'published' => true, 'draft' => false], ['id' => '2', 'published' => true, 'draft' => true], ['id' => '3', 'published' => false, 'draft' => false],];
$filtered = $filter->filterObjects($objects, [ 'include' => 'published:true', 'exclude' => 'draft:true']);// Result: Only object '1'Check Single Object
Section titled “Check Single Object”$object = ['published' => true, 'featured' => true];
$matches = $filter->matchesFilter($object, [ 'include' => 'published:true,featured:true']);// Result: true (matches all criteria)Real-World Examples
Section titled “Real-World Examples”Blog Posts
Section titled “Blog Posts”URL Parameters:
?exclude=draft # Exclude draft posts?include=featured # Only featured posts?include=published:true # Only published posts?include=published&exclude=draft,private # Published, not draft or privatePHP Code:
$posts = $filter->fetchFilteredIndex('blog', [ 'include' => 'published:true,featured:true', 'exclude' => 'draft:true']);E-commerce Products
Section titled “E-commerce Products”URL Parameters:
?exclude=discontinued # Exclude discontinued products?include=instock:true # Only in-stock products?include=category:electronics,featured # Electronics category + featured?include=instock&exclude=discontinued # In stock, not discontinuedPHP Code:
$products = $filter->fetchFilteredIndex('products', [ 'include' => 'instock:true,category:electronics', 'exclude' => 'discontinued:true']);Events
Section titled “Events”URL Parameters:
?exclude=cancelled # Exclude cancelled events?include=status:upcoming # Only upcoming events?include=featured&exclude=soldout # Featured, not sold outPHP Code:
$events = $filter->fetchFilteredIndex('events', [ 'include' => 'status:upcoming', 'exclude' => 'cancelled:true,soldout:true']);Portfolio
Section titled “Portfolio”URL Parameters:
?include=published # Only published work?exclude=private # Exclude private projects?include=category:webdesign,featured # Web design + featuredPHP Code:
$portfolio = $filter->fetchFilteredIndex('portfolio', [ 'include' => 'published:true,category:webdesign', 'exclude' => 'private:true']);Array Fields (Tags, Categories)
Section titled “Array Fields (Tags, Categories)”URL Parameters:
?include=tags:travel # Posts with "travel" tag?exclude=tags:archived # Exclude posts with "archived" tag?include=tags:featured,published:true # Featured tag + publishedPHP Code:
// Posts with "travel" tag that aren't drafts$posts = $filter->fetchFilteredIndex('blog', [ 'include' => 'tags:travel', 'exclude' => 'draft:true']);
// Products in "electronics" category that are in stock$products = $filter->fetchFilteredIndex('products', [ 'include' => 'categories:electronics,instock:true']);
// Events with "featured" tag, excluding cancelled$events = $filter->fetchFilteredIndex('events', [ 'include' => 'tags:featured', 'exclude' => 'tags:cancelled,tags:soldout']);Advanced Examples
Section titled “Advanced Examples”Multiple Include Criteria (AND Logic)
Section titled “Multiple Include Criteria (AND Logic)”All conditions must match:
// Object must be published AND featured AND in electronics category$objects = $filter->fetchFilteredIndex('products', [ 'include' => 'published:true,featured:true,category:electronics']);Multiple Exclude Criteria (OR Logic)
Section titled “Multiple Exclude Criteria (OR Logic)”If ANY condition matches, object is excluded:
// Exclude if draft OR archived OR deleted$objects = $filter->fetchFilteredIndex('blog', [ 'exclude' => 'draft:true,archived:true,deleted:true']);Combined Include and Exclude
Section titled “Combined Include and Exclude”// Must be published AND featured// AND NOT draft AND NOT private$objects = $filter->fetchFilteredIndex('blog', [ 'include' => 'published:true,featured:true', 'exclude' => 'draft:true,private:true']);String Value Matching
Section titled “String Value Matching”// Match specific string values$objects = $filter->fetchFilteredIndex('products', [ 'include' => 'status:active,category:electronics', 'exclude' => 'vendor:discontinued']);Boolean False Matching
Section titled “Boolean False Matching”// Include only objects where draft is explicitly false$objects = $filter->fetchFilteredIndex('blog', [ 'include' => 'draft:false']);Helper Methods
Section titled “Helper Methods”Extract Filter Options
Section titled “Extract Filter Options”$options = [ 'include' => 'published:true', 'exclude' => 'draft:true', 'limit' => 10, 'offset' => 0];
$filterOptions = $filter->extractFilterOptions($options);// Returns: ['include' => 'published:true', 'exclude' => 'draft:true']// Note: limit and offset remain in $optionsParse Filter String
Section titled “Parse Filter String”$parsed = $filter->parseFilterString('published:true,featured:true,status:active');// Returns:// [// ['field' => 'published', 'value' => true],// ['field' => 'featured', 'value' => true],// ['field' => 'status', 'value' => 'active']// ]Where It’s Used
Section titled “Where It’s Used”The IndexFilter service is used throughout Total CMS:
- Sitemap Builder - Filter which objects appear in XML sitemaps (Sitemap Documentation)
- Collection Index API - Filter collection objects via API endpoint
- RSS Feeds - Control feed content based on object properties
- Data Export - Filter which objects are included in JSON and CSV exports (Export Documentation)
- Form Fields - Filter relational dropdown options (Field Settings)
- Grid Display - Filter objects in Twig templates
- Custom Services - Build your own filtered collections
Collection Index API
Section titled “Collection Index API”The collection index API endpoint supports filtering via URL parameters:
GET /collections/{collection}/index?include=published:true&exclude=draft:trueExamples:
# Get all published blog postsGET /collections/blog/index?include=published:true
# Get featured products that are in stockGET /collections/products/index?include=featured:true,instock:true
# Get events excluding cancelledGET /collections/events/index?exclude=cancelled:true
# Get blog posts with "travel" tagGET /collections/blog/index?include=tags:travel
# Complex filtering: published posts with travel tag, excluding draftsGET /collections/blog/index?include=published:true,tags:travel&exclude=draft:trueThe API returns a filtered IndexData object with only matching objects.
Best Practices
Section titled “Best Practices”1. Use Specific Filters
Section titled “1. Use Specific Filters”// Good - specific criteria'include' => 'published:true,status:active'
// Less specific'include' => 'published'2. Exclude Early
Section titled “2. Exclude Early”Exclude filters run first for better performance:
// Efficient - excluded objects skip include check'exclude' => 'deleted:true,archived:true''include' => 'published:true'3. Default to True
Section titled “3. Default to True”Use shorthand for boolean true values:
// Concise'include' => 'published,featured'
// Verbose but equivalent'include' => 'published:true,featured:true'4. Document Your Filters
Section titled “4. Document Your Filters”When using filters in code, document the business logic:
// Only show active, non-discontinued products to customers$products = $filter->fetchFilteredIndex('products', [ 'include' => 'active:true,instock:true', 'exclude' => 'discontinued:true']);Filter Logic Reference
Section titled “Filter Logic Reference”Include Logic (AND)
Section titled “Include Logic (AND)”Object: {published: true, featured: true}Filter: include=published:true,featured:trueResult: ✓ INCLUDED (both match)
Object: {published: true, featured: false}Filter: include=published:true,featured:trueResult: ✗ EXCLUDED (featured doesn't match)Exclude Logic (OR)
Section titled “Exclude Logic (OR)”Object: {draft: true, private: false}Filter: exclude=draft:true,private:trueResult: ✗ EXCLUDED (draft matches)
Object: {draft: false, private: false}Filter: exclude=draft:true,private:trueResult: ✓ INCLUDED (neither match)Precedence (Exclude First)
Section titled “Precedence (Exclude First)”Object: {published: true, draft: true}Filter: include=published:true, exclude=draft:trueResult: ✗ EXCLUDED (exclude takes precedence)Troubleshooting
Section titled “Troubleshooting”No Results
Section titled “No Results”If filtering returns no results:
- Check field names - Ensure properties exist in your objects
- Check values - Boolean vs string comparison
- Check logic - Remember include=AND, exclude=OR
- Check data - Verify objects have expected values
// Debug: Check filter options$filterOptions = $filter->extractFilterOptions($options);var_dump($filterOptions);
// Debug: Check parsed filters$parsed = $filter->parseFilterString($options['include']);var_dump($parsed);Unexpected Results
Section titled “Unexpected Results”If filtering returns unexpected objects:
- Exclude precedence - Exclude runs before include
- Missing fields - Objects without the field won’t match include filters
- Type mismatches - ‘true’ (string) vs true (boolean)
// Check single object$matches = $filter->matchesFilter($object, $filterOptions);var_dump($matches); // true or falseSee Also
Section titled “See Also”- Sitemap Builder Documentation - Using filters in sitemaps
- RSS Feed Documentation - Using filters in RSS feeds
- Twig Integration - Using filters in templates