php - WP_QUERY, ARRAYS, Duplicate post if has multiple categories

I have a shortcode with wp_query loop which outputs a list of categories as navigation and list of posts. The original code is much more complex so I tried to remove unnecessary lines for better understanding.

function filter_shortcode($atts, $content = null) {

    global $post;
    
    $attributes = shortcode_atts(
        array(
            'type'      => '',  
            'num'       => -1
         ), $atts);

    $args = array(
        'post_type'      => $attributes["type"],
        'posts_per_page' => $attributes["num"]
    );  
        
    $post_query = new WP_Query($args);
    
    $master_array = array();
    
    if ($post_query->have_posts() ) {
            
        while ($post_query->have_posts()) {
                
            $post_query->the_post();
                    
            $nav_arr = wp_get_post_terms( get_the_ID(), 'solutions_category' );
            
            // Problem here
            foreach($nav_arr as $nav_value ) {
                $nav = $nav_value->name; 
            }
            
            
            $box_tooltip = '<div>Lorem Ipsum</div>';
            $info_box_title = '<h5 style="margin-bottom: 0;">' . strtoupper($post->post_title) . '</h5>';       
                
                
            if (!($master_array[$nav])) {
                $master_array[$nav] = array();
                array_push(
                      $master_array[$nav],
                      [ 
                        'icon_title'  => $info_box_title, 
                        'tooltip'     => $box_tooltip,  
                      ]
                );
            } else {
                array_push(
                      $master_array[$nav],
                      [ 
                        'icon_title'  => $info_box_title, 
                        'tooltip'     => $box_tooltip,  
                      ]
                );
            }
                
        } // End of while have_posts
        
        wp_reset_postdata();

        /* Foreach master array */
        foreach ((array)$master_array as $master_key => $master_value) {
            
            /* Navigation */
            $navigation .= '<div>'. $master_key .'</div>';

            foreach($master_value as $query_key => $query_value) {
                
                $output_inner = '';     
                
                foreach ($query_value as $object_key => $object_value) {
                    $output_inner .= $object_value;
                }
                /* Content */
                $output_outer .= '<div class="'. $master_key .'">'. $output_inner .'</div>';
                
            } // end foreach mastervalue

        } // end foreach master
    
    } // end if have posts
    
    return '
            <div style="margin-bottom: 30px;">'. $navigation .'</div>
            <div>'. $output_outer .'</div>
           ';
            
}
add_shortcode('cpt_filter', 'filter_shortcode');

Output:

enter image description here

The problem is that if post has more than one category the function will output only one occurrence of post. For example "GOOGLE WORKSPACE" has two categories - "Collaboration" and "Communication" but it outputs only once.

enter image description here

How I can output "GOOGLE WORKSPACE" as many times as the number of categories it has, and also to have it's own category class specified?

var_dump($nav_arr) on "GOOGLE WORKSPACE" post iteration: array(2) { [0]=> object(WP_Term)#3992 (10) { ["term_id"]=> int(825) ["name"]=> string(13) "Collaboration" ["slug"]=> string(13) "collaboration" ["term_group"]=> int(0) ["term_taxonomy_id"]=> int(825) ["taxonomy"]=> string(18) "solutions_category" ["description"]=> string(0) "" ["parent"]=> int(0) ["count"]=> int(8) ["filter"]=> string(3) "raw" } [1]=> object(WP_Term)#3986 (10) { ["term_id"]=> int(824) ["name"]=> string(13) "Communication" ["slug"]=> string(13) "communication" ["term_group"]=> int(0) ["term_taxonomy_id"]=> int(824) ["taxonomy"]=> string(18) "solutions_category" ["description"]=> string(0) "" ["parent"]=> int(0) ["count"]=> int(8) ["filter"]=> string(3) "raw" } }

Tried to check if $nav_arr has more than one value if (sizeof($nav_arr) > 1) { .... } and create separate array, but stuck.

Answer

Solution:

The problem appears to be that in the following foreach statement:

foreach($nav_arr as $nav_value ) {
    $nav = $nav_value->name; 
}

You are iterating through the array once, and setting the value of $nav to "Collaboration" and then when you are looping through it the second time, (because there are two items in $nav_arr), you are just rewriting $nav to the new value of "Communication", ending up with only one $nav.

$nav should be saved as an array of values, instead of a string of a single value, like this:

foreach($nav_arr as $nav_value){
    $nav[] = $nav_value;
}

Without seeing the full code, I am having a hard time understanding what all is going on.... but I think you can then replace all of:

if (!($master_array[$nav])) {
            $master_array[$nav] = array();
            array_push(
                  $master_array[$nav],
                  [ 
                    'icon_title'  => $info_box_title, 
                    'tooltip'     => $box_tooltip,  
                  ]
            );
        } else {
            array_push(
                  $master_array[$nav],
                  [ 
                    'icon_title'  => $info_box_title, 
                    'tooltip'     => $box_tooltip,  
                  ]
            );
        }
}

with the following:

foreach($nav as $navitem){
    $master_array[$nav] = array(
        'icon_title'  => $info_box_title,
        'tool_tip'    => $box_tooltip
    ),
}

Source