2

I am trying to use categories to provide the structure for my site. I will create sections on my site called England, Wales, Scotland and Ireland . These will be categories. My posts will use custom post types - Video, Guide, Event, Clubs etc.

So, if I create a post called 'Trouble down south' using the 'Video' CPT, attach it to the 'England' category, I would like the slug format to be:

category-name/custom-post-type-name/post-name

e.g. mysite.com/england/video/trouble-down-south

I would also like the following permalinks to work like so -

mysite.com/england/ - show all posts in that category regardless of CPT

mysite.com/video/ - show all Video CPT posts regardless of category

Sticking with my example, I create my Video CPT like so:

add_action( 'init', 'register_cpt_video' );

function register_cpt_video() {

    $labels = array( 
        'name' => _x( 'videos', 'video' ),

        some code...

    );

    $args = array( 

        some code...

        'taxonomies' => array( 'category' ),

        some code...

        'has_archive' => true,
        'rewrite' => array( 
            'slug' => 'video', 
            'with_front' => false,
            'feeds' => true,
            'pages' => true
        ),
        'capability_type' => 'post'
    );

    register_post_type( 'video', $args );
}

This almost does what I need. I get the correct listings at

mysite.com/england (i.e category archive page)

and

mysite.com/video (i.e CPT archive page)

Unfortunately when I click through to the actual post, the slug shows as

mysite.com/video/trouble-down-south

I would like

mysite.com/england/video/trouble-down-south

I've tried using the following rewrite slug

'slug' => '%category%/video',

This does not work. %category% gets literally inserted into the url without being converted into the actual category name.

I have searched this site for a solution but haven't found any. Most related answers seem to address appending taxonomies to the CPT slug.

i.e mysite.com/video/england/trouble-down-south

