Welcome to the Treehouse Community

Want to collaborate on code errors? Have bugs you need feedback on? Looking for an extra set of eyes on your latest project? Get support with fellow developers, designers, and programmers of all backgrounds and skill levels here with the Treehouse Community! While you're at it, check out some resources Treehouse students have shared here.

Looking to learn something new?

Treehouse offers a seven day free trial for new students. Get access to thousands of hours of content and join thousands of Treehouse students and alumni in the community today.

Start your free trial

WordPress

Displaying content from several custom taxonomies on a single archive page

Hello,

What am I trying to achieve?

Building an archive page to display organised content from several custom taxonomies.

First, I'm giving you the elements I'm working with.

I created custom post types and taxonomies using the CPT UI plugin.

I have 3 custom post types:

  • artist
  • community
  • museum

Then, I have 3 custom taxonomies:

  • communities (attached to the Community post type)
  • museums (attached to the Museum post type)
  • countries (attached to the Artist, Community and Museum post types)

The communities taxonomy has several terms: ex: Art Associations, Art Fairs, Art Festivals… The museums taxonomy has the following terms: Alternative Spaces, Galleries, Museums. The countries taxonomy contains country names of course.

I also created a countries navigation menu that I activated in my theme and set it to display for the countries taxonomy.

I set custom fields to input the information for each custom post using the Advanced Custom Fields plugin.

Now, I'm going to describe as clearly as I can what I'm trying to do.

For each country I want to display the following (in that order):

  1. The Artists profiles.
  2. Then, the Communities profiles separated by terms in ascending order.
  3. Then the Museums profiles, also separated by terms in the same fashion.

Then there is a side menu to allow visitors to jump from one section of the page to another like on the twitter bootstrap site.

What I've done so far.

I've been trying to get this to work for about 3 weeks now but I'm no php master. And even though I tried, I don't think I'm going to find the answer on my own.

First, I created a taxonomy.php file where I tried the regular WordPress loop. This is displaying all the posts for a specific country like I want, but I can't find a way to separate each section/post type and display the terms for the custom taxonomies. Code below.

First the code to call the menu on index.php

<?php if (is_tax('countries')) : ?>
    <nav class="categories-nav tk-proxima-nova-extra-condensed" role="navigation">
        <h1>Countries:</h1> 
        <?php
            if (has_nav_menu('countries_navigation')) :
                wp_nav_menu(array('theme_location' => 'countries_navigation', 'menu_class' => 'nav category-selector'));
            endif;
        ?>
    </nav>
<?php endif ?>

Then the code for the regular WordPress loop in taxonomy.php

<?php if ( have_posts() ) : ?>
    <?php while (have_posts()) : the_post(); ?>
        <article <?php post_class('col-sm-6 col-lg-4'); ?>>
            <div>
                My content using custom fields.
            </div>
        </article>
    <?php endwhile; ?>
<?php endif; ?>

Then I tried something that only displays content for one country, see code below:

<?php 
$terms = get_terms( 'communities', array(
    'orderby'    => 'name',
    'order'      => 'ASC',
    'hide_empty' => 0
) );
?>
<?php
foreach( $terms as $term ) {

    $args = array(
        'post_type' => 'community',
        'countries' => 'denmark',
        'communities' => $term->slug
    );
    $query = new WP_Query( $args );

    if ( $query->have_posts() ) {

        echo'<h2>' . $term->name . '</h2>';

        while ( $query->have_posts() ) : $query->the_post(); ?>

        <article <?php post_class('col-sm-6 col-lg-4'); ?>>
            <div>
                My content using custom fields.
            </div>
        </article>

        <?php endwhile;

        wp_reset_postdata();
    }
} ?>

The last code example is displaying only the terms for Communities and for one country only. Is there someone out there who can tell me what I'm doing wrong?

13 Answers

Andrew Shook
Andrew Shook
31,709 Points

Sebastien, the last bit of code I posted was messed up. I for got to include the line that created a new WP_Query. I've included that fix in the code posted below. Theoretically, this should work and be what you need.

