I have CPT called "investment" and I want to display all the posts in that CPT by their category. I'm doing that via shortcode.

e.g. [myshortcode type_of_post="investment" cat="12,30"]

and I want it to display posts as below

Category 12

  • post1
  • post2

Category 30

  • post3
  • post4

The problem that I have right now is that it shows all the posts but I'm struggling to separate posts by their categories.

This is how it shows right now:

  • post1
  • post2
  • post3
  • post4

Here's my code:

  // define attributes
    extract( shortcode_atts( array (
        'cat'   => '',
        'type_of_post' => '',
        'orderby' => 'date',
    ), $atts ) );

Separate posts

    $args['post_type'] = $type_of_post;
if($cat) {
    $args['category__in'] = array_map('intval',explode(',',$cat));


    $query = new WP_Query( $args );

    if( $query->have_posts() ){
        echo "<div class='row col-div investment'>";
        while( $query->have_posts() ){
            $id = get_the_ID();
            $page = get_post($id);
            $image = get_the_post_thumbnail_url($id);

           display_layout($page,$image); //calls the function below
        echo '</div>';

Display posts

function display_layout($page,$image) {
    echo "<div class='col-sm-6  pair-cards'>
            <div class='sub-div'>
                <div class='card-img'>
                <a href='$page->post_name'><img src='$image' alt='$page->post_name'  /></a>
                    "<div class='entry-title'><a href='$page->post_name'>" .  $page->post_title  .  "</a></div>
                <div class='card-desc'>".
                    "<p>" . strip_tags($page->post_excerpt) ."</p>
                <div class='read-more'>
                    <a href='$page->post_name'> Read More </a>

I hope I've explained my problem clearly.

1 Answer 1


In the loop you'll want to get the posts terms. Then instead of echo'ing, return the output and group it into an array where the keys are the categories. Then loop through the grouped array and output your categories and related posts. Something similar to:

// to hold and sort your output
$categories = array();

// loop through posts
while( $query->have_posts() ){

    // get the posts terms
    $terms = wp_get_post_terms(..
    $firstTerm = $terms[0];

    // see if the first term is defined in our holder
    if (!isset($categories[$firstTerm->name])) {
        $categories[$firstTerm->name] = array();

    // ..

    $categories[$firstTerm->name][] = display_layout($page,$image);

Your display_layout() function would return your html instead of echoing.

Then you'd loop through $categories for your output:

foreach ($categories as $catName => $catItems) {
    echo "<strong>{$catName}</strong>";
    foreach ($catItems as $catItem) {
        echo $catItem;

This isn't a copy/paste answer, it's just an idea for the logic of storing and sorting your values in an array - please keep in mind that you will want to:

  • use better logic than "first term", as the first term won't necessarily be 12 or 30 (unless you only did one-term-per-post - but that still leaves high probability of human error), you'll want to loop through all the terms and see if it contains a $cat value
  • use IDs instead of strings for the array keys, $firstTerm->term_id instead of $firstTerm->name, finding a different way of carrying over the name value.
  • wrap this in a cache so each page load isn't doing a ton of queries
  • Thanks very much for the answer!! few edits: $terms = wp_get_post_terms($id, 'category'); for the nested foreach loop foreach($catItems as $itemKey => $values) { echo $values; }
    – Mais_Cule
    Commented Aug 8, 2018 at 6:42
  • I cant vote up your answer coz I dont have enough reputation. but I've ticked your answer!
    – Mais_Cule
    Commented Aug 8, 2018 at 6:47

Your Answer

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

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