1

I have a widget that must display my custom post type. I don't display title or content, but just the values of 3 custom fields (acf) : start date, end date and city.

Here is my loop. I grab all the custom post that have the selected taxonomies (category and level). It's working.

$args = array(
    'post_type' => 'formation',
    'posts_per_page'    => $instance['number_events'],
    'tax_query' => array(
        'relation' => 'AND',
        array(
            'taxonomy' => 'categories-formations',
            'field' => 'id',
            'terms' => $tax_id,
        ),
         array(
            'taxonomy' => 'niveau-formations',
            'field'    => 'id',
            'terms'    =>  $niveau_id,
        ),
    ),

);
$upcoming_events = new WP_Query($args);

if ( $title ) {
    echo $before_title . $title . $after_title;
}
// vars
?>
<ul class="event_entries">
    <?php  
    while( $upcoming_events->have_posts() ): $upcoming_events->the_post();
        $event_start_date = get_field( 'date_de_debut');
        $event_end_date = get_field('date_de_fin' );
        $city = get_field('ville'); 
        $posts = $upcoming_events->posts;
        echo '<h3>' . $city . '</h3>';
        echo '<ul>';
        echo '<li>Du' . $event_start_date . ' au ' . $event_end_date . '</li>';
        echo '</ul>';
    endwhile; ?>
    <?php
    wp_reset_query();
    echo $after_widget;
}

In frontend, I have my list with the city name as h3 and the corresponding start and end date in a ul li group.

Paris

    Du13/07/2020 au 24/07/2020

Paris

    Du22/06/2020 au 26/06/2020

Orléans

    Du18/05/2020 au 22/05/2020

What I want to have is to group by city name, in order to have this: (all dates corresponding to a particular city grouped together)

Paris

    Du13/07/2020 au 24/07/2020

    Du22/06/2020 au 26/06/2020

Orléans

    Du18/05/2020 au 22/05/2020

Please can anyone help me because i'm on this since two days, trying multiple queries and loop. I don't know how to achieve this. I would like to understand the logic.

1 Answer 1

1

First off, in your tax queries (tax_query), you should use 'field' => 'term_id' and not 'field' => 'id' — i.e. use term_id and not id. But the default value/field is term_id (term ID), though.

So you can achieve the grouped display like so, and you'd use the code in place of those lines starting from the <ul class="event_entries"> up to that wp_reset_query(); — you don't need to reset the global/main query because you're just making a secondary query which doesn't touch the global $wp_query variable:

<?php
// First, group the posts by the city.
$groups = array();
foreach ( $upcoming_events->posts as $i => $p ) {
    $city = get_field( 'ville', $p->ID );

    if ( ! isset( $groups[ $city ] ) ) {
        $groups[ $city ] = array();
    }
    $groups[ $city ][] =& $upcoming_events->posts[ $i ];
}

// Optional, sort the city names.
ksort( $groups, SORT_FLAG_CASE );

// Then display the posts.
if ( ! empty( $groups ) ) {
    echo '<ul class="event_entries">';
    foreach ( $groups as $city => $posts ) {
        if ( ! empty( $posts ) ) {
            // Display city name.
            echo '<h3>' . $city . '</h3>';

            // Display posts in that city.
            echo '<ul>';
            foreach ( $posts as $post ) {
                $event_start_date = get_field( 'date_de_debut', $post->ID );
                $event_end_date   = get_field( 'date_de_fin', $post->ID );

                echo '<li>Du' . $event_start_date . ' au ' . $event_end_date . '</li>';
            }
            echo '</ul>';
        }
    }
    echo '</ul>'; // close .event_entries
}

Note that I'm assuming all the posts always have a valid city.

Also, the above technique doesn't alter the original posts in the $upcoming_events object, and if you called setup_postdata() in your code, you should also call wp_reset_postdata() at the end of your code/function.

UPDATE: In response to your comment.

Sorry, I can't give a full tutorial or detailed guide on The Loop, but check that linked article and you'd at least understand what is, and how to use or work with The Loop.

However, what I can tell you from the above code, are:

  1. When you do $upcoming_events = new WP_Query(), the $upcoming_events variable becomes an instance of the WP_Query class and therefore, the variable will inherit/contain all the (public) properties (e.g. $upcoming_events->posts) and methods/functions (e.g. $upcoming_events->query()) of the class. And you can find out about those properties and methods by checking the official reference here.

  2. "I also don't understand what is $upcoming_events->posts as $i => $p'..what are $i and $p??" - $i is the index of the current item in the $upcoming_events->posts which is being looped through, whereas $p is the value of that current item (i.e. $upcoming_events->posts[ $i ]) which is a WP_Post instance — but if you're having trouble understanding that "current item", then you need to check the PHP manual on foreach, or you can ask or search for "What is foreach in PHP?" on Stack Overflow..

  3. "I also don't understand $groups[ $city ][] =& $upcoming_events->posts[ $i ];" - well, that is just an alternative to $groups[ $city ][] = $upcoming_events->posts[ $i ]; (copying; note the = versus =&). And it's called "assigning by reference" which basically helps improve performance in that we don't need to copy the post objects to $groups and simply reference to them in the original $upcoming_events->posts.

    See this Stack Overflow question and/or this article for more details.

    But basically, think of references like shortcuts in your computer; e.g. an icon on the desktop that points to a file, e.g. a text file. So $my_var =& $file; is like making a shortcut to that text file, whereas $my_var = $file; (once again, note the = vs =&) is like you copied the file to your desktop.

  4. "I don't understand what is inside the $posts variable" - if you mean that foreach ( $groups as $city => $posts ), then that $posts is an array of post object references, so $posts[0] for example, is a reference to $upcoming_events->posts[0] which is a post object. I.e. They both point to the exact same WP_Post instance (see point #2 above).

So I hope those 4 points help you, but for more information about PHP-specific things like foreach/loop and references, please ask on Stack Overflow or search on Google, etc. :)

4
  • 1
    First of all, i'm really greatful to you, you saved me hours or days of headache ! I really appreciate your help and I just had to copy your code and it's working perfectly...Thank you very very much!!!
    – blogob
    Mar 23, 2020 at 23:29
  • I still have question regarding your code. I didn't know that I could use a '$upcoming_events = new WP_Query($args);' and then a foreach loop, instead of the traditionnal 'while( $upcoming_events->have_posts() ): $upcoming_events->the_post();'.. I also don't understand what is 'posts as $i => $p )'..what are $i and $p?? I also don't understand '$groups[ $city ][] =& $upcoming_events->posts[ $i ];' And finaly I don't understand what is inside the '$posts' variable..I'll look at this closer in order to understand. But you help me improve my skill. Thanks a lot. Gracie mille !!
    – blogob
    Mar 23, 2020 at 23:31
  • So I have revised the answer, but you'll need to take time to check all the links, I mean read (and understand) the contents. We would not master a skill at a blink of an eye, right?.. However, please first accept the answer (i.e. mark it as correct) since I believe it already answered your original question. :) Happy coding & all the best!
    – Sally CJ
    Mar 24, 2020 at 4:52
  • 1
    Thank you so much Sally !!
    – blogob
    Mar 24, 2020 at 13:40

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.