<?php
/*
* taxonomy-country.php
*
* This template is for displaying all artists, communites, and museums associate with 
* a give county.
*
* @author 
*/



/**
 * Will hold the country term object for the custom query used later
 * 
 * (default value: get_queried_object() )
 * 
 * @var int
 * @access public
 */
$country_id = get_queried_object(); 


/**
 * This will hold the slugs of your custom post types. I guessed their slug so please
 * make sure and replace them with the correct values as needed.
 *
 * (default value: array("artist", "community", "museum"))
 * 
 * @var string
 * @access public
 */
$post_types = array("community"=>"Communities", "museum"=>"Museums");

get_header();
?>

<h1 class="page-title"><?php echo $country_id->name ;?></h1>

            <?php 
                $args = array(
                    'post_type' => "artist",
                    'tax_query' => array(
                        array(
                            'taxonomy' => 'country',
                            'terms' => $country_id->term_id,
                        )
                    ),
                );
                //forgot to add this last time
            $query = new WP_Query( $args );
         ?>



            <?php if ( $query->have_posts() ) : ?>
                <h2>Artists</h2>
              <!-- the loop -->
              <?php while ( $query->have_posts() ) : $query->the_post(); ?>

                <?php get_template_part( "templates/content", "artist" ); ?>
                <hr>
              <?php endwhile; ?>
              <!-- end of the loop -->

              <?php //wp_reset_postdata(); ?>

            <?php else:  ?>

            <?php endif; ?>



<?php foreach($post_types as $post_type => $title) : //loops through each post type, creates custom query, and displays it. ?>
    <h2><?php echo $title; ?></h2>
    <?php 
            $terms =  get_terms( strtolower($title), array(
                'orderby'    => 'name',
                'order'      => 'ASC',
                'hide_empty' => 1 //leave this true because it hides empty terms
            ) );

        ?>

        <?php foreach( $terms as $term): ?>
            <?php 
                $args = array(
                    'post_type' => $post_type,
                    'tax_query' => array(
                        "relation" => "AND",
                        array(
                            'taxonomy' => 'country',
                            'terms' => $country_id->term_id,
                        ),
                        array(
                            'taxonomy' => strtolower($title),
                            'terms' => $term->term_id,
                        )
                    ),
                );
                // forgot to add this last time
                $query = new WP_Query( $args );
            ?>

            <?php if ( $query->have_posts() ) : ?>
                <h3><?php echo $term->name; ?></h3>
                <p><?php echo $term->description; ?></p>
              <!-- the loop -->
              <?php while ( $query->have_posts() ) : $query->the_post(); ?>

                <?php 
                        // this get a template named content-slug where slug is the slug of the post type.
                        // so if the slug is artist then the template needs to be content-artist.php 
                        get_template_part( "templates/content", $post_type ); //this get a template named content-slugOfPostType so  ?>
                <hr>
              <?php endwhile; ?>
              <!-- end of the loop -->

              <?php //wp_reset_postdata(); ?>

            <?php else:  ?>

            <?php endif; ?>

        <?php endforeach; ?>

<?php endforeach; ?>

<?php get_footer(); ?>

That's it ! It's working. Thank you again. That's going to be some good training ground for me, trying to fully understand and replicate this. I hope this will help other students as well. Don't hesitate to get in touch if you ever need help with something on the design side of things, that's my expertise, I'll be glad to help if I can. Bye for now.

Andrew Shook
Andrew Shook
31,709 Points

If you need help understanding the code just let me know and I'll try to talk you through it.

Andrew Shook
Andrew Shook
31,709 Points

I did this real quick as an example. It's based on the idea that you want the artist, community, and museum post for each country. Hope this helps, and feel free to let me know if you need it modified.

<?php
/*
* taxonomy-country.php
*
* This template is for displaying all artists, communites, and museums associate with 
* a give county.
*
* @author 
*/



/**
 * Will hold the country term object for the custom query used later
 * 
 * (default value: get_queried_object() )
 * 
 * @var int
 * @access public
 */
$country_id = get_queried_object(); 


