Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

WordPress: Order WP_Query by custom date field and split the loop after every month

I'm using a WordPress Custom Post Type to manage and display events on my site. To sort the events I'm using a custom meta field with the date in it (stored like this: YYYY-MM-DD). I check the meta field against the current date. Like this:

$current_date_query = date ('Y-m-d');

$temp = $wp_query; $wp_query= null; $wp_query = new WP_Query();

$wp_query->query(
    array(
       'post_type' => 'event',
       'meta_key' => 'event_date',
       'meta_compare' => '>=',
       'meta_value' => $current_date_query,
       'post_status' => 'publish',
       'posts_per_page' => '99',
       'orderby' => 'meta_value',
       'order' => 'ASC',
       'paged' => $paged
    )
); ?>


... Loop stuff ...

<?php endif; $wp_query = null; $wp_query = $temp; wp_reset_query(); ?>

I now want to add a headline before every new month. Like this:

January 2018

  • Event 1
  • Event 2
  • Event 3

February 2018

  • Event 4
  • Event 5

March 2018

  • Event 5

and so on...

Is there any way to split the loop after every month? I guess I have to use the month from the meta field but I don't know how.

I tried the following solution (thanks to the comment from @FluffyKitten):

<?php

/* declare an array to save the data */
$quarters = array();

foreach($posts as $q) {

    $donor_date = get_post_meta($q->ID,'gid_22',true);
    $donor_month = date('m',strtotime($donor_date));

    /* format the date once - best practice is not to repeat code */
    $formatteddate = date('d/m/Y',strtotime($donor_date));

    /* organise the dates by quarter, using the heading as the key for easy display */
    if(in_array($donor_month, array('01')))
       $quarters["January"][] = $formatteddate;

    else if(in_array($donor_month, array('02')))
       $quarters["February"][] = $formatteddate;

    else if(in_array($donor_month, array('03')))
       $quarters["March"][] = $formatteddate;

    else if(in_array($donor_month, array('04')))
       $quarters["April"][] = $formatteddate;

    else if(in_array($donor_month, array('05')))
       $quarters["May"][] = $formatteddate;

    else if(in_array($donor_month, array('06')))
       $quarters["June"][] = $formatteddate;

    else if(in_array($donor_month, array('07')))
       $quarters["July"][] = $formatteddate;

    else if(in_array($donor_month, array('08')))
       $quarters["August"][] = $formatteddate;

    else if(in_array($donor_month, array('09')))
       $quarters["September"][] = $formatteddate;

    else if(in_array($donor_month, array('10')))
       $quarters["Ocotber"][] = $formatteddate;

    else if(in_array($donor_month, array('11')))
       $quarters["November"][] = $formatteddate;

    else if(in_array($donor_month, array('12')))
       $quarters["December"][] = $formatteddate;
}

 /* now go through your array and display the results */
 foreach ($quarters as $quartername => $quarter){
     /* $quarters won't have any entry for quarters with no dates, so technically this 'if' isn't needed,
       however I've added it in case it will be needed it for any other changes you make */
     if ($quarter){ ?>

         <h3><?php echo $quartername; ?></h3>
         <table>
         <?php  foreach ($quarter as $qdate ){ ?>
             <tr><td> <?php echo $qdate; ?> </td></tr>
         <?php  }  // end foreach($quarter...) ?>
         </table>
     <?php
     } // end if
} // end foreach($quarters...)
?>

Now I get a result like this:

Ocotber

  • 02/10/2018
  • 29/10/2017

January

  • 02/01/2018

December

  • 02/12/2017
  • 01/12/2017

May

  • 02/05/2018

...

The order is based on the post date and not based on the custom field. Also there is no difference between years. The year 2017 should have it's own loop as well as 2018.

If I try to put my own array in the first array of the loop, it has no effect and it only shows as text in the frontend.

Edit: I could order the month by using a custom query like this:

$posts = query_posts( array(
    'post_type' => 'event',
    'meta_key' => 'event_date',
    'meta_compare' => '>=',
    'meta_value' => $current_date_query,
    'post_status' => 'publish',
    'posts_per_page' => '99',
    'orderby' => 'meta_value',
    'order' => 'ASC',
    'paged' => $paged
) );

