Display Custom Post Types Specific to Two Custom Taxonomy in WordPress

Recently I have worked on a tour management website project where the client have purchased a tour theme and it required a few customizations. The theme has all the things related to tour management like tour package display, wishlist, user account management, online/offline payment management and many more. But the client required one more feature which is for destinations. He wanted to display destinations as per locations/states & destination category, which will only have details of various tour spots.

So here is the challenge.

Display Location Taxonomy
Display Location Taxonomy

I have to create a custom post type type called “destinations” (pretty simple) and two custom taxonomies locations & destination category associate with it. The harder part, to display  destination categories (eg. Beaches, Hill Stations) taxonomy terms after a visitor click on a single location. And the hardest part, after clicking on a destination category it should display an archive page where it shows posts only associated with the location & destination category and finally to the destination CPT post.

Destination(Page from Menu) -> Locations (Custom Taxonomy) -> Destination Category (Custom Taxonomy) -> Destination Archive Page (Based on visits) -> Destination (The CPT post)

I will do this in 2 parts of coding, where in first part I’ll implement the custom URL for CPT. In second part I’ll create the pages within our flow to display contents as expected.

Part 1

URL for the destination post is like: http://www.mytourexample.com/{CPT}/{custom-taxonomy-1}/{custom-taxonomy-2}/{CPT-post-slug}/

So without talking more, lets get into the coding part.

At first I have registered a new CPT.

Here few things we have to remember while registering custom post type. Change the has_archive  parameter to post type slug name and rewrite slug parameter to custom_post_type_slug/%taxonomy-1%/%taxonomy-2%.

By This we will have custom post type url as http://www.mytourexample.com/{CPT}/{custom-taxonomy-1}/{custom-taxonomy-2}/{CPT-post-slug}/, but before that we need to register our custom taxonomies and need to apply few filters.

Now after registering taxonomies we have to apply few filters to modify our CPT post URL as we want and have mentioned previously while registering CPT.

This post_type_link filter changes the permalink for the post. For more explanation see this stackoverflow answer, which I followed to get this done.

After doing all this codes we can now have URL of Nathula Pass (destination post) which is in Sikkim (location taxonomy term) and a hill station (destination category taxonomy term) in category, like http://www.mytourexample.com/destinations/sikkim/hill-station/nathula-pass/

Here first part of our coding ends.

Part 2

After getting our post URL right, we will focus on our flow pages. Here is user flow for destinations.

Destination(Page from Menu) -> Locations (Custom Taxonomy) -> Destination Category (Custom Taxonomy) -> Destination Archive Page (Based on visits) -> Destination (The CPT post)

First 2 is the same, which will display locations if someone click on destination from main navigation menu of the site. It can be done easily so I’m skipping that here. Then comes destination category custom taxonomy page, which will show destination categories based on the location visitor clicked on.

This is the code of my location taxonomy template page which is taxonomy-destination_location.php. By get_queried_object()  function I am fetching the object of current taxonomy term. Then querying by  the slug of this term I’m fetching the array of IDs of those posts, which are associated with this term and assigned it to $destination_post_IDs.

Now we get the list of posts which are associated with our location taxonomy term. Then we will look for the destination category taxonomy terms which are associated with these posts, by  wp_get_object_terms() and store them in  $destination_category variable. Then I run a loop to get each term name to display. You can follow this tutorial for more in-depth explanation of it.

Here now pay attention to the link of the anchor tag of the term name. It is the link of the page where I’ll list all my destination posts which are only associated with this 2 custom taxonomy term. It will be post archive page.

Normal archive page of post will display all the posts, but here I will use taxonomy query to specify my posts requirement through URL. And the link which I used on term name anchor is the pretty URL of my taxonomy query. Follow this tutorial on advanced taxonomy queries with pretty URLs, which I implemented here below.

I can get destination posts associated with bengal and beaches by this URL.

But it will not look good as a URL within the flow of visitor looking for places to travel. So I make it look like this.

To do this I have to tell WordPress about the URL structure, and how it should handle requests by this URL structure. For this we need to add rewrite rules as mentioned below.

After adding this rewrite rules now I can get only those destinations which are in bengal and beach in category by visiting the pretty URL mentioned above. Then we can design our archive-mt_destination.php file as per our requirement.

You can purchase the theme from here if you have tour management project. I liked all their modules, designs and page builder elements. It is very easy to create custom elements in their page builders.

So this is it. Here I wrap up my coding.


  1. Hi Pradip,

    Thanks for your tutorial and it was of great help as I was struggling with it.
    Just one question I have if you can answer this:

    1- I have 2 custom taxonomies, say while creating custom post type I don’t select any of the one like I have services and procedures taxonomies so while creating a post I select only 1 of them it still shows the one i have not selected like this: %procedures% and then this page gives error.

    Let me know if you can help.



    1. Hey Wajiha,

      I’m glad that this tutorial helped you by any means. 🙂

      And for your question, this will not work if you don’t choose 2 taxonomies while adding a new post, because while registering post type we have specified
      'slug' => 'destinations/%destination_location%/%destination_category%',
      So, a post will have the slug with those 2 taxonomies term included.

  2. Hey Pradip,

    cool solution. I searched for a new project for this permalink-solution. Do you think this works with same two taxonomies and two (or multiple) custom-post-types too? I need custom-post-types like “sale” and “rent”. Both need the taxonomies (with many terms) like “city” and “type”.

    best regards

    1. No, this solution will work for a custom post type with 2 taxonomies only. If you wish to query different post types based on city & type, then I think you should use query_var to get the city queried through URL and then write custom queries to fetch posts from different CPTs.
      Currently, I can think of this solution only, there can be other solutions.

      1. Thank you for your answer Pradip. An other idee was to create a copy of the taxonomy with another slug (posttype1-city, posttype1-type, posttype2-city, posttype2-type) and to rewrite this to posttype1/city/type and posttype2/city/type. What do you mean?

        1. Sorry, didn’t get your question properly. But for rewrite URL & taxonomy queries you can have a look at the references which I provided in this article. Also, do some research on StackOverflow, I think it’ll help you with your issues.

          1. Hey Pradip,

            thank you. A solution with two posttypes works fine. But a have a problem with your filter (post type link) and I hope you can help me. I use to translate WPML an change the slug. The rewriting works, but the link brakes in the second language. I think i need something like this in die filters:

            if (ICL_LANGUAGE_CODE == ‘de’) {
            return ‘/de/’ . $link;
            else {
            return $link;

            You know what I mean? Thanks Marcus

Leave a Reply

Your email address will not be published. Required fields are marked *