/**
 * This will hold the slugs of your custom post types. I guessed their slug so please
 * make sure and replace them with the correct values as needed.
 *
 * (default value: array("artist", "community", "museum"))
 * 
 * @var string
 * @access public
 */
$post_types = array("artist"=>"Artists", "community"=>"Communities", "museum"=>"Museums");

get_header();
?>

<h1 class="page-title"><?php echo $country_id->name ;?></h1>
<?php foreach($post_types as $post_type => $title) : //loops through each post type, creates custom query, and displays it. ?>

    <?php 
        $args = array(
            'post_type' => $post_type,
            'tax_query' => array(
                array(
                    'taxonomy' => 'country',
                    'terms' => $country_id->term_id,
                )
            ),
        );
        $query = new WP_Query( $args );
    ?>

    <?php if ( $query->have_posts() ) : ?>
        <h2><?php echo $title; ?></h2>
      <!-- the loop -->
      <?php while ( $query->have_posts() ) : $query->the_post(); ?>
            <h3><?php the_title(); ?></h3>
            <?php the_excerpt(); ?>
            <a href="<?php the_permalink(); ?>" class="btn btn-primary">Read More</a>
            <hr>
      <?php endwhile; ?>
      <!-- end of the loop -->

      <?php //wp_reset_postdata(); ?>

    <?php else:  ?>

    <?php endif; ?>


<?php endforeach; ?>

<?php get_footer(); ?>
Andrew Shook
Andrew Shook
31,709 Points

Just saw your code and noticed you have custom template parts for each post type so I reworked the above code to accommodate that:

<?php
/*
* taxonomy-country.php
*
* This template is for displaying all artists, communites, and museums associate with 
* a give county.
*
* @author 
*/



/**
 * Will hold the country term object for the custom query used later
 * 
 * (default value: get_queried_object() )
 * 
 * @var int
 * @access public
 */
$country_id = get_queried_object(); 


/**
 * This will hold the slugs of your custom post types. I guessed their slug so please
 * make sure and replace them with the correct values as needed.
 *
 * (default value: array("artist", "community", "museum"))
 * 
 * @var string
 * @access public
 */
$post_types = array("artist"=>"Artists", "community"=>"Communities", "museum"=>"Museums");

get_header();
?>

<h1 class="page-title"><?php echo $country_id->name ;?></h1>
<?php foreach($post_types as $post_type => $title) : //loops through each post type, creates custom query, and displays it. ?>

    <?php 
        $args = array(
            'post_type' => $post_type,
            'tax_query' => array(
                array(
                    'taxonomy' => 'country',
                    'terms' => $country_id->term_id,
                )
            ),
        );
        $query = new WP_Query( $args );
    ?>

    <?php if ( $query->have_posts() ) : ?>
        <h2><?php echo $title; ?></h2>
      <!-- the loop -->
      <?php while ( $query->have_posts() ) : $query->the_post(); ?>

        <?php 
                // this gets a template named content-slug where slug is the slug of the post type.
                // so if the slug is artist then the template needs to be content-artist.php 
                get_template_part( "templates/content", get_post_type() ); //this get a template named content-slugOfPostType so  ?>
        <hr>
      <?php endwhile; ?>
      <!-- end of the loop -->

      <?php //wp_reset_postdata(); ?>

    <?php else:  ?>

    <?php endif; ?>


<?php endforeach; ?>

<?php get_footer(); ?>
Andrew Shook
Andrew Shook
31,709 Points

Sebastien Thiroux, sorry for taking so long to get back to you. In the last piece of code I posted there is a syntax error on line 89. I forgot to put a comma between the relation argument and the first taxonomy argument:

'tax_query' => array(
                        "relation" => "AND"
                        array(
                            'taxonomy' => 'country',
                            'terms' => $country_id->term_id,
                        ),
                        array(
                            'taxonomy' => strtolower($title),
                            'terms' => $term->term_id,
                        )
                    ),

