WordPress Development Stack Exchange is a question and answer site for WordPress developers and administrators. Join them; it only takes a minute:

Sign up
Here's how it works:
  1. Anybody can ask a question
  2. Anybody can answer
  3. The best answers are voted up and rise to the top

Is wordpress capable of having hierarchical taxonomy and hierarchical custom post types in one permalink ?

I would like to do the following:


in other words:


or in a real case where "a-day-at-the-beach" is the travelog post and "sand-in-my-trunks" is the video post:


I have 4 custom post types that would always be at the level of cpt-1 and 3 custom post types (videos, photos, sketches) that would always appear at the sub level of cpt-2.

When complete the site will have roughly 4000 posts in total.

I'm hoping there is a plugin or set of plugins to help achieve this, but so far I've not found that magic combination. If not, can this permalink structure be custom coded with url re-writes and REGEX ?

share|improve this question
Looks like you're trying to make parent/child relationship in 2 different post types. While it's not technically impossible, wordpress admin panel will ignore it. You'll need to create a custom admin screen for it. Have you successfully implemented this? – Mridul Aggarwal Dec 17 '12 at 19:02
@MridulAggarwal I'm confused by your comment. Are you saying that the permalink I desire is not technically possible ? I have neither implemented the permalink structure nor created a custom admin screen. – timoto Dec 17 '12 at 19:14
I'm trying to understand what you have already done & what you want to do. If you haven't implemented a custom screen, how can you make a post from 1 CPT a parent of another CPT? Technically it is possible but i'm trying to understand what you've achieved so far. – Mridul Aggarwal Dec 17 '12 at 19:26
@MridulAggarwal I am able to create custom hierarchical taxonomy and custom post types, but not hierarchical custom post types. So I don't know how to make the proposed permalink that has both hierarchical taxonomy and hierarchical custom post types. At best I have been able to create tax-1/tax-2/cpt-1/cpt-1-post/ but with varying results depending on the use of plugins. Have you seen a working example such as this: tax-1/tax-2/cpt-1/cpt-1-post/cpt-2/cpt-2-post/ ? – timoto Dec 17 '12 at 23:35
I haven't. Wordpress lacks simple ways when it comes to permalink structures. For this case, you'll need more than just permalink. First you'll need to provide a way to link cpt-1-post & cpt-2-post(you need a custom admin screen for it). Then you'll use add_rewrite_rule to add rules. In this function you have to make sure you don't rewrite it to both cpt-1-post & cpt-2-post. – Mridul Aggarwal Dec 18 '12 at 3:53
up vote 1 down vote accepted

To cut a long story short the answer to the question is Yes.

In the end I was able to create: tax-1/tax-2/cpt-1/cpt-1-post/cpt-2/cpt-2-post/

and another structure: tax-1/tax-2/cpt-1/cpt-1-post/cpt-2/custom-field/cpt-2-post/

I used Types from http://wp-types.com/ to create the hierarchical custom taxonomy, the custom post types, custom fields, but most importantly the parent/child relationship between custom post types. That plugin saved a lot of time.

The next step was to do the URL re-writes using regular expressions.


    // Add custom rewrite rules

    add_filter( 'rewrite_rules_array','my_insert_rewrite_rules' );

    // Add custom variable for WP_QUERY

    add_filter( 'query_vars','my_insert_query_vars' );

    // This hook is fired once WP, all plugins, and the theme are fully loaded and instantiated.

    add_action( 'wp_loaded','my_flush_rules' );

    // Flush rules for include custom rewrite rules

    function my_flush_rules(){
        $rules = get_option( 'rewrite_rules' );
        global $wp_rewrite;

    // Add custom rewrite rules

    function my_insert_rewrite_rules( $rules ){
        $newrules = array();  

        $location_list = get_terms("location",array("parent" => 0, "hide_empty" => 0));
        foreach($location_list as $location){
            $base_url = sprintf('index.php?taxonomy=%s&term=%s&child_term=$matches[1]&type=$matches[2]&type_title=$matches[3]&media_type=$matches[4]&media_maintitle=$matches[5]&media_title=$matches[6]','location',$location->slug);
            $newrules[$location->slug.'/([^/]+)/([^/]+)/([^/]+)/([^/]+)/([^/]+)/([^/]+)/?'] = $base_url;
            $base_url = sprintf('index.php?taxonomy=%s&term=%s&child_term=$matches[1]&type=$matches[2]&type_title=$matches[3]&media_type=$matches[4]&media_title=$matches[5]','location',$location->slug);        
            $newrules[$location->slug.'/([^/]+)/([^/]+)/([^/]+)/([^/]+)/([^/]+)/?'] = $base_url;
            $base_url = sprintf('index.php?taxonomy=%s&term=%s&child_term=$matches[1]&type=$matches[2]&type_title=$matches[3]&media_type=$matches[4]','location',$location->slug);        
            $newrules[$location->slug.'/([^/]+)/([^/]+)/([^/]+)/([^/]+)/?'] = $base_url;
            $base_url = sprintf('index.php?taxonomy=%s&term=%s&child_term=$matches[1]&type=$matches[2]&type_title=$matches[3]','location',$location->slug);        
            $newrules[$location->slug.'/([^/]+)/([^/]+)/([^/]+)/?'] = $base_url;
            $base_url = sprintf('index.php?taxonomy=%s&term=%s&child_term=$matches[1]&type=$matches[2]','location',$location->slug);        
            $newrules[$location->slug.'/([^/]+)/([^/]+)/?'] = $base_url;
            $base_url = sprintf('index.php?taxonomy=%s&term=%s&child_term=$matches[1]','location',$location->slug);        
            $newrules[$location->slug.'/([^/]+)/?'] = $base_url;

        return $newrules + $rules;

    // Add custom variable for WP_QUERY

    function my_insert_query_vars( $vars ){                                                                                               
        array_push($vars, 'taxonomy');          
        array_push($vars, 'child_term');                   
        array_push($vars, 'type');                       
        array_push($vars, 'type_title');                           
        array_push($vars, 'media_type');                                
        array_push($vars, 'media_maintitle');                    
        array_push($vars, 'media_title'); 
        return $vars;


I'm not sure if the optimum REGEX rules have been created here, but I found I had to do this much to make sure re-directs were in place ensuring no potential for multiple URLs displaying the same content.

There is more to the answer than I've posted here, but I think there is a limit to the number of lines I can post.

share|improve this answer
You can post up to 30.000 characters. So there is still room for detailed explanations. :) – toscho Jan 15 '13 at 20:44

Your Answer


By posting your answer, you agree to the privacy policy and terms of service.

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