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 ' ';
$post_type = get_post_type();
echo 'Type: ';
echo ucfirst($post_type);
echo ' ';
echo '<a target="_blank" href="';
the_permalink();
echo '">';
the_title();
echo '</a>';
echo ' ';
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 ' ';
$post_type = get_post_type();
echo 'Type: ';
echo ucfirst($post_type);
echo ' ';
echo '<a target="_blank" href="';
the_permalink();
echo '">';
the_title();
echo '</a>';
echo ' ';
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>
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:
<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 ' ';
$post_type = get_post_type();
echo 'Type: ';
echo ucfirst($post_type);
echo ' ';
echo '<a target="_blank" href="';
the_permalink();
echo '">';
the_title();
echo '</a>';
echo ' ';
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 ' ';
$post_type = get_post_type();
echo 'Type: ';
echo ucfirst($post_type);
echo ' ';
echo '<a target="_blank" href="';
the_permalink();
echo '">';
the_title();
echo '</a>';
echo ' ';
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>
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.