In this tutorial we are going to be looking at a nice way of displaying a custom post type(s) post split up into different taxonomy terms.
For this example I’ve set up a custom post type of “films” and we are going to group these by a taxonomy of “genres”. It is these genres that will become the bootstrap tabs, and the tab content will be the films that are under these particular genres.
Here is what we are trying to achieve:

 

Setting up the Post Type and Taxonomy

First things first, we need to create the custom post type of films. The easiest way to do this is to install the excellent Custom Post Type UI.
Use the settings below:

Then we need to set up the taxonomy of genres so that we can group all of our films into their correct genre. Using Custom Post Type UI, create the taxonomy using the settings below:

Make sure that you set hierarchical to be true, as this will ensure the taxonomy uses category style select boxes rather than tags.
The next job is to create some films and categorise them into their correct genres. I’ve just added a few test films so I can show you the next process.
For simplicity, I am going to create a page in WordPress called “films” and then make a page override of this page where we will put our code. The way to do this is to create a file called page-films.php in your theme folder, and then WordPress will use this file to show the films page. This way, we can add the code that we need and only have it show up when you go to the film page of the site.
The page doesn’t have to be called “films”, but make sure that the slug of the page matches the second part of the page template. So if the page’s slug is “movies”, the template file will be called page-movies.php.

Creating the Tabs

If you visit the Bootstrap site and visit the section on tabs, we can grab the markup that we are going to need. As long as you’ve got all the Bootstrap javascript files loaded (especially tabs.js and transitions.js), the tabs you’ve just pasted into your page should work fine.
What we want to do though, is show our genres names in the tabs. To do this we are going to have to use the WordPress function get_terms. This function basically just returns all the terms with posts that are related to a taxonomy. So, we need to use get_terms to return information about all the genres we have.

<?php $film_genres = get_terms('genres'); ?>

In this example, I’ve set get_terms against the variable $film_genres, but you can use whatever you like.
Next, we need to do a foreach loop over this variable in order to return all the genres we’ve added. We want to do this over the nav-tabs unordered list like so:

<ul class="nav nav-tabs nav-justified">
  <li class="active">
    <a data-toggle="tab" href="#all">All</a>
  </li>
  <?php foreach($film_genres as $film_genre) { ?>
    <li>
      <a href="#<?php echo $film_genre->slug ?>" data-toggle="tab"><?php echo $film_genre->name ?></a>
    </li>
  <? } ?>
</ul>

 
 
Be sure to set an “All” li before you foreach, as we will want the first tab to default to displaying all the films, and then we filter by genre using the other tabs.

The Tab Content

We then want to set the tab content for the all films tab, and we do this using a wp_query over the post type of films like so:

<div class="tab-pane active" id="all">
  <?php
  $args = array(
    'post_type' => 'films',
    'posts_per_page' -1,
    'orderby' => 'title',
    'order' => 'ASC'
  );
  $all_films = new WP_Query( $args );
  ?>
  <?php if ( $all_films->have_posts() ) : // make sure we have films to show before doing anything?>
  <div class="table-responsive">
    <table class="table">
      <?php while ( $all_films->have_posts() ) : $all_films->the_post(); ?>
      <tr>
        <td><?php the_post_thumbnail() ?></td>
        <td><h3><?php the_title() ?></h3</td>
        <td>
          <p class="lead"><?php the_excerpt() ?></p>
          <a href="<?php the_permalink() ?>" class="btn btn-primary">Read more</a>
        </td>
      </tr>
      <?php endwhile; ?>
      <?php wp_reset_query() ?>
    </table>
  </div>
  <?php endif; ?>
</div><!-- all films tab pane -->

 
Make sure you set the post type key value to the name of your post type (if you aren’t using films) ie ‘post_type’ => ‘your post type here’. We are then returning all the posts in the films post type and ordering by title, ascending.
We then run a normal WordPress loop over this wp_query object and return all the film posts.
For this example, I’ve chosen to output a bootstrap table within each tab pane, but you can output your data whenever you like. The thing to remember, is that by using a wp_query loop we can use all the great WordPress functions such as the_title, the_excerpt, the_post_thumbnail etc.
At the end of this loop don’t forget to run wp_reset_query, as this will avoid any possible problems we may have if we are going to be running any other loops on this page.
After we’ve sorted out the tab content showing all films, we then want to be able to show all the films that have been grouped into genres. We want this to dynamic, so that if we choose to add any more genres in the future, we don’t need to code in other wp_quires for each of these. To achieve this, we will need to run another foreach loop over the next tab-content div and within this, we will run a wp_query with some taxonomy parameters in it.

