$conf, $runtime; function_exists('chdir') AND chdir(APP_PATH); $r = 'mysql' == $conf['cache']['type'] ? website_set('runtime', $runtime) : cache_set('runtime', $runtime); } function runtime_truncate() { global $conf; 'mysql' == $conf['cache']['type'] ? website_set('runtime', '') : cache_delete('runtime'); } register_shutdown_function('runtime_save'); ?>functions - Output parentchild categories and posts in that parentchild hierarchy|Programmer puzzle solving
最新消息:Welcome to the puzzle paradise for programmers! Here, a well-designed puzzle awaits you. From code logic puzzles to algorithmic challenges, each level is closely centered on the programmer's expertise and skills. Whether you're a novice programmer or an experienced tech guru, you'll find your own challenges on this site. In the process of solving puzzles, you can not only exercise your thinking skills, but also deepen your understanding and application of programming knowledge. Come to start this puzzle journey full of wisdom and challenges, with many programmers to compete with each other and show your programming wisdom! Translated with DeepL.com (free version)

functions - Output parentchild categories and posts in that parentchild hierarchy

matteradmin6PV0评论

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.

Post a comment

comment list (0)

  1. No comments so far