If you put a comma after the "AND" the script should run. In the future, a white screen on a php script usually means there is a syntax error somewhere. Most of the time it's because you forgot a semi-colon, closing ")", or a comma. To help debug this you can either turn on error reporting for wordpress, or use an online php validator. Here is the debugged code:

<?php
/*
* taxonomy-country.php
*
* This template is for displaying all artists, communites, and museums associate with 
* a give county.
*
* @author 
*/



/**
 * Will hold the country term object for the custom query used later
 * 
 * (default value: get_queried_object() )
 * 
 * @var int
 * @access public
 */
$country_id = get_queried_object(); 


/**
 * This will hold the slugs of your custom post types. I guessed their slug so please
 * make sure and replace them with the correct values as needed.
 *
 * (default value: array("artist", "community", "museum"))
 * 
 * @var string
 * @access public
 */
$post_types = array("community"=>"Communities", "museum"=>"Museums");

get_header();
?>

<h1 class="page-title"><?php echo $country_id->name ;?></h1>

            <?php 
                $args = array(
                    'post_type' => "artist",
                    'tax_query' => array(
                        array(
                            'taxonomy' => 'country',
                            'terms' => $country_id->term_id,
                        )
                    ),
                );
            ?>




            <?php if ( $query->have_posts() ) : ?>
                <h2>Artists</h2>
              <!-- the loop -->
              <?php while ( $query->have_posts() ) : $query->the_post(); ?>

                <?php get_template_part( "templates/content", "artist" ); ?>
                <hr>
              <?php endwhile; ?>
              <!-- end of the loop -->

              <?php //wp_reset_postdata(); ?>

            <?php else:  ?>

            <?php endif; ?>



<?php foreach($post_types as $post_type => $title) : //loops through each post type, creates custom query, and displays it. ?>
    <h2><?php echo $title; ?></h2>
    <?php 
            $terms =  get_terms( strtolower($title), array(
                'orderby'    => 'name',
                'order'      => 'ASC',
                'hide_empty' => 1 //leave this true because it hides empty terms
            ) );

        ?>

        <?php foreach( $terms as $term): ?>
            <?php 
                $args = array(
                    'post_type' => $post_type,
                    'tax_query' => array(
                        "relation" => "AND",
                        array(
                            'taxonomy' => 'country',
                            'terms' => $country_id->term_id,
                        ),
                        array(
                            'taxonomy' => strtolower($title),
                            'terms' => $term->term_id,
                        )
                    ),
                );
            ?>

            <?php if ( $query->have_posts() ) : ?>
                <h3><?php echo $term->name; ?></h3>
                <p><?php echo $term->description; ?></p>
              <!-- the loop -->
              <?php while ( $query->have_posts() ) : $query->the_post(); ?>

                <?php 
                        // this get a template named content-slug where slug is the slug of the post type.
                        // so if the slug is artist then the template needs to be content-artist.php 
                        get_template_part( "templates/content", $post_type ); //this get a template named content-slugOfPostType so  ?>
                <hr>
              <?php endwhile; ?>
              <!-- end of the loop -->

              <?php //wp_reset_postdata(); ?>

            <?php else:  ?>

            <?php endif; ?>

        <?php endforeach; ?>

<?php endforeach; ?>

<?php get_footer(); ?>

Hi Andrew, sorry for the late reply, I didn't receive an email after you sent this. The country name is displaying then the rest of the page is blank so I'm going to try to see if there is a syntax error. Thank you for taking the time to help. I'll have some time to work on that in a couple of days. I'll let you know how it goes.

Zac Gordon
STAFF
Zac Gordon
Treehouse Guest Teacher

Hi, so did that resolve it for you? You might try a resource like this http://generatewp.com/wp_query/ for help creating each individual query. I would say practice getting each one individually first working as template includes and then we can get everything working together.

Hi, it didn't solve it. So I'll try what you are suggesting and I'll post an answer when I find one.

Andrew Shook
Andrew Shook
31,709 Points

Sebastien, just to clarify, you want a country taxonomy page ( say Denmark) to display all the artists custom posts, communities custom post, and museums custom posts associated with that country?

Hi Andrew,