The problem now is only the year. It's all sort by month with different years per month.

like image 896
Cray Avatar asked Sep 29 '17 17:09

Cray


1 Answers

The solution you tried from @FluffyKitten doesn't work properly because it groups into groups of the same month, not the same month and year so falls over when there are posts in the same month of different years

As @ChinLeung suggested you need to store the current month in a variable to test when it has changed and output your headers. To implement this for your example you would do the following:

// Set current month
$current_month = '';

// Get event posts in order
$events = query_posts( array(
    'post_type' => 'event',
    'meta_key' => 'event_date',
    'meta_compare' => '>=',
    'meta_value' => date ('Y-m-d'),
    'meta_type'  => 'DATE',
    'post_status' => 'publish',
    'posts_per_page' => '99',
    'orderby' => 'meta_value',
    'order' => 'ASC',
    'paged' => $paged
) );

// Iterate over events
foreach($events as $key => $event){

  // Get event date from post meta
  $event_date = get_post_meta($event->ID, 'event_date', $single = true);

  // Cache event month/year
  $event_month = date('F Y', strtotime($event_date));

  // Open new group if new month
  if($current_month != $event_month){
    if($key !== 0) echo '</ul>';  
    echo "<h2>{$event_month}</h2>";
    echo '<ul>';  
  }     

  // Add event date as new list item
  echo "<li>{$event_date}</li>";

  // Cache $curret_month
  $current_month = $event_month;        
}

// Close the last list if query had events
if(count($events)) echo '</ul>';

EDIT

To update this solution to work for a post type archive, instead of the above add the following to your theme's functions.php, or a file included from it:

/**
 * Get event date 
 *
 * @return string
 */
function get_event_date() {

    /** @global WP_Post $post */
    global $post;

    /** Get event date from post meta */
    return get_post_meta($post->ID, 'event_date', $single = true);
}

/**
 * Get event month/year formatted
 *
 * @param string $event_date
 * 
 * @return string
 */
function format_event_date($event_date) {
    return date('F Y', strtotime($event_date));
}

/**
 * Order events archive by event date post meta
 *
 * @param WP_Query $query
 *
 * @return WP_Query
 */
function events_archive_pre_get_posts($query) {
    if (!$query->is_main_query() || !is_post_type_archive('event')) return $query;
    $query->set('meta_key', 'event_date');
    $query->set('meta_compare', '>=');
    $query->set('meta_value', date('Y-m-d'));
    $query->set('meta_type', 'DATE');
    $query->set('posts_per_page', 2);
    $query->set('orderby', 'meta_value');
    $query->set('order', 'ASC');
    return $query;
}

add_filter('pre_get_posts', 'events_archive_pre_get_posts');

Then you can set posts per page to the number desired and pagination will work as expected. Here is a sample post type archive template which can be used to add the date headers between neatly formatted HTML lists, or at least they were until SO converted tabs to spaces:

<?php // Template header
get_header(); ?>


<?php /** @global WP_Query */
global $wp_query; ?>

<?php // Events archive loop
for($previous_month = ''; have_posts(); $previous_month = $formatted_date): the_post();

    // Get event date from post meta
    $event_date = get_event_date();

    // Format event month/year
    $formatted_date = format_event_date($event_date);

    // The formatted date is the first or not the same as previous event
    if($previous_month != $formatted_date):

        // Close previous list if this is not the first
        if($wp_query->current_post !== 0) echo "\n\t</ul>\n"; 

        // New month template ?>
    <h2><?php echo $formatted_date; ?></h2>
<?php
        // Open new list
        echo "\t<ul>\n";
    endif; 

    // Event post template ?>
        <li><?php echo $event_date; ?></li>
<?php endfor;

// Reset query 
wp_reset_query();

// Close the last list if query had events 
if(have_posts()) echo "\t</ul>\n"; ?>


<?php // Pagination
the_posts_pagination(); ?>


<?php // Template footer
get_footer();
like image 134
Will Craig Avatar answered Oct 06 '22 02:10

Will Craig