I know there are lots of questions and answers out there for this very thing, but I'm having a hard time putting it all together. The goal is to get a <ul>
with category names as the <li>
, then each <li>
will have a nested <ul>
to list the posts in that category. Something like
Hotels
- Best Western
- Holiday Inn
Restaurants
- Burger King
- McDonald's
What I've tried so far:
- Based on this answer: Sort posts by category name and title
I came up with this code:
<?php
$categories = get_categories(
array (
'post_type' => 'attractions', // does nothing
'orderby' => 'name',
'order' => 'asc'
)
);
foreach ($categories as $category){
echo '<li class="category-list-item">';
echo '<h2 class="bluegreen allcaps">' . $category->name . '</h2>';
echo '<ul class="attractions-list">';
$catPosts = new WP_Query(
array (
'post_type' => 'attractions', //breaks page
'category_name' => $category->slug,
'orderby' => 'title'
)
);
if ( $catPosts->have_posts() ){
while ( $catPost->have_posts() ){
$catPost->the_post(); ?>
<li class="attractions-list-item">
<a target="_blank" href="<?php echo get_field('website_url', $post->ID); ?>"><?php the_title(); ?></a>
</li>
<?php
} //end while
}//end if
echo '</ul>';
echo '</li>';
} //end foreach
wp_reset_postdata();
?>
In the get_categories()
array, adding the post_type
doesn't do anything. Further down, I added it to the WP_Query
array and that breaks the page (basically it will display the first category name alphabetically, then the rest of the page—such as the footer and stuff—doesn't show up). I've tried declaring the post_type
in each place individually and both places at the same time but the results don't change.
- I've also tried forking the code from this answer:
With that, I was able to get everything to display, but the order was all janky and I couldn't figure out how to sort it. Here's what I ended up with from going down that road:
<ul class="category-list">
<?php
$args = array(
'post_type' => 'attractions',
'posts_per_page' => -1,
'orderby' => 'name', //also tried 'slug', which changes the order but still doesn't make sense
'exclude' => '1'
);
$query = new WP_Query($args);
$q = array();
while ( $query->have_posts() ) {
$query->the_post();
$a = '<li class="attractions-list-item">' . get_the_title() .'</li>';
$categories = get_the_category();
foreach ( $categories as $key=>$category ) {
$b = '<li class="categories-list-item"><a class="anchor" name="' . $category->slug . '"></a><h2 class="bluegreen allcaps">' . $category->name . '</h2>';
}
$q[$b][] = $a; // Create an array with the category names and post titles
}
/* Restore original Post Data */
wp_reset_postdata();
foreach ($q as $key=>$values) {
echo $key;
echo '<ul class="attractions-list">';
foreach ($values as $value){
echo $value;
}
echo '</ul>';
}
echo '</li>';
?>
</ul>
In the end, with method one, I'm able to return a complete, alphabetically ordered list of categories but no posts (assuming because I'm having a hard time declaring a post_type
other than the default posts). With method two, I can get all the categories and the posts beneath them, but they're in a weird, nonsensical order and I can't figure out how to sort them. The posts under each category are also in a weird order.
I know this is a crazy long post, but I'm at my wit's end at this point!! Thanks in advance for any help, suggestions or insights!!
I know there are lots of questions and answers out there for this very thing, but I'm having a hard time putting it all together. The goal is to get a <ul>
with category names as the <li>
, then each <li>
will have a nested <ul>
to list the posts in that category. Something like
Hotels
- Best Western
- Holiday Inn
Restaurants
- Burger King
- McDonald's
What I've tried so far:
- Based on this answer: Sort posts by category name and title
I came up with this code:
<?php
$categories = get_categories(
array (
'post_type' => 'attractions', // does nothing
'orderby' => 'name',
'order' => 'asc'
)
);
foreach ($categories as $category){
echo '<li class="category-list-item">';
echo '<h2 class="bluegreen allcaps">' . $category->name . '</h2>';
echo '<ul class="attractions-list">';
$catPosts = new WP_Query(
array (
'post_type' => 'attractions', //breaks page
'category_name' => $category->slug,
'orderby' => 'title'
)
);
if ( $catPosts->have_posts() ){
while ( $catPost->have_posts() ){
$catPost->the_post(); ?>
<li class="attractions-list-item">
<a target="_blank" href="<?php echo get_field('website_url', $post->ID); ?>"><?php the_title(); ?></a>
</li>
<?php
} //end while
}//end if
echo '</ul>';
echo '</li>';
} //end foreach
wp_reset_postdata();
?>
In the get_categories()
array, adding the post_type
doesn't do anything. Further down, I added it to the WP_Query
array and that breaks the page (basically it will display the first category name alphabetically, then the rest of the page—such as the footer and stuff—doesn't show up). I've tried declaring the post_type
in each place individually and both places at the same time but the results don't change.
- I've also tried forking the code from this answer: http://wordpress.stackexchange/a/145960/31545
With that, I was able to get everything to display, but the order was all janky and I couldn't figure out how to sort it. Here's what I ended up with from going down that road:
<ul class="category-list">
<?php
$args = array(
'post_type' => 'attractions',
'posts_per_page' => -1,
'orderby' => 'name', //also tried 'slug', which changes the order but still doesn't make sense
'exclude' => '1'
);
$query = new WP_Query($args);
$q = array();
while ( $query->have_posts() ) {
$query->the_post();
$a = '<li class="attractions-list-item">' . get_the_title() .'</li>';
$categories = get_the_category();
foreach ( $categories as $key=>$category ) {
$b = '<li class="categories-list-item"><a class="anchor" name="' . $category->slug . '"></a><h2 class="bluegreen allcaps">' . $category->name . '</h2>';
}
$q[$b][] = $a; // Create an array with the category names and post titles
}
/* Restore original Post Data */
wp_reset_postdata();
foreach ($q as $key=>$values) {
echo $key;
echo '<ul class="attractions-list">';
foreach ($values as $value){
echo $value;
}
echo '</ul>';
}
echo '</li>';
?>
</ul>
In the end, with method one, I'm able to return a complete, alphabetically ordered list of categories but no posts (assuming because I'm having a hard time declaring a post_type
other than the default posts). With method two, I can get all the categories and the posts beneath them, but they're in a weird, nonsensical order and I can't figure out how to sort them. The posts under each category are also in a weird order.
I know this is a crazy long post, but I'm at my wit's end at this point!! Thanks in advance for any help, suggestions or insights!!
Share Improve this question asked Dec 28, 2018 at 22:46 iprobablyhavenoanswersiprobablyhavenoanswers 431 silver badge8 bronze badges 1 |1 Answer
Reset to default 0This is a lot simpler than you might believe, first, I must dispel some misconceptions:
post_type
is not a parameter ofget_categories
, isn't listed in the documentation, and wouldn't make sense. Categories are terms not posts, they don't have a post type, and this API call retrieves terms, it has no business looking into the posts table- When the page just suddenly abruptly stops, it usually means there was a PHP fatal error. Check your PHP error logs for the reason, and look up debugging in WordPress
- Reusing the post categories is messy, things would be much better if you used a custom taxonomy instead. Additionally it makes the APIs more useful, as you don't have to figure out how many of a term are one post type, and how many are of another.
- APIs such as
get_categories
are higher level helper functions, mostly there for backwards compatibility. As a result they tend to have "strings attached". Use the terms API instead, it works the same for all taxonomies be they tags, categories, or something you registered yourself. - You never checked if what you were given was what you expected! What if no categories had been found! Or it returned a
WP_Error
object? get_template_part
is an amazing function, don't ignore it and write super long files, break it up- There's no escaping in your code example, this is a major security issue. Anybody can insert any HTML they want by putting it in a term title, use
esc_html
etc
So finally, you kind of had the answer to begin with:
grab the terms
for each term
display the terms title
grab the posts of type "attractions" that have that term
for each post
display it
So lets break that apart and fetch the terms:
$terms = get_terms([
'taxonomy' => 'category',
'order' => 'asc',
'orderby' => 'name',
]);
if ( !empty( $terms ) && !is_wp_error( $terms ) ) {
echo '<h3>'.esc_html( $term->name ).'</h3>';
foreach ( $terms as $term ) {
// .. display posts
}
}
Then for your post loop:
$args = [
'post_type' => 'attractions',
'cat' => $term->term_id,
];
$query = new WP_Query( $args );
// etc..
post_type
option toget_categories
, is there a specific reason you chose to reuse the categories taxonomy that came with WP by default rather than creating a new custom taxonomy, e.g.attraction_type
? Or why you're using the category specific APIs rather than the general taxonomy APIs? – Tom J Nowell ♦ Commented Dec 29, 2018 at 0:21