Thanks for replying. That is exactly what I'm trying to do, and Denmark is actually one of the countries. You must have seen one of my questions around the web.

I've been working on templates using query_posts after I send my question today and it's almost working except for the Artist part that is not working because it doesn't have custom taxonomies associated with it, so the country restriction doesn't work. A solution would be to create a dummy taxonomy just to add the fetch terms like the other loops. This is not a very elegant solution. But the worse part is that I need to replicate a template for each country and those share the same code except for the country name. I also read several times that query_posts was a solution to avoid. So it's still not what I'm looking for.

See what I came up with below:

<div class="container">
<?php query_posts('post_type=artist'); ?>
<?php if ( have_posts() ) : ?>
<div class="row">
    <h2>Artists</h2>
    <?php while (have_posts()) : the_post(); ?>

        <?php get_template_part('templates/content', 'artists'); ?>

    <?php endwhile; ?>
<?php endif; ?>
</div>
<?php query_posts('post_type=community'); ?>
<?php 
//start by fetching the terms for the communities taxonomy
$terms = get_terms( 'communities', array(
    'orderby'    => 'name',
    'order'      => 'ASC',
    'hide_empty' => 0
) );
?>
<?php
// run a query for each community
foreach( $terms as $term ) {

    // Define the query
    $args = array(
        'countries' => 'denmark',
        'communities' => $term->slug
    );
    $query = new WP_Query( $args );

    if ( $query->have_posts() ) {

        echo'<div class="row"><h2>' . $term->name . '</h2>';

        while ( $query->have_posts() ) : $query->the_post(); ?>

            <?php get_template_part('templates/content', 'communities'); ?>

        <?php endwhile;
        echo "</div>";

        // restore orginal query
        wp_reset_postdata();
    }
} ?>

<?php query_posts('post_type=museum'); ?>
<?php 

//start by fetching the terms for the museums taxonomy
$terms = get_terms( 'museums', array(
    'orderby'    => 'name',
    'order'      => 'ASC',
    'hide_empty' => 0
) );
?>
<?php
// run a query for each museum
foreach( $terms as $term ) {

    // Define the query
    $args = array(
        'countries' => 'denmark',
        'museums' => $term->slug
    );
    $query = new WP_Query( $args );

    if ( $query->have_posts() ) {

        echo'<div class="row"><h2>' . $term->name . '</h2>';

        while ( $query->have_posts() ) : $query->the_post(); ?>

            <?php get_template_part('templates/content', 'museums'); ?>

        <?php endwhile;
        echo "</div>";

        // restore orginal query
        wp_reset_postdata();
    }
} ?>
</div>

Do you have a solution? a suggestion?

Thank you so much for your help. Your code is so simple, and thanks also for the comments. This is really showing me that I need to find time to learn php.

My page is almost complete. The only elements I'm missing are the terms to separate the different types of Communities and the different types of Museums. That's why I had 3 loops in my code, so I could get the terms each time around. Is there a way to do that in your template?

Andrew Shook
Andrew Shook
31,709 Points

Ok so you want all three post types associated with the given country, plus museums and communities ordered and separated by their own taxonomies?

Exactly! Right now I'm reading your code and I tried to add other arrays in the tax_query array, but every time I break the code. I'm pretty sure it's possible though.

Andrew Shook
Andrew Shook
31,709 Points

Ok, challenge accepted! give me a minutes and I'll whip something up.

Andrew Shook
Andrew Shook
31,709 Points

Ok so I couldn't find a way in my limited time to get it to run in one loop so I made two. One is for the artists and has no terms, and the other is for communities and museums which are separated by name a sorted by terms.

<?php
/*
* taxonomy-country.php
*
* This template is for displaying all artists, communites, and museums associate with 
* a give county.
*
* @author 
*/



/**
 * Will hold the country term object for the custom query used later
 * 
 * (default value: get_queried_object() )
 * 
 * @var int
 * @access public
 */
$country_id = get_queried_object(); 


