Pagination System
Day-based pagination system for Calendar block ensuring complete day groups are never split across pages.
Overview
The Pagination system provides day-based pagination for the Calendar block. Instead of paginating by event count (which can split a single day’s events across pages), pagination is based on complete calendar days. Each page displays 5 complete days worth of events, regardless of how many events occur on those days.
Features
Day-Based Pagination
- Complete Day Groups: Events are never split across pages – each day appears in full
- 5 Days Per Page: Configurable via
DAYS_PER_PAGEconstant inCalendar_Query.php - Dynamic Event Counts: Pages may contain varying numbers of events (5 events on a slow week, 250 on a busy week)
- User-Friendly Browsing: Users see natural calendar day boundaries
SQL-Based Optimization
- Date Boundary Queries: Efficient queries using date ranges for each page
- Indexed Meta Fields: Uses
_datamachine_event_datetimemeta field for fast filtering - Minimal Memory Usage: Only loads events for the current page’s date range
Progressive Enhancement
- Server-First: Full pagination functionality without JavaScript
- JavaScript Enhanced: Seamless page transitions without page reloads
- History API: Browser back/forward button support
- URL Preservation: Shareable pagination states via URL parameters
Architecture
Core Components
Calendar_Query.php – Single source of truth for pagination logic:
DAYS_PER_PAGEconstant (default: 5)get_unique_event_dates()– Returns ordered array of unique event datesget_date_boundaries_for_page()– Calculates start/end dates for a given page
Pagination.php – Renders pagination controls with extensibility filters
Algorithm
Given: page=2, DAYS_PER_PAGE=5
1. Query all unique event start dates (respecting filters)
2. total_days = count(unique_dates) // e.g., 23 days
3. max_pages = ceil(total_days / 5) // e.g., 5 pages
4. For page 2: start_index = (2-1) * 5 = 5
5. end_index = min(5 + 5 - 1, 23 - 1) = 9
6. start_date = unique_dates[5] // 6th day
7. end_date = unique_dates[9] // 10th day
8. Query all events WHERE start_date BETWEEN start_date AND end_date
9. Return all events (no posts_per_page limit)
Usage
Calendar Query Integration
use DataMachineEventsBlocksCalendarCalendar_Query;
use const DataMachineEventsBlocksCalendarDAYS_PER_PAGE;
// Build base query parameters
$base_params = [
'show_past' => false,
'search_query' => '',
'tax_filters' => [],
];
// Get unique event dates
$unique_dates = Calendar_Query::get_unique_event_dates($base_params);
// Get date boundaries for current page
$date_boundaries = Calendar_Query::get_date_boundaries_for_page($unique_dates, $current_page);
// Calculate max pages
$max_pages = $date_boundaries['max_pages'];
// Build final query with date boundaries
$query_params = $base_params;
$query_params['date_start'] = $date_boundaries['start_date'];
$query_params['date_end'] = $date_boundaries['end_date'];
$query_args = Calendar_Query::build_query_args($query_params);
$events_query = new WP_Query($query_args);
Pagination Rendering
use DataMachineEventsBlocksCalendarPagination;
// Render pagination controls
echo Pagination::render_pagination($current_page, $max_pages, $show_past);
REST API Integration
Pagination Parameters
// JavaScript pagination request
fetch('/wp-json/datamachine/v1/events/calendar?' + new URLSearchParams({
paged: 2,
event_search: searchValue,
date_start: startDate,
date_end: endDate,
'tax_filter[venue][]': selectedVenues
}))
.then(response => response.json())
.then(data => {
// Update calendar content
document.querySelector('.datamachine-events-content').innerHTML = data.html;
// Update pagination
document.querySelector('.datamachine-events-pagination').outerHTML = data.pagination.html;
// Update results counter
document.querySelector('.datamachine-events-results-counter').outerHTML = data.counter;
});
Server Response
{
"success": true,
"html": "Rendered events HTML for 5 days",
"pagination": {
"html": "Pagination controls HTML",
"current_page": 2,
"max_pages": 5,
"total_events": 23
},
"counter": "Results counter HTML",
"navigation": {
"html": "Past/upcoming navigation HTML",
"past_count": 150,
"future_count": 87
}
}
Customization
Pagination Filters
// Customize pagination arguments
add_filter('datamachine_events_pagination_args', function($args, $current_page, $max_pages, $show_past) {
// Custom prev/next text
$args['prev_text'] = '← Previous 5 Days';
$args['next_text'] = 'Next 5 Days →';
return $args;
}, 10, 4);
// Customize pagination wrapper classes
add_filter('datamachine_events_pagination_wrapper_classes', function($classes, $current_page, $max_pages, $show_past) {
$classes[] = 'custom-pagination-class';
return $classes;
}, 10, 4);
Query Customization
// Modify calendar query arguments
add_filter('datamachine_events_calendar_query_args', function($args, $params) {
// Add custom meta query conditions
return $args;
}, 10, 2);
Edge Cases
| Scenario | Behavior |
|---|---|
| Fewer than 5 days of events | Shows all events, pagination hidden (max_pages = 1) |
| No events | Shows "no events" template, pagination hidden (max_pages = 0) |
| Exactly 5 days | Shows all events, pagination hidden (max_pages = 1) |
| Invalid page number | Clamped to valid range (1 to max_pages) |
| User-provided date filter | Pagination operates within user’s date range |
| Past events mode | Same logic, ordered DESC by date |
Accessibility
ARIA Support
<nav class="datamachine-events-pagination" aria-label="Events pagination">
<ul class="page-numbers">
<li><a class="prev page-numbers" href="?paged=1">« Previous</a></li>
<li><span aria-current="page" class="page-numbers current">2</span></li>
<li><a class="next page-numbers" href="?paged=3">Next »</a></li>
</ul>
</nav>
Keyboard Navigation
- Standard link navigation with Tab key
- Enter/Space to activate pagination links
- Browser back/forward for history navigation
Performance
Why Day-Based Pagination?
- User Experience: Natural calendar browsing without split days
- Predictable Navigation: "Next 5 days" is more intuitive than "next 10 events"
- Scalable: Works consistently whether a day has 2 events or 50 events
Query Optimization
- Single query for unique dates (IDs only, minimal memory)
- Single query for page events using date boundaries
- No post-processing to group events (already within date range)
Troubleshooting
Common Issues
- Pagination Not Appearing: Verify more than 5 unique event days exist
- Wrong Page Count: Check
DAYS_PER_PAGEconstant value - Events Missing: Verify
_datamachine_event_datetimemeta field is populated
Debug Information
// Debug pagination data
$unique_dates = Calendar_Query::get_unique_event_dates($base_params);
error_log('Total unique days: ' . count($unique_dates));
error_log('First date: ' . ($unique_dates[0] ?? 'none'));
error_log('Last date: ' . ($unique_dates[count($unique_dates)-1] ?? 'none'));
$boundaries = Calendar_Query::get_date_boundaries_for_page($unique_dates, $current_page);
error_log('Page ' . $current_page . ' boundaries: ' . $boundaries['start_date'] . ' to ' . $boundaries['end_date']);
error_log('Max pages: ' . $boundaries['max_pages']);
The Pagination system ensures complete day groups are always displayed together, providing a natural calendar browsing experience for high-volume event calendars.