<?php foreach($film_genres as $film_genre) { ?>
  <div class="tab-pane" id="<?php echo $film_genre->slug ?>">
    <?php
    $args = array(
      'post_type' => 'films',
      'posts_per_page' -1,
      'orderby' => 'title',
      'order' => 'ASC',
      'tax_query' => array(
        array(
          'taxonomy' => 'genres',
          'field' => 'slug',
          'terms' => $film_genre->slug
        )
      )
    );
    $films = new WP_Query( $args );
    ?>
    <?php if ( $films->have_posts() ) : // make sure we have films to show before doing anything?>
    <table class="table">
      <?php while ( $films->have_posts() ) : $films->the_post(); ?>
      <tr>
        <td><?php the_post_thumbnail() ?></td>
        <td><h3><?php the_title() ?></h3</td>
        <td>
          <p class="lead"><?php the_excerpt() ?></p>
          <a href="<?php the_permalink() ?>" class="btn btn-primary">Read more</a>
        </td>
      </tr>
      <?php endwhile; ?>
      <?php wp_reset_query() ?>
    </table>
    <?php endif; ?>
  </div>
<? }  ?>

 
We wrap the same foreach that we used before around the tab-pane div. We need to do this so that we can get access to values from the $film_genre object that we’ve set, as we will need to use the slug value to pass into our wp_query, and also to set the tab-pane id.
Once we’ve created the wp_query we use the same WordPress loop as on the “all” films tab-pane, remembering to change the wp_query variable to something unique like $film_genres.
And that really is about it. The Bootstrap tabs should work perfectly, and when you add a new genre with films attached to it, that should appear as a new tab.
Here is the complete code:

<section class="films-tabs">
  <?php $film_genres = get_terms('genres'); // get all the genres ?>
  <!-- Nav tabs -->
  <ul class="nav nav-tabs nav-justified">
    <li class="active">
      <a data-toggle="tab" href="#all">All</a>
    </li>
    <?php foreach($film_genres as $film_genre) { ?>
      <li>
        <a href="#<?php echo $film_genre->slug ?>" data-toggle="tab"><?php echo $film_genre->name ?></a>
      </li>
    <? } ?>
  </ul>
  <!-- Tab panes -->
  <div class="tab-content">
    <div class="tab-pane active" id="all">
      <?php
      $args = array(
        'post_type' => 'films',
        'posts_per_page' -1,
        'orderby' => 'title',
        'order' => 'ASC'
      );
      $all_films = new WP_Query( $args );
      ?>
      <?php if ( $all_films->have_posts() ) : // make sure we have films to show before doing anything?>
      <div class="table-responsive">
        <table class="table">
          <?php while ( $all_films->have_posts() ) : $all_films->the_post(); ?>
          <tr>
            <td><?php the_post_thumbnail() ?></td>
            <td><h3><?php the_title() ?></h3</td>
            <td>
              <p class="lead"><?php the_excerpt() ?></p>
              <a href="<?php the_permalink() ?>" class="btn btn-primary">Read more</a>
            </td>
          </tr>
          <?php endwhile; ?>
          <?php wp_reset_query() ?>
        </table>
      </div>
      <?php endif; ?>
    </div><!-- all films tab pane -->
    <?php foreach($film_genres as $film_genre) { ?>
      <div class="tab-pane" id="<?php echo $film_genre->slug ?>">
        <?php
        $args = array(
          'post_type' => 'films',
          'posts_per_page' -1,
          'orderby' => 'title',
          'order' => 'ASC',
          'tax_query' => array(
            array(
              'taxonomy' => 'genres',
              'field' => 'slug',
              'terms' => $film_genre->slug
            )
          )
        );
        $films = new WP_Query( $args );
        ?>
        <?php if ( $films->have_posts() ) : // make sure we have films to show before doing anything?>
        <table class="table">
          <?php while ( $films->have_posts() ) : $films->the_post(); ?>
          <tr>
            <td><?php the_post_thumbnail() ?></td>
            <td><h3><?php the_title() ?></h3</td>
            <td>
              <p class="lead"><?php the_excerpt() ?></p>
              <a href="<?php the_permalink() ?>" class="btn btn-primary">Read more</a>
            </td>
          </tr>
          <?php endwhile; ?>
          <?php wp_reset_query() ?>
        </table>
        <?php endif; ?>
      </div>
    <? }  ?>
  </div><!-- tab-content -->
</section><!-- film-tabs -->