/**
 * This will hold the slugs of your custom post types. I guessed their slug so please
 * make sure and replace them with the correct values as needed.
 *
 * (default value: array("artist", "community", "museum"))
 * 
 * @var string
 * @access public
 */
$post_types = array("community"=>"Communities", "museum"=>"Museums");

get_header();
?>

<h1 class="page-title"><?php echo $country_id->name ;?></h1>

            <?php 
                $args = array(
                    'post_type' => "artist",
                    'tax_query' => array(
                        array(
                            'taxonomy' => 'country',
                            'terms' => $country_id->term_id,
                        )
                    ),
                );
            ?>




            <?php if ( $query->have_posts() ) : ?>
                <h2>Artists</h2>
              <!-- the loop -->
              <?php while ( $query->have_posts() ) : $query->the_post(); ?>

                <?php get_template_part( "templates/content", "artist" ); ?>
                <hr>
              <?php endwhile; ?>
              <!-- end of the loop -->

              <?php //wp_reset_postdata(); ?>

            <?php else:  ?>

            <?php endif; ?>



<?php foreach($post_types as $post_type => $title) : //loops through each post type, creates custom query, and displays it. ?>
    <h2><?php echo $title; ?></h2>
    <?php 
            $terms =  get_terms( strtolower($title), array(
                'orderby'    => 'name',
                'order'      => 'ASC',
                'hide_empty' => 1 //leave this true because it hides empty terms
            ) );

        ?>

        <?php foreach( $terms as $term): ?>
            <?php 
                $args = array(
                    'post_type' => $post_type,
                    'tax_query' => array(
                        "relation" => "AND"
                        array(
                            'taxonomy' => 'country',
                            'terms' => $country_id->term_id,
                        ),
                        array(
                            'taxonomy' => strtolower($title),
                            'terms' => $term->term_id,
                        )
                    ),
                );
            ?>

            <?php if ( $query->have_posts() ) : ?>
                <h3><?php echo $term->name; ?></h3>
                <p><?php echo $term->description; ?></p>
              <!-- the loop -->
              <?php while ( $query->have_posts() ) : $query->the_post(); ?>

                <?php 
                        // this get a template named content-slug where slug is the slug of the post type.
                        // so if the slug is artist then the template needs to be content-artist.php 
                        get_template_part( "templates/content", $post_type ); //this get a template named content-slugOfPostType so  ?>
                <hr>
              <?php endwhile; ?>
              <!-- end of the loop -->

              <?php //wp_reset_postdata(); ?>

            <?php else:  ?>

            <?php endif; ?>

        <?php endforeach; ?>

<?php endforeach; ?>

<?php get_footer(); ?>

I think this should about be it. Good Luck!

Hi Andrew, Sorry for getting back to you so late. Thank you for trying, the last code you sent me doesn't work. So I'm trying to understand why the first one was working and not this one. Maybe it'll give me some insight in what I should do to make this work.

Andrew Shook
Andrew Shook
31,709 Points

What's not working? Is it throwing and error or white screening? Or is it just not formatting the info correctly?

I get a white screen. I have my header, then the rest of the page is blank. The footer disappears as well.

Zac Gordon
STAFF
Zac Gordon
Treehouse Guest Teacher

Hi, I wanted to jump in here and see if you ever resolved this situation. I didn't follow all of the back and forth completely, but it seems like this can be handled by three separate custom queries.

Hi Zac, thank you for stepping in. I still didn't find a solution yet, which is really frustrating. I've been watching probably all your courses about WordPress, but i believe there is nothing covering such a topic. So I'm planning on taking the php classes when I will have time, in hope that I will learn more about loops and custom queries.

So far, Andrew helped me and the first code suggestion he sent me was working, only it was solving half of my problem. He then sent me another bit of code (the last one in the thread) that he thought would solve my problem, but it doesn't work. I get the header for the page, then the rest of the page is blank.

I'm going to work on it today, so I'll try to focus on what you said : three separate custom queries.

In the meantime, if you have a code exemple of what it should look like. I'm a visual learner, so that would definitely help me.

Do you want me to make a summary of what I'm trying to achieve?