functions - Output parentchild categories and posts in that parentchild hierarchy

admin2025-05-31  0

I'm using the WP_Query loop to output all categories and their posts into a jQuery accordion, using the category name as the accordion header, with posts from each category inside each accordion.

The problem with my current code is that it outputs a strictly alphabetical list of all top level categories and any child categories, like this:

A Top Level Category
Another Category
Child Category
D-Category
D Child Category
EE Category
Z Category

"Child Category" is a child of "A Top Level Category", but currently it isn't displayed directly under "A Top Level Category" to show the parent/child relationship. The same with "D Child Category" being a child of "Z Category".

What I want to do is output all parent and child categories in alphabetical order, but nest the child categories under their parent categories, like this:

A Top Level Category
- Child Category
Another Category
D-Category
EE Category
Z Category
- D Child Category

Do I need another foreach loop to determine if a parent has a child category and if so, display it?

Should I restrict $categories = get_categories(); to only top level categories, and then check parents for child categories near WP_Query and loop again for any child categories?

And what about child of a child categories?

(I know this use of WP_Query is very CPU intensive, but it's not for a site with lots of categories or posts, and I can investigate using transients down the road for storing the output.)


Update 5/11/25

Seems like we need to use / to get all children, even children of children, which my example doesn't take into account, since I don't know how :). Can get_term_children grab all children ad loop through them with a foreach?


Current code:

echo '<div class="accordion">';

    $categories = get_categories();

        foreach ($categories as $category)
            {
            echo '<h2>' . $category->name . '</h2>';
            echo $term_id;
            echo '<div><p>';

        $args = array(
            'post_type' => array('post','book','video'),
            'posts_per_page' => -1,
            'orderby' => 'date',
            'order' => 'DESC',
            'depth' => 2,
            'post_status'=> 'publish',
            'cat' => $category->term_id
            );

        $posts = new WP_Query($args);
            while ($posts->have_posts()) {
            $posts->the_post();
                    echo get_the_date();
                    echo '&nbsp;';
                    $post_type = get_post_type();
                    echo 'Type: ';
                    echo ucfirst($post_type); 
                    echo '&nbsp;';
                    echo '<a target="_blank" href="';
                    the_permalink();
                    echo '">';
                    the_title();
                    echo '</a>';
                    echo '&nbsp;';
                    echo wp_trim_words(get_the_excerpt(),20,'...');
                    echo '<br />';
                    }
        echo '</p></div>';
        }
    echo '</div>';

Also needed if you test this on your own localhost:

Header:

<script src=".7.1/jquery.min.js"></script> 
<link rel="stylesheet" href=".13.2/themes/smoothness/jquery-ui.css">
<script src=".13.2/jquery-ui.min.js"></script>

Footer:

<script>
jQuery( document ).ready(function() {
jQuery(".accordion").accordion({
    collapsible: true,
    clearStyle: true,
    active: false,
})
});
</script>

I'm using the WP_Query loop to output all categories and their posts into a jQuery accordion, using the category name as the accordion header, with posts from each category inside each accordion.

The problem with my current code is that it outputs a strictly alphabetical list of all top level categories and any child categories, like this:

A Top Level Category
Another Category
Child Category
D-Category
D Child Category
EE Category
Z Category

"Child Category" is a child of "A Top Level Category", but currently it isn't displayed directly under "A Top Level Category" to show the parent/child relationship. The same with "D Child Category" being a child of "Z Category".

What I want to do is output all parent and child categories in alphabetical order, but nest the child categories under their parent categories, like this:

A Top Level Category
- Child Category
Another Category
D-Category
EE Category
Z Category
- D Child Category

Do I need another foreach loop to determine if a parent has a child category and if so, display it?

Should I restrict $categories = get_categories(); to only top level categories, and then check parents for child categories near WP_Query and loop again for any child categories?

And what about child of a child categories?

