Displaying Future Events with Advanced Custom Fields (ACF) and WP_Query in WordPress

[ ][ ] July 20, 2024

Whats the Problem?

When building dynamic websites with WordPress, you might need to list upcoming events or posts from a custom post type while ensuring that only future events are displayed. This requires integrating custom fields for event dates and filtering posts based on these dates. Achieving this functionality can be complex, especially when working with Advanced Custom Fields (ACF) and custom taxonomies.

The Solution


To solve this problem, we’ll create a custom Gutenberg block in WordPress that lists terms from a selected taxonomy, displaying only posts with future event dates. We’ll use ACF for custom fields, specifically for selecting the post type and taxonomy, as well as storing event start dates. The solution involves querying posts with WP_Query and filtering them based on the event dates.

Step-by-Step Guide

  1. Create the Custom Block: Register a custom Gutenberg block that will handle displaying the list of terms and their posts.
  2. Fetch ACF Fields:Retrieve the selected post type and taxonomy using ACF. Default to `post` and `category` if these fields are empty.
  3. Fetch and Filter Terms: Fetch terms from the selected taxonomy and check if each term has posts with future event dates or no date set.
  4. Filter Posts Based on Event Date: For each term, query the posts and filter out those with past event dates. Only include posts with future dates or no date set.
  5. Display the Terms and Posts: Output the terms with their descriptions and the list of filtered posts. Ensure the event start date is formatted and displayed after the post title.

Below is a code example that demonstrates this approach:

<?php
// Fetch the ACF fields
$post_type = get_field('select_post_type') ?: 'post';
$taxonomy = get_field('select_taxonomy') ?: 'category';

// Fetch terms
$terms = get_terms(array(
    'taxonomy' => $taxonomy,
    'hide_empty' => true,
));

if (!empty($terms) && !is_wp_error($terms)) {
    $terms_with_posts = [];

    // Loop through each term and check if it has posts
    foreach ($terms as $term) {
        // Get posts for each term with date conditions
        $query = new WP_Query(array(
            'post_type' => $post_type,
            'tax_query' => array(
                array(
                    'taxonomy' => $taxonomy,
                    'field' => 'term_id',
                    'terms' => $term->term_id,
                ),
            ),
            'meta_query' => array(
                array(
                    'key' => 'event_start_date',
                    'compare' => 'EXISTS', // Ensure 'event_start_date' exists
                ),
            ),
            'posts_per_page' => -1, // Fetch all posts to filter later
        ));

        // Check if any posts are returned
        if ($query->have_posts()) {
            // Filter posts based on the event_start_date
            $filtered_posts = [];
            while ($query->have_posts()) {
                $query->the_post();
                $post_id = get_the_ID();
                $event_start_date = get_field('event_start_date', $post_id);

                if ($event_start_date) {
                    $event_date = strtotime($event_start_date);
                    $current_date = current_time('timestamp');
                    // Include post if the date is in the future or not set
                    if ($event_date >= $current_date) {
                        $filtered_posts[] = get_the_ID();
                    }
                } else {
                    $filtered_posts[] = get_the_ID(); // Include posts without a date set
                }
            }
            wp_reset_postdata();

            // Add the term if there are filtered posts
            if (!empty($filtered_posts)) {
                $terms_with_posts[] = $term;
            }
        }
    }

    if (!empty($terms_with_posts)) {
        echo '<div class="upcoming-list-container">';
        foreach ($terms_with_posts as $term) {
            $term_link = get_term_link($term);
            $term_name = $term->name;
            $term_description = term_description($term->term_id, $taxonomy); // Get the term description

            // Get posts for each term again for display
            $query = new WP_Query(array(
                'post_type' => $post_type,
                'tax_query' => array(
                    array(
                        'taxonomy' => $taxonomy,
                        'field' => 'term_id',
                        'terms' => $term->term_id,
                    ),
                ),
                'meta_query' => array(
                    array(
                        'key' => 'event_start_date',
                        'compare' => 'EXISTS', // Ensure 'event_start_date' exists
                    ),
                ),
            ));

            echo '<div class="upcoming-list-term">';
                echo '<div class="list-inner-header">';
                    echo '<h2 class="list-term_title"><a href="' . esc_url($term_link) . '">' . esc_html($term_name) . '</a></h2>';
                    if ($term_description) {
                        echo wp_kses_post($term_description); // Display term description with HTML allowed
                    }
                echo '</div>';
                echo '<div class="list-inner_container">';
                    switch ($post_type) {
                        case 'event':
                            echo '<h3 class="list-title">Upcoming Classes</h3>';
                            break;
                        
                        default:
                            echo '<h3 class="list-title">Upcoming Events</h3>';
                            break;
                    }
                    echo '<ul class="upcoming-list-posts">';
                    while ($query->have_posts()) {
                        $query->the_post();
                        $post_id = get_the_ID(); // Get the current post ID
                        $event_start_date = get_field('event_start_date', $post_id); // Pass post ID here

                        if ($event_start_date) {
                            $event_date = strtotime($event_start_date);
                            $current_date = current_time('timestamp');
                            // Format the date if it is in the future or not set
                            if ($event_date >= $current_date) {
                                $formatted_date = date('F j, Y', $event_date);
                            } else {
                                $formatted_date = 'Date not set';
                            }
                        } else {
                            $formatted_date = 'Date not set';
                        }

                        if ($formatted_date !== 'Date not set') {
                            echo '<li class="upcoming-list">';
                            echo '<a href="' . get_permalink() . '">' . esc_html($formatted_date) . ' – ' . get_the_title() . '</a>';
                            echo '</li>';
                        }
                    }
                    echo '</ul>';
                echo '</div>';
            echo '</div>';
            wp_reset_postdata();
        }
        echo '</div>';
    } else {
        echo '<p>No terms with posts found.</p>';
    }
} else {
    echo '<p>No terms found.</p>';
}
?>

Conclusion

By following this guide, you can create a custom Gutenberg block that dynamically lists terms from a selected taxonomy and displays posts with future event dates. This solution leverages Advanced Custom Fields (ACF) and WP_Query to fetch and filter posts effectively. It ensures that only relevant future events are displayed, providing a seamless experience for users looking for upcoming events or posts. This approach can be adapted and expanded to fit various use cases and custom post types within WordPress.

Please note: This website is currently under development. I appreciate your patience and understanding as I work on perfecting everything. Thank you!