They seem to achieve this by explicitly prefixing the CPT name to the taxonomy rewrite slug which exploits the cascade-like nature of the rewrite rules (plus some other wizardry I don't understand.)

Anyhow, nothing I have found has addressed my requirement of category-name/custom-post-type-name/post-name, plus the archive pages I require.

Any ideas how to achieve my desired permalink structure?

  • Please add more details to your question: We have many solutions for this on our site (see the related questions in the sidebar). What have you tried, why didn’t it work? – fuxia Aug 28 '12 at 15:19
  • I've updated my question as per your suggestion. Hopefully it's more clearer now. And yup, I've searched this site for over an hour but most of the related answers address the CPT/taxonomy/post-name permalink scheme. I tried wrangling the code I did find to suite my needs, but don't really know what I'm doing. – bob Aug 28 '12 at 17:53
  • Well, I spent another hour or so proving to myself that I really need to work on my google-fu. I have failed to find anyone document the permalink scheme I'm trying to achieve. Usually that means I'm going about things the wrong way. Maybe I should try to layout my site structure a different, more WordPress-friendly way? – bob Aug 29 '12 at 7:26
4

You have to filter 'post_type_link' to create the appropriate permalink. See this answer for a similar example.

The following code works:

add_action( 'init', array ( 'WPSE_63343', 'init' ) );

class WPSE_63343
{
    protected $post_type = 'video';

    public static function init()
    {
        new self;
    }

    public function __construct()
    {
        $args = array (
            'public' => TRUE,
            'rewrite' => array(
                'slug' => '[^/]+/' . $this->post_type .'/([^/]+)/?$'
            ),
            'has_archive' => TRUE,
            'supports' => array( 'title', 'editor' ),
            'taxonomies' => array( 'category' ),
            'labels' => array (
                'name' => 'Video',
                'singular_name' => 'Video'
            )
        );
        register_post_type( $this->post_type, $args );

        add_rewrite_rule(
            '[^/]+/' . $this->post_type .'/([^/]+)/?$',
            'index.php?post_type=' . $this->post_type . '&pagename=$matches[1]',
            'top'
        );

        // Inject our custom structure.
        add_filter( 'post_type_link', array ( $this, 'fix_permalink' ), 1, 2 );

        # uncomment for debugging
        # add_action( 'wp_footer', array ( $this, 'debug' ) );
    }

    /**
     * Print debug data into the 404 footer.
     *
     * @wp-hook wp_footer
     * @since   2012.09.04
     * @return  void
     */
    public function debug()
    {
        if ( ! is_404() )
        {
            return;
        }

        global $wp_rewrite, $wp_query;

        print '<pre>' . htmlspecialchars( print_r( $wp_rewrite, TRUE ) ) . '</pre>';
        print '<pre>' . htmlspecialchars( print_r( $wp_query, TRUE ) ) . '</pre>';
    }

    /**
     * Filter permalink construction.
     *
     * @wp-hook post_type_link
     * @param  string $post_link default link.
     * @param  int    $id Post ID
     * @return string
     */
    public function fix_permalink( $post_link, $id = 0 )
    {
        $post = &get_post( $id );
        if ( is_wp_error($post) || $post->post_type != $this->post_type )
        {
            return $post_link;
        }
        // preview
        empty ( $post->slug )
        and ! empty ( $post->post_title )
        and $post->slug = sanitize_title_with_dashes( $post->post_title );

        $cats = get_the_category( $post->ID );

        if ( ! is_array( $cats ) or ! isset ( $cats[0]->slug ) )
        {
            return $post_link;
        }

        return home_url(
            user_trailingslashit( $cats[0]->slug . '/' . $this->post_type . '/' . $post->slug )
        );
    }
}

Don’t forget to visit the permalink settings once to refresh the stored rewrite rules.

  • Wow, cheers for helping me out. Seems to work as intended. I'll have a go at creating additional cpt's using your code as a template. Many thanks – bob Aug 29 '12 at 18:31
  • Doh, when I use your code example it breaks standard posts and other post types I add categories to. Any ideas why? – bob Sep 4 '12 at 14:45
  • @bob Interesting. See my update: you have to put the rewrite regex into the slug parameter. Looks a little bit strange, but it works now. – fuxia Sep 4 '12 at 21:29
0
add_action( 'init', 'register_my_types' );
function register_my_types() {
    register_post_type( 'recipes',
        array(
            'labels' => array(
                'name' => __( 'Recipes' ),
                'singular_name' => __( 'Recipee' )
            ),
            'public' => true,
            'has_archive' => true,
        )
    );
    register_taxonomy( 'country', array( 'recipes' ), array( 
            'hierarchical' => true, 
            'label' => 'Country'
        )
    );
}
// Add our custom permastructures for custom taxonomy and post
add_action( 'wp_loaded', 'add_clinic_permastructure' );
function add_clinic_permastructure() {
    global $wp_rewrite;
    add_permastruct( 'country', 'recipes/%country%', false );
    add_permastruct( 'recipes', 'country/%country%/recipes/%recipes%', false );
}
// Make sure that all links on the site, include the related texonomy terms
add_filter( 'post_type_link', 'recipe_permalinks', 10, 2 );
function recipe_permalinks( $permalink, $post ) {
    if ( $post->post_type !== 'recipes' )
        return $permalink;
    $terms = get_the_terms( $post->ID, 'country' );

    if ( ! $terms )
        return str_replace( '%country%/', '', $permalink );
    //$post_terms = array();
    foreach ( $terms as $term )
        $post_terms = $term->slug;
    print_r($permalink);
    return str_replace( '%country%',  $post_terms , $permalink );
}
// Make sure that all term links include their parents in the permalinks
add_filter( 'term_link', 'add_term_parents_to_permalinks', 10, 2 );
function add_term_parents_to_permalinks( $permalink, $term ) {
    $term_parents = get_term_parents( $term );
    foreach ( $term_parents as $term_parent )
        $permlink = str_replace( $term->slug, $term_parent->slug . ',' . $term->slug, $permalink );
    return $permlink;
}
// Helper function to get all parents of a term
function get_term_parents( $term, &$parents = array() ) {
    $parent = get_term( $term->parent, $term->taxonomy );

    if ( is_wp_error( $parent ) )
        return $parents;

    $parents[] = $parent;
    if ( $parent->parent )
        get_term_parents( $parent, $parents );
    return $parents;
}

above code paste in theme function.php file and change the permalink option /%category/%postname%/ %

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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