Issue with filtering posts by date ranges in ACF repeater fields on WordPress

I am working on a WordPress website where I have posts that include ACF repeater fields. Each repeater consists of date ranges defined by a start date and an end date. I need to filter these posts based on user-selected dates that should match with any of the defined date ranges in the repeaters, but it seems that my current implementation is faulty.

For example:
One of my posts has repeaters with the following date ranges:

  • From 01.06.2025 to 15.06.2025
  • From 01.08.2025 to 15.08.2025

When I enter the dates from 20.06.25 to 30.06.25, this post still appears in the results, even though it shouldn’t.

function fetch_more_posts() {
    add_filter('posts_where', 'customize_posts_where');
    
    function customize_posts_where($where) {
        $where = str_replace("meta_key = 'date_range_$", "meta_key LIKE 'date_range_%", $where);
        return $where;
    }

    $input_date_start = isset($_POST['date_start']) ? sanitize_text_field($_POST['date_start']) : '';
    $input_date_end = isset($_POST['date_end']) ? sanitize_text_field($_POST['date_end']) : '';

    $query_parameters = array(
        'post_type' => 'post',
        'posts_per_page' => 8,
        'suppress_filters' => false,
        'meta_query' => [
            'relation' => 'AND',
            [
                'relation' => 'AND',
                [
                    'key' => 'date_range_$_date_start',
                    'value' => $input_date_end,
                    'compare' => '<=',
                    'type' => 'DATE',
                ],
                [
                    'key' => 'date_range_$_date_end', 
                    'value' => $input_date_start,
                    'compare' => '>=',
                    'type' => 'DATE',
                ],
            ],
        ]
    );

    $custom_query = new WP_Query($query_parameters);
    remove_filter('posts_where', 'customize_posts_where');

    $result_html_content = '';
    if ($custom_query->have_posts()) {
        ob_start();
        while ($custom_query->have_posts()) {
            $custom_query->the_post();
            echo '<div>' . get_the_title() . '</div>';
        }
        $result_html_content = ob_get_contents();
        ob_end_clean();
    }

    echo json_encode(['html' => $result_html_content]);
    exit;
}
add_action('wp_ajax_fetch_more_posts', 'fetch_more_posts');
add_action('wp_ajax_nopriv_fetch_more_posts', 'fetch_more_posts');

I also attempted using the BETWEEN clause, but it didn’t yield the expected results. What is the proper way to filter posts that have overlapping date ranges defined in ACF repeater fields?

I’ve hit this same ACF repeater date filtering problem. The issue is WordPress can’t properly match across multiple repeater rows for one post - your customize_posts_where filter won’t fix that since meta_query is still broken for this use case.

Here’s what actually worked: ditch the meta_query approach and use pre_get_posts with a custom $wpdb query instead. Hook into pre_get_posts, grab all posts with your repeater field, then query the postmeta table directly with a JOIN that checks for overlapping date ranges across all repeater instances. Use HAVING COUNT to make sure at least one range overlaps your input dates.

This completely bypasses WordPress’s meta_query limitations and gives you real control over the repeater logic.

your logic’s backwards here. the meta_query checks if one row matches both conditions, but with repeaters you need posts that have at least one overlapping range. try a custom sql query instead, or use get_posts with a custom where clause that properly joins the postmeta table for repeater data.

WordPress repeater field queries are a pain - meta_query just can’t handle the dynamic indices properly. Your current approach won’t work. I hit this same wall last year. Here’s what actually works: query all posts with the repeater field first, then filter in PHP. Loop through each post’s repeater data with get_field() and manually check for date overlaps using: range_start <= input_end AND range_end >= input_start. Yeah, it’s less efficient than a direct query, but it actually works. Trying to force meta_query with dynamic field names is just asking for headaches.