(I know this use of WP_Query is very CPU intensive, but it's not for a site with lots of categories or posts, and I can investigate using transients down the road for storing the output.)


Update 5/11/25

Seems like we need to use https://developer.wordpress/reference/functions/get_term_children/ to get all children, even children of children, which my example doesn't take into account, since I don't know how :). Can get_term_children grab all children ad loop through them with a foreach?


Current code:

echo '<div class="accordion">';

    $categories = get_categories();

        foreach ($categories as $category)
            {
            echo '<h2>' . $category->name . '</h2>';
            echo $term_id;
            echo '<div><p>';

        $args = array(
            'post_type' => array('post','book','video'),
            'posts_per_page' => -1,
            'orderby' => 'date',
            'order' => 'DESC',
            'depth' => 2,
            'post_status'=> 'publish',
            'cat' => $category->term_id
            );

        $posts = new WP_Query($args);
            while ($posts->have_posts()) {
            $posts->the_post();
                    echo get_the_date();
                    echo '&nbsp;';
                    $post_type = get_post_type();
                    echo 'Type: ';
                    echo ucfirst($post_type); 
                    echo '&nbsp;';
                    echo '<a target="_blank" href="';
                    the_permalink();
                    echo '">';
                    the_title();
                    echo '</a>';
                    echo '&nbsp;';
                    echo wp_trim_words(get_the_excerpt(),20,'...');
                    echo '<br />';
                    }
        echo '</p></div>';
        }
    echo '</div>';

Also needed if you test this on your own localhost:

Header:

<script src="https://ajax.googleapis/ajax/libs/jquery/3.7.1/jquery.min.js"></script> 
<link rel="stylesheet" href="https://ajax.googleapis/ajax/libs/jqueryui/1.13.2/themes/smoothness/jquery-ui.css">
<script src="https://ajax.googleapis/ajax/libs/jqueryui/1.13.2/jquery-ui.min.js"></script>

Footer:

<script>
jQuery( document ).ready(function() {
jQuery(".accordion").accordion({
    collapsible: true,
    clearStyle: true,
    active: false,
})
});
</script>
Share Improve this question edited May 11 at 19:03 BlueDogRanch asked May 5 at 15:05 BlueDogRanchBlueDogRanch 1405 silver badges25 bronze badges
Add a comment  | 

2 Answers 2

Reset to default 0

You're right that we need to handle parent-child relationships differently. Let me explain the approach and then show you the code changes needed.

The key is to:

  • First get only top-level categories (parent = 0)
  • For each top-level category, check for child categories
  • Display the hierarchy properly in the accordion
<script src="https://ajax.googleapis/ajax/libs/jquery/3.7.1/jquery.min.js"></script> 
<link rel="stylesheet" href="https://ajax.googleapis/ajax/libs/jqueryui/1.13.2/themes/smoothness/jquery-ui.css">
<script src="https://ajax.googleapis/ajax/libs/jqueryui/1.13.2/jquery-ui.min.js"></script>
<?php
echo '<div class="accordion">';

// Get only top-level categories
$args = array(
    'parent' => 0,
    'orderby' => 'name',
    'order' => 'ASC'
);
$parent_categories = get_categories($args);

foreach ($parent_categories as $parent_category) {
    // Display parent category
    echo '<h2>' . $parent_category->name . '</h2>';
    echo '<div><p>';
    
    // Get posts for parent category
    $parent_args = array(
        'post_type' => array('post','book','video'),
        'posts_per_page' => -1,
        'orderby' => 'date',
        'order' => 'DESC',
        'post_status'=> 'publish',
        'cat' => $parent_category->term_id
    );
    
    $parent_posts = new WP_Query($parent_args);
    while ($parent_posts->have_posts()) {
        $parent_posts->the_post();
        echo get_the_date();
        echo '&nbsp;';
        $post_type = get_post_type();
        echo 'Type: ';
        echo ucfirst($post_type); 
        echo '&nbsp;';
        echo '<a target="_blank" href="';
        the_permalink();
        echo '">';
        the_title();
        echo '</a>';
        echo '&nbsp;';
        echo wp_trim_words(get_the_excerpt(),20,'...');
        echo '<br />';
    }
    wp_reset_postdata();
    
    // Get child categories
    $child_args = array(
        'parent' => $parent_category->term_id,
        'orderby' => 'name',
        'order' => 'ASC'
    );
    $child_categories = get_categories($child_args);
    
    // Display child categories and their posts
    foreach ($child_categories as $child_category) {
        echo '<h3>' . $child_category->name . '</h3>';
        echo '<div><p>';
        
        $child_post_args = array(
            'post_type' => array('post','book','video'),
            'posts_per_page' => -1,
            'orderby' => 'date',
            'order' => 'DESC',
            'post_status'=> 'publish',
            'cat' => $child_category->term_id
        );
        
        $child_posts = new WP_Query($child_post_args);
        while ($child_posts->have_posts()) {
            $child_posts->the_post();
            echo get_the_date();
            echo '&nbsp;';
            $post_type = get_post_type();
            echo 'Type: ';
            echo ucfirst($post_type); 
            echo '&nbsp;';
            echo '<a target="_blank" href="';
            the_permalink();
            echo '">';
            the_title();
            echo '</a>';
            echo '&nbsp;';
            echo wp_trim_words(get_the_excerpt(),20,'...');
            echo '<br />';
        }
        wp_reset_postdata();
        echo '</p></div>';
    }
    
    echo '</p></div>';
}

echo '</div>';
?>
<script>
jQuery(document).ready(function() {
    jQuery(".accordion").accordion({
        collapsible: true,
        clearStyle: true,
        active: false,
    });
});
</script>
  • First, we get only top-level categories using get_categories() with 'parent' => 0
  • For each parent category, we:
    • Display the parent category name as an accordion header ()
    • Show all posts belonging to that parent category
    • Get all child categories of that parent
    • For each child category, display it as a sub-header () with its posts

I'd be more worried about the disk i/o impact of running queries recursively than the CPU. The right way to do this would be to use one query and a walker.

转载请注明原文地址:http://conceptsofalgorithm.com/Algorithm/1748631747a313660.html

最新回复(0)