skip to Main Content

I am trying to create a function with post query and with following structure in output:

    2021
      January
        1.Post Title
        2.Post Title
      March
        3.Post Title

    2020
      May
        4.Post Title

Here is what I’ve done so far:

global $post;

$posts = get_posts( array(
    'post_type' => 'history',
    'orderby'   => 'date'
) );

$_year_mon = '';
$_has_grp = false;

foreach ( $posts as $post ) {
    setup_postdata( $post );

    $time = strtotime( $post->post_date );
    $year = date( 'Y', $time );
    $mon = date( 'F', $time );
    $year_mon = "$year-$mon";

    if ( $year_mon !== $_year_mon ) {
        // Close previous group, if any.
        if ( $_has_grp ) {
            echo '</div>';
            echo '</div>';
        }
        $_has_grp = true;

        echo '<div class="year">';
        echo "<span>$year</span>";

        echo '<div class="month">';
        echo "<span>$mon</span>";
    }

    // Display post title.
    if ( $title = get_the_title() ) {
        echo "<div>$title</div>";
    } else {
        echo "<div>#{$post->ID}</div>";
    }

    $_year_mon = $year_mon;
}

if ( $_has_grp ) {
    echo '</div>';
    echo '</div>';
}

wp_reset_postdata();

The problem is that same year with multiple month is not grouped in one element..

Current output:

    2021
      January
        1.Post Title
        2.Post Title
    
    2021
      March
        3.Post Title
    
    2020
      May
        4.Post Title

2

Answers


  1. Create an multidimensional array like this way and print it. You might get appropriate result.

    $master = [];
    
    foreach ( $posts as $post ) {
        setup_postdata( $post );
    
        $time = strtotime( $post->post_date );
        $year = date( 'Y', $time );
        $mon = date( 'F', $time );
        
        $master[$year][$mon][] = $post;
    }
    
    wp_reset_postdata();
    
    echo '<pre>';
    print_r($master);
    echo '</pre>';
    
    Login or Signup to reply.
  2. The following solution has been fully tested and works seamlessly fine for the default wordpress post type. If you want to run it for your custome post type that has its own custom fields then you may encounter some discrepancies. Therefore, feel free to customize it for your own custom post type as needed.

    As we talked on this question, when you use get_posts it will return post objects. Each post object contains the following properties/data:

    WP_Post Object
    (
        [ID] =>
        [post_author] =>
        [post_date] => 
        [post_date_gmt] => 
        [post_content] => 
        [post_title] => 
        [post_excerpt] => 
        [post_status] =>
        [comment_status] =>
        [ping_status] => 
        [post_password] => 
        [post_name] =>
        [to_ping] => 
        [pinged] => 
        [post_modified] => 
        [post_modified_gmt] =>
        [post_content_filtered] => 
        [post_parent] => 
        [guid] => 
        [menu_order] =>
        [post_type] =>
        [post_mime_type] => 
        [comment_count] =>
        [filter] =>
    )
    

    For example if you need, title, content, author, exerpt of the post, then you could create a multi-dimensional array and use array_push function to create your custom array like so:

    $posts = get_posts(array(
      'post_type' => 'history', // Since 'history' is a custom post type, I tested it on 'post'
    //'post_type' => 'post'
      'posts_per_page' => -1,
      'orderby'   => 'date'
    ));
    
    // You could see the number of posts for your query
    echo "Number of posts found: ". count($posts) . "<br>";
    
    $your_custom_array_yearly = array();
    
    foreach ($posts as $post) {
      setup_postdata($post);
      $time = strtotime($post->post_date);
      $year = date('Y', $time);
      $mon = date('F', $time);
    
      if (!($your_custom_array_yearly[$year][$mon])) {
        $your_custom_array_yearly[$year][$mon] = array();
        array_push(
          $your_custom_array_yearly[$year][$mon],
          [
            'title'   => $post->post_title,
            'excerpt' => $post->post_excerpt,
            'author'  => $post->post_author,
            'content' => $post->post_content,
          ]
        );
      } else {
        array_push(
          $your_custom_array_yearly[$year][$mon],
          [
            'title'   => $post->post_title,
            'excerpt' => $post->post_excerpt,
            'author'  => $post->post_author,
            'content' => $post->post_content,
          ]
        );
      };
    }
    
    wp_reset_postdata();
    

    For debugging/investigating the array, you could use this:

    echo "<pre>";
    print_r($your_custom_array_yearly);
    echo "</pre>";
    

    Now that you have your own custom array, then you could loop through it and output your data:

    foreach ((array)$your_custom_array_yearly as $yearly => $yvalue) {
      echo $yearly . ": <br>";
      echo "<ul>";
      foreach ($yvalue as $monthly => $mvalue) {
        echo "<li>" . $monthly . ": </li><ul>";
        foreach ($mvalue as $monthly_k => $monthly_v) {
          foreach ($monthly_v as $post_title_k => $post_title_v) {
            echo "<li>" . $post_title_k . ": " . $post_title_v . "</li></ul>";
          }
        }
      }
      echo "</ul>";
    }
    

    The above code will output the following result:

    2021:
      January:
        1.Title: Post Title
        1.Excerpt: Post excerpt
        1.Author: Post author
        1.Content: Post content
        2.Title: Post Title
        2.Excerpt: Post excerpt
        2.Author: Post author
        2.Content: Post content
      March:
        3.Title: Post Title
        3.Excerpt: Post excerpt
        3.Author: Post author
        3.Content: Post content
    
    2020:
      May:
        4.Title: Post Title
        4.Excerpt: Post excerpt
        4.Author: Post author
        4.Content: Post content
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search