Collection Filtering and Sorting
Total CMS provides powerful filterCollection and sortCollection Twig filters for advanced data manipulation. These filters allow you to perform complex filtering operations on collections using a variety of operators, making it easy to create dynamic, filtered content displays.
filterCollection Filter
Section titled “filterCollection Filter”The filterCollection filter allows you to filter arrays of objects based on complex criteria. It uses the CollectionRefiner service under the hood.
Basic Syntax
Section titled “Basic Syntax”{% set filtered = collection | filterCollection([ { property: "field_name", operator: "operator_name", value: "filter_value" }]) %}Multiple filters can be combined - all filters must match (AND logic):
{% set filtered = collection | filterCollection([ {property: "status", operator: "equal", value: "published"}, {property: "date", operator: "past", value: ""}]) %}String Operators
Section titled “String Operators”Exact match (case-sensitive for strings, loose comparison).
{% set published = cms.collection.objects('blog') | filterCollection([ {property: "status", operator: "equal", value: "published"}]) %}contains
Section titled “contains”Check if string contains substring (case-sensitive).
{% set guides = cms.collection.objects('blog') | filterCollection([ {property: "title", operator: "contains", value: "Guide"}]) %}starts
Section titled “starts”Check if string starts with prefix.
{% set tutorials = cms.collection.objects('blog') | filterCollection([ {property: "title", operator: "starts", value: "How to"}]) %}Check if string ends with suffix.
{% set questions = cms.collection.objects('blog') | filterCollection([ {property: "title", operator: "ends", value: "?"}]) %}Regular expression pattern matching.
{% set posts = cms.collection.objects('blog') | filterCollection([ {property: "title", operator: "like", value: "PHP.*Tutorial"}]) %}Case-Insensitive Variants
Section titled “Case-Insensitive Variants”All string operators have case-insensitive versions:
equalCaseInsensitivecontainsCaseInsensitivestartsCaseInsensitiveendsCaseInsensitive
{% set posts = cms.collection.objects('blog') | filterCollection([ {property: "title", operator: "containsCaseInsensitive", value: "guide"}]) %}{# Matches "Guide", "GUIDE", "guide", etc. #}Comparison Operators
Section titled “Comparison Operators”less / lt
Section titled “less / lt”Less than comparison.
{% set cheapProducts = cms.collection.objects('products') | filterCollection([ {property: "price", operator: "less", value: "50"}]) %}lesseq / le
Section titled “lesseq / le”Less than or equal to.
{% set affordableProducts = cms.collection.objects('products') | filterCollection([ {property: "price", operator: "lesseq", value: "100"}]) %}greater / gt
Section titled “greater / gt”Greater than comparison.
{% set premiumProducts = cms.collection.objects('products') | filterCollection([ {property: "price", operator: "greater", value: "100"}]) %}greatereq / ge
Section titled “greatereq / ge”Greater than or equal to.
{% set expensiveProducts = cms.collection.objects('products') | filterCollection([ {property: "price", operator: "greatereq", value: "500"}]) %}Boolean Operators
Section titled “Boolean Operators”istrue
Section titled “istrue”Check if value is true (true, ‘true’, ‘1’, or 1).
{% set featured = cms.collection.objects('products') | filterCollection([ {property: "featured", operator: "istrue"}]) %}isfalse
Section titled “isfalse”Check if value is false (false, ‘false’, ‘0’, or 0).
{% set notFeatured = cms.collection.objects('products') | filterCollection([ {property: "featured", operator: "isfalse"}]) %}isempty
Section titled “isempty”Check if value is empty.
{% set noDescription = cms.collection.objects('products') | filterCollection([ {property: "description", operator: "isempty"}]) %}isnotempty
Section titled “isnotempty”Check if value is not empty.
{% set hasDescription = cms.collection.objects('products') | filterCollection([ {property: "description", operator: "isnotempty"}]) %}Date Operators
Section titled “Date Operators”Basic Date Operators
Section titled “Basic Date Operators”past - Date is in the past
{% set pastEvents = cms.collection.objects('events') | filterCollection([ {property: "date", operator: "past"}]) %}future - Date is in the future
{% set upcomingEvents = cms.collection.objects('events') | filterCollection([ {property: "date", operator: "future"}]) %}today - Date is today
{% set todaysEvents = cms.collection.objects('events') | filterCollection([ {property: "date", operator: "today"}]) %}pastToday - Date is today or in the past
{% set currentAndPast = cms.collection.objects('events') | filterCollection([ {property: "date", operator: "pastToday"}]) %}futureToday - Date is today or in the future
{% set currentAndFuture = cms.collection.objects('events') | filterCollection([ {property: "date", operator: "futureToday"}]) %}Date Range Operators
Section titled “Date Range Operators”todayPlusDays - Today through N days in future
{# Events from today through next 7 days #}{% set nextWeekEvents = cms.collection.objects('events') | filterCollection([ {property: "date", operator: "todayPlusDays", value: 7}]) %}todayMinusDays - Today and N days back
{# Blog posts from last 30 days including today #}{% set recentPosts = cms.collection.objects('blog') | filterCollection([ {property: "date", operator: "todayMinusDays", value: 30}]) %}Date Comparison Operators
Section titled “Date Comparison Operators”after - Date is after another date
{% set recent = cms.collection.objects('blog') | filterCollection([ {property: "date", operator: "after", value: "2024-01-01"}]) %}before - Date is before another date
{% set archived = cms.collection.objects('blog') | filterCollection([ {property: "date", operator: "before", value: "2023-01-01"}]) %}Calendar Period Operators
Section titled “Calendar Period Operators”thisWeek - Date is in current week (Monday-Sunday)
{% set thisWeeksPosts = cms.collection.objects('blog') | filterCollection([ {property: "date", operator: "thisWeek"}]) %}thisMonth - Date is in current month
{% set thisMonthsPosts = cms.collection.objects('blog') | filterCollection([ {property: "date", operator: "thisMonth"}]) %}thisYear - Date is in current year
{% set thisYearsPosts = cms.collection.objects('blog') | filterCollection([ {property: "date", operator: "thisYear"}]) %}Numeric Range Operators
Section titled “Numeric Range Operators”between - Value is between min and max (inclusive)
Usage: value: "min,max"
{# Products priced between $10 and $100 #}{% set affordableProducts = cms.collection.objects('products') | filterCollection([ {property: "price", operator: "between", value: "10,100"}]) %}
{# Cars with mileage between 10k-50k miles #}{% set usedCars = cms.collection.objects('cars') | filterCollection([ {property: "mileage", operator: "between", value: "10000,50000"}]) %}
{# Ratings between 3-5 stars #}{% set topRated = cms.collection.objects('reviews') | filterCollection([ {property: "rating", operator: "between", value: "3,5"}]) %}Text Length Operators
Section titled “Text Length Operators”longerThan - Text exceeds N characters
{# Blog posts with detailed content (over 500 chars) #}{% set detailedPosts = cms.collection.objects('blog') | filterCollection([ {property: "content", operator: "longerThan", value: 500}]) %}shorterThan - Text is under N characters
{# Products with short descriptions for grid view #}{% set compactProducts = cms.collection.objects('products') | filterCollection([ {property: "description", operator: "shorterThan", value: 200}]) %}Array Counting Operators
Section titled “Array Counting Operators”hasMin - Array has at least N items
{# Posts with at least 3 tags #}{% set wellTaggedPosts = cms.collection.objects('blog') | filterCollection([ {property: "tags", operator: "hasMin", value: 3}]) %}
{# Products with multiple images #}{% set multiImageProducts = cms.collection.objects('products') | filterCollection([ {property: "gallery", operator: "hasMin", value: 2}]) %}hasMax - Array has at most N items
{# Products with 5 or fewer images #}{% set simpleProducts = cms.collection.objects('products') | filterCollection([ {property: "gallery", operator: "hasMax", value: 5}]) %}hasCount - Array has exactly N items
{# Events with exactly 2 speakers #}{% set dualSpeakerEvents = cms.collection.objects('events') | filterCollection([ {property: "speakers", operator: "hasCount", value: 2}]) %}Day-of-Week Operators
Section titled “Day-of-Week Operators”isWeekday - Date is Monday through Friday
{# Business hours events only #}{% set businessEvents = cms.collection.objects('events') | filterCollection([ {property: "date", operator: "isWeekday"}]) %}isWeekend - Date is Saturday or Sunday
{# Weekend activities #}{% set weekendEvents = cms.collection.objects('events') | filterCollection([ {property: "date", operator: "isWeekend"}]) %}dayOfWeek - Date is specific day of week
Value can be day name (Monday-Sunday) or number (1=Monday, 7=Sunday):
{# Tuesday specials #}{% set tuesdaySpecials = cms.collection.objects('specials') | filterCollection([ {property: "date", operator: "dayOfWeek", value: "Tuesday"}]) %}
{# Using day number (1=Mon, 7=Sun) #}{% set mondayEvents = cms.collection.objects('events') | filterCollection([ {property: "date", operator: "dayOfWeek", value: "1"}]) %}Array Logic (OR vs AND)
Section titled “Array Logic (OR vs AND)”When filtering with an array of values, you can control whether items must match ANY value (OR logic) or ALL values (AND logic) using the logic parameter.
OR Logic (Default)
Section titled “OR Logic (Default)”Returns items that match ANY of the values in the array:
{# Posts tagged with 'php' OR 'javascript' OR 'web' #}{% set posts = cms.collection.objects('blog') | filterCollection([ { property: "tags", operator: "contains", value: ["php", "javascript", "web"], logic: "or" {# Default - can be omitted #} }]) %}
{# Products in category 'electronics' OR 'computers' #}{% set products = cms.collection.objects('products') | filterCollection([ { property: "category", operator: "equal", value: ["electronics", "computers"] {# logic: "or" is the default #} }]) %}AND Logic
Section titled “AND Logic”Returns items that match ALL of the values in the array:
{# Posts that contain ALL tags: 'php' AND 'framework' AND 'tutorial' #}{% set advancedPosts = cms.collection.objects('blog') | filterCollection([ { property: "tags", operator: "contains", value: ["php", "framework", "tutorial"], logic: "and" }]) %}
{# Products with ALL specified features #}{% set premiumProducts = cms.collection.objects('products') | filterCollection([ { property: "features", operator: "contains", value: ["waterproof", "wireless", "fast-charging"], logic: "and" {# Must have ALL three features #} }]) %}Logic Summary
Section titled “Logic Summary”| Logic | Behavior | Use Case |
|---|---|---|
"or" (default) | Match ANY value | Broad filtering, multiple categories |
"and" | Match ALL values | Strict requirements, feature completeness |
Negation (NOT Operator)
Section titled “Negation (NOT Operator)”Any operator can be negated by prefixing with not- or prepending the value with !:
{# Using not- prefix #}{% set notPublished = cms.collection.objects('blog') | filterCollection([ {property: "status", operator: "not-equal", value: "published"}]) %}
{# Using ! prefix on value #}{% set notPublished = cms.collection.objects('blog') | filterCollection([ {property: "status", operator: "equal", value: "!published"}]) %}
{# Not in the past (future or today) #}{% set notPastEvents = cms.collection.objects('events') | filterCollection([ {property: "date", operator: "not-past"}]) %}sortCollection Filter
Section titled “sortCollection Filter”The sortCollection filter sorts arrays based on one or more properties.
Basic Syntax
Section titled “Basic Syntax”{% set sorted = collection | sortCollection([ { property: "field_name", reverse: false, {# Optional: true for descending #} natural: false, {# Optional: true for natural sort #} shuffle: false {# Optional: true to randomize #} }]) %}Single Property Sort
Section titled “Single Property Sort”{# Sort by date ascending #}{% set sorted = cms.collection.objects('blog') | sortCollection([ {property: "date"}]) %}
{# Sort by price descending #}{% set sorted = cms.collection.objects('products') | sortCollection([ {property: "price", reverse: true}]) %}Multi-Property Sort
Section titled “Multi-Property Sort”Sorts are applied in order - first sort is primary, subsequent sorts break ties:
{# Sort by featured (desc), then date (desc), then title (natural) #}{% set posts = cms.collection.objects('blog') | sortCollection([ {property: "featured", reverse: true}, {property: "date", reverse: true}, {property: "title", natural: true}]) %}Natural Sorting
Section titled “Natural Sorting”Natural sorting treats numbers within strings intelligently:
{# Without natural: "Item 1", "Item 10", "Item 2" #}{# With natural: "Item 1", "Item 2", "Item 10" #}{% set sorted = cms.collection.objects('products') | sortCollection([ {property: "name", natural: true}]) %}Random Shuffle
Section titled “Random Shuffle”{# Randomize order #}{% set randomProducts = cms.collection.objects('products') | sortCollection([ {property: "name", shuffle: true}]) %}manualSort Filter
Section titled “manualSort Filter”The manualSort filter allows you to sort collections by an explicit order of values, with control over how remaining items are handled. This is useful when you need a specific custom order that can’t be achieved with standard property sorting.
Basic Syntax
Section titled “Basic Syntax”{% set sorted = collection | manualSort({ property: "field_name", order: ["value1", "value2", "value3"], remainder: {property: "name"}, excludeRemainder: false}) %}Options
Section titled “Options”| Option | Type | Description |
|---|---|---|
property | string | The property to match against the order array |
order | array | Explicit list of values defining the sort order |
collection | string | Collection ID to auto-lookup order from collection metadata |
remainder | object | Sort rule for items not in the order array (same format as sortCollection) |
excludeRemainder | boolean | If true, items not in the order array are excluded from results |
Note: When using collection, the filter looks up manualSort.{property} from the collection’s metadata. You can use either order or collection, but order takes precedence if both are provided.
Basic Manual Sort
Section titled “Basic Manual Sort”{# Sort team members by role in specific order #}{% set team = cms.collection.objects("team") | manualSort({ property: 'role', order: ['ceo', 'cfo', 'cmo', 'vp', 'director']}) %}{# CEO first, CFO second, CMO third, etc. #}{# Any roles not listed appear at the end in original order #}With Remainder Sorting
Section titled “With Remainder Sorting”Items not matching the explicit order can be sorted by a secondary property:
{# Executives in order, then remaining staff sorted by lastName #}{% set team = cms.collection.objects("team") | manualSort({ property: 'position', order: ['ceo', 'cfo', 'cmo', 'vp'], remainder: {property: 'lastName'}}) %}Exclude Non-Matching Items
Section titled “Exclude Non-Matching Items”Use excludeRemainder to only return items that match the order array:
{# Only show featured team members in specific order #}{% set featured = cms.collection.objects("team") | manualSort({ property: 'id', order: ['john-smith', 'jane-doe', 'bob-wilson'], excludeRemainder: true}) %}{# Returns exactly 3 items (if they exist) in that order #}Using Collection Metadata
Section titled “Using Collection Metadata”Store sort orders in the collection’s metadata for easy admin editing. Use the collection option to automatically look up the order:
{# Automatic lookup from collection metadata #}{% set team = cms.collection.objects("team") | manualSort({ property: 'position', collection: 'team', remainder: {property: 'lastName'}}) %}This is equivalent to manually fetching the metadata:
{# Manual lookup (same result) #}{% set meta = cms.collection.get('team') %}{% set team = cms.collection.objects("team") | manualSort({ property: 'position', order: meta.manualSort.position | default([]), remainder: {property: 'lastName'}}) %}To configure the order, edit the collection settings in the admin and add JSON to the “Manual Sort Orders” field:
{ "position": ["ceo", "cfo", "cmo", "vp", "director", "manager"], "department": ["executive", "engineering", "sales", "marketing"]}Sub-Sorting Within Groups
Section titled “Sub-Sorting Within Groups”When multiple items have the same ordered value, the remainder rule sorts them:
{# If there are multiple VPs, sort them by name #}{% set team = cms.collection.objects("team") | manualSort({ property: 'role', order: ['ceo', 'vp', 'manager'], remainder: {property: 'name'}}) %}{# Result: CEO, then all VPs sorted by name, then managers sorted by name, then everyone else by name #}Practical Examples
Section titled “Practical Examples”Portfolio with curated order:
{% set projects = cms.collection.objects("projects") | manualSort({ property: 'id', order: ['flagship-project', 'award-winner', 'client-favorite'], remainder: {property: 'date', reverse: true}}) %}{# Featured projects first, then remaining by date descending #}Navigation menu order:
{% set pages = cms.collection.objects("pages") | manualSort({ property: 'slug', order: ['home', 'about', 'services', 'portfolio', 'contact'], excludeRemainder: true}) %}{# Only these pages, in this exact order #}Product categories with priority:
{% set meta = cms.collection.get('products') %}{% set products = cms.collection.objects("products") | manualSort({ property: 'category', order: meta.manualSort.category | default(['featured', 'new', 'sale']), remainder: {property: 'name', natural: true}}) %}Real-World Examples
Section titled “Real-World Examples”Blog Post Management
Section titled “Blog Post Management”{# Published posts from this year, sorted by date #}{% set posts = cms.collection.objects('blog') | filterCollection([ {property: "status", operator: "equal", value: "published"}, {property: "date", operator: "thisYear"} ]) | sortCollection([ {property: "featured", reverse: true}, {property: "date", reverse: true} ]) %}E-commerce Product Filtering
Section titled “E-commerce Product Filtering”{# In-stock products, price $20-$100, with good ratings #}{% set products = cms.collection.objects('products') | filterCollection([ {property: "instock", operator: "istrue"}, {property: "price", operator: "between", value: "20,100"}, {property: "rating", operator: "greatereq", value: "4"}, {property: "description", operator: "longerThan", value: 50} ]) | sortCollection([ {property: "rating", reverse: true}, {property: "price"} ]) %}Event Calendar
Section titled “Event Calendar”{# This week's events on weekdays, sorted by date #}{% set events = cms.collection.objects('events') | filterCollection([ {property: "date", operator: "thisWeek"}, {property: "date", operator: "isWeekday"}, {property: "cancelled", operator: "isfalse"} ]) | sortCollection([ {property: "date"}, {property: "start_time"} ]) %}Content Discovery
Section titled “Content Discovery”{# Well-tagged posts from last 30 days with detailed content #}{% set qualityPosts = cms.collection.objects('blog') | filterCollection([ {property: "date", operator: "todayMinusDays", value: 30}, {property: "tags", operator: "hasMin", value: 3}, {property: "content", operator: "longerThan", value: 1000}, {property: "status", operator: "equal", value: "published"} ]) | sortCollection([ {property: "views", reverse: true}, {property: "date", reverse: true} ]) %}Weekend Special Offers
Section titled “Weekend Special Offers”{# Special offers valid this weekend #}{% set weekendDeals = cms.collection.objects('specials') | filterCollection([ {property: "active", operator: "istrue"}, {property: "start_date", operator: "isWeekend"}, {property: "discount", operator: "greatereq", value: "20"} ]) | sortCollection([ {property: "discount", reverse: true} ]) %}User-Driven Filters
Section titled “User-Driven Filters”{# Dynamic filtering based on URL parameters #}{% set minPrice = get.min | default(0) %}{% set maxPrice = get.max | default(1000) %}{% set category = get.category | default('') %}
{% set products = cms.collection.objects('products') | filterCollection([ {property: "price", operator: "between", value: minPrice ~ "," ~ maxPrice}, {property: "category", operator: "equal", value: category} ]) | sortCollection([ {property: get.sort | default('price'), reverse: get.order == 'desc'} ]) %}Performance Tips
Section titled “Performance Tips”- Filter before sorting - Reduce the dataset size before sorting
- Use specific operators - More specific operators are faster
- Limit results - Use
| slice(0, 10)after filtering - Cache filtered results - For expensive operations
{# Good: Filter first, then sort, then limit #}{% set results = cms.collection.objects('blog') | filterCollection([...]) | sortCollection([...]) | slice(0, 10) %}See Also
Section titled “See Also”- Twig Filters Reference - All available Twig filters
- Index Filtering - Server-side filtering for APIs
- CMS Grid Tag - Display filtered collections