How to make Complex WordPress IA Part 1

I’ve been heads down on a project at work that has had a number of reasonably interesting challenges, so I’m writing a series of posts to capture not just what I did but how I actually did it.

One of the major knocks on WordPress is that, because it was originally designed as blogging software, making comparatively complex information structures is more difficult than it could be with a different system. Still, there may be reasons a project needs to be both WordPress and have a complex architecture. As a developer, our overarching goal is to bend the software to the human needs, not try to bend people to the limits of software.

Without getting too in the weeds about how WP treats content, let’s show a hypothetical case instead.

Say we’re building a site for one of our favorite musicians.  Obviously, they want people to discover and understand their music better.  So we decide that the key content item on this site will be each Song they choose to put on the site.  We will need to have a custom post type called Songs. Each Song is part of an album, and each album has a genre.

Song -> Album

Song -> Genre.

Believe it or not, this is can be pretty complex to display.

Let’s say we wanted to show all of the Songs in a Catalog.  Then we want to sort the Catalog by Genre. Additionally, we’ll want to have a page that lists all our Albums (call it “Discography”) and each Album gets it’s own page. On each album page,  there’s a menu of all the albums.

Still following?

Here’s the basic IA structure we’re going to make.

Catalog -> Lists all Songs, Sorts by Genre (each Genre is it’s own url).

Genre -> Lists just the Songs in this Genre

Discography (parent) -> Lists all Albums (child)

Album (child) -> has menu listing all Albums (siblings).

Default WordPress IA has a really difficult time making this happen. The default WordPress IA can be summed up as: We make Menus out of Pages, and we display Posts on a Blog Page. It is not a coincidence that the templates WP looks for are home.php and index.php.  In our example we have posts, pages, parents, children, and if you’re not careful the logic could end up looking like spaghetti.

WP Ideal Site Map
This is the Ideal WP Information Architecture. You have 3 basic types of content, all related by built in functionality. 

Let’s start with giving our custom post type, Songs, a custom taxonomy.  I use the plugin Custom Post Type UI plugin, but the code to do it yourself is pretty straightforward.

Here’s the custom post type code:

add_action( 'init', 'create_posttype' );
function create_posttype() {
register_post_type( 'songs',
'labels' => array(
'name' => __( 'Songs' ),
'singular_name' => __( 'Song' )
'public' => true,
'has_archive' => true,
'rewrite' => array('slug' => 'songs'),

and here’s the custom taxonomy code:

function genre_init() {
// create a new taxonomy
'label' => __( 'Genre' ),
'rewrite' => array( 'slug' => 'genre' ),
add_action( 'init', 'genre_init' );

Now we’re cooking. Note this line especially:

 "has_archive => 'true'.

This allows us to create an archive template for songs, and tells WordPress to look for an archive template.

For this illustration we’re not going to create custom fields for each Song, but certainly we’d want to do things like:

  • list any awards this song won or was nominated for
  • list who produced, mixed, wrote, or performed on the song
  • show the lyrics
  • insert a soundcloud module so a user can listen to the song right there
  • insert a download link

and we can create all of those fields either on our own, or, even better, with the Advanced Custom Fields Pro plugin. There’s a free version that is excellent but A) I encourage you to support them, B) the pro version is even more useful and C) it’s a steal at the price.

Now we can make an archive template for all of the Songs, that is, our Catalog. We’ll make a new archive template,  which we’ll name


Like all WP templates we’ll start with get_header() and we’ll close the document with get_footer();. If you’re completely new to WP,  these tags help you repeat the head and closing parts of the page, which saves your typing and ensures that the right stylesheets and scripts load on every page.

Problem 1: how do we get all of the Songs?  In this example we’re going to create an unordered list (<ul></ul>) and each list item (<li></li>) will show a Song title, wrapped in a link (<a></a>) back to that song’s unique page.  This is the simple part.

WordPress queries the database using a function called, you guessed it, WP_query.

And this function accepts arguments – that is, we can tell WP_query what to, well, query the database for.

The way we do it is like so (inside a php tag):

// Define custom query parameters
$custom_query_args = array(
'post_type' => 'songs',

The key is we’re telling WordPress to only query the post type we’re after. There’s about a dozen different parameters we can configure with these arguments, and you should bookmark this link in the WordPress Codex. (For WP, the answer is almost always in the Codex).  Note that we are *not* going to use the ‘posts_per_page’ argument here. This is so we can use virtually the exact same code later, for our genre pages.

One thing you might not find is how to paginate these. This took me a little digging, but is actually quite simple.  We can write 1 line of php that will insert this parameter into our arguments, which will then be passed on to the query.

// Get current page and append to custom query parameters array
$custom_query_args['paged'] = get_query_var( 'paged' ) ? get_query_var( 'paged' ) : 1;

However, this only gets us halfway there.  We then must add additional code that sets the query object back to null.  The reason is simple – without this code,  WP will use the default query object.  Instead, we’re telling it to use our object, then null, and then finally reset everything so we don’t break the site.

Here’s that code:

// Pagination fix
$temp_query = $wp_query;
$wp_query = NULL;
$wp_query = $custom_query;
//then later, after we end the loop wp_reset_postdata(); // Reset main query object $wp_query = NULL; $wp_query = $temp_query;

and here is all of the code, together, so far for our




Voila!  You now have all of the Songs on an archive template.  This basic code will also help us make the Genre pages,  which will display all of the Songs in that Genre.

We’re right at about 900 words, a few php concepts, and lots to grok about WordPress IA.  In the next part of this series, we’ll tackle problem 2: How to let a user sort these songs by Genre from this template.