loop - Custom post type, organized by categories

admin2025-06-05  1

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:

  1. 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.

  1. 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:

  1. 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.

  1. 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
  • I'm not sure what you're expecting it to do if you add the post_type option to get_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
Add a comment  | 

1 Answer 1

Reset to default 0

This is a lot simpler than you might believe, first, I must dispel some misconceptions:

  • post_type is not a parameter of get_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..
转载请注明原文地址:http://conceptsofalgorithm.com/Algorithm/1749061219a316020.html

最新回复(0)