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: http://wordpress.stackexchange.com/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!!

  • 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 Dec 29 '18 at 0:21
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..
  • I appreciate all the time you took on this! To answer some of your questions, I can tell you my mistakes are probably the result of guessing or misinterpreting other information, so I appreciate the time you took to spell things out. That said, I'm still having issues I guess. If I'm reading this right, I should be able to drop the first code box in and at least see my cat names, correct? Because I'm not and I'm not sure what to do to troubleshoot it! – iprobablyhavenoanswers Dec 31 '18 at 16:42
  • It might be cat not category, I can never quite remember which one it is – Tom J Nowell Dec 31 '18 at 16:59

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.