22

I know people have asked this before and have gone as far as adding the custom post type, and rewrite for permalink.

The problem is I have 340 existing categories which I would like to continue using. I used to be able to see /category/subcategory/postname

Now I have the slug of customposttype/postname. Selecting the category no longer shows up in permalink...I've not changed the permalink setting in admin to anything different.

Is there something I'm missing or need to add to this code?

function jcj_club_post_types() {
    register_post_type( 'jcj_club', array(
        'labels' => array(
            'name' => __( 'Jazz Clubs' ),
            'singular_name' => __( 'Jazz Club' ),
            'add_new' => __( 'Add New' ),
            'add_new_item' => __( 'Add New Jazz Club' ),
            'edit' => __( 'Edit' ),
            'edit_item' => __( 'Edit Jazz Clubs' ),
            'new_item' => __( 'New Jazz Club' ),
            'view' => __( 'View Jazz Club' ),
            'view_item' => __( 'View Jazz Club' ),
            'search_items' => __( 'Search Jazz Clubs' ),
            'not_found' => __( 'No jazz clubs found' ),
            'not_found_in_trash' => __( 'No jazz clubs found in Trash' ),
            'parent' => __( 'Parent Jazz Club' ),
        ),
        'public' => true,
        'show_ui' => true,
        'publicly_queryable' => true,
        'exclude_from_search' => false,
        'menu_position' => 5,
        'query_var' => true,
        'supports' => array( 
            'title',
            'editor',
            'comments',
            'revisions',
            'trackbacks',
            'author',
            'excerpt',
            'thumbnail',
            'custom-fields',
        ),
        'rewrite' => array( 'slug' => 'jazz-clubs-in', 'with_front' => true ),
        'taxonomies' => array( 'category','post_tag'),
        'can_export' => true,
    )
);
16
+100

There are 2 points of attack to cover when you are adding custom post type rewrite rules:

Rewrite rules

This happens when the rewrite rules are being generated in wp-includes/rewrite.php in WP_Rewrite::rewrite_rules(). WordPress allows you to filter the rewrite rules for specific elements like posts, pages and various types of archive. Where you see posttype_rewrite_rules the posttype part should be the name of your custom post type. Alternatively you can use the post_rewrite_rules filter as long as you don't obliterate the standard post rules too.

Next we need the function to actually generate the rewrite rules:

// add our new permastruct to the rewrite rules
add_filter( 'posttype_rewrite_rules', 'add_permastruct' );

function add_permastruct( $rules ) {
    global $wp_rewrite;

    // set your desired permalink structure here
    $struct = '/%category%/%year%/%monthnum%/%postname%/';

    // use the WP rewrite rule generating function
    $rules = $wp_rewrite->generate_rewrite_rules(
        $struct,       // the permalink structure
        EP_PERMALINK,  // Endpoint mask: adds rewrite rules for single post endpoints like comments pages etc...
        false,         // Paged: add rewrite rules for paging eg. for archives (not needed here)
        true,          // Feed: add rewrite rules for feed endpoints
        true,          // For comments: whether the feed rules should be for post comments - on a singular page adds endpoints for comments feed
        false,         // Walk directories: whether to generate rules for each segment of the permastruct delimited by '/'. Always set to false otherwise custom rewrite rules will be too greedy, they appear at the top of the rules
        true           // Add custom endpoints
    );

    return $rules;
}

The main thing to watch out for here if you decide to play around is the 'Walk directories' boolean. It generates rewrite rules for each segment of a permastruct and can cause rewrite rule mismatches. When a WordPress URL is requested the rewrite rules array is checked from top to bottom. As soon as a match is found it will load whatever it has come across so for example if your permastruct has a greedy match eg. for /%category%/%postname%/ and walk directories is on it will output rewrite rules for both /%category%/%postname%/ AND /%category%/ which will match anything. If that happens too early you're screwed.

Permalinks

This is the function that parses the post type permalinks and converts a permastruct (eg '/%year%/%monthnum%/%postname%/') into an actual URL.

The next part is a simple example of what would ideally be a version of the get_permalink() function found in wp-includes/link-template.php. Custom post permalinks are generated by get_post_permalink() which is a much watered down version of get_permalink(). get_post_permalink() is filtered by post_type_link so we're using that to make a custom permastructure.

// parse the generated links
add_filter( 'post_type_link', 'custom_post_permalink', 10, 4 );

function custom_post_permalink( $permalink, $post, $leavename, $sample ) {

    // only do our stuff if we're using pretty permalinks
    // and if it's our target post type
    if ( $post->post_type == 'posttype' && get_option( 'permalink_structure' ) ) {

        // remember our desired permalink structure here
        // we need to generate the equivalent with real data
        // to match the rewrite rules set up from before

        $struct = '/%category%/%year%/%monthnum%/%postname%/';

        $rewritecodes = array(
            '%category%',
            '%year%',
            '%monthnum%',
            '%postname%'
        );

        // setup data
        $terms = get_the_terms($post->ID, 'category');
        $unixtime = strtotime( $post->post_date );

        // this code is from get_permalink()
        $category = '';
        if ( strpos($permalink, '%category%') !== false ) {
            $cats = get_the_category($post->ID);
            if ( $cats ) {
                usort($cats, '_usort_terms_by_ID'); // order by ID
                $category = $cats[0]->slug;
                if ( $parent = $cats[0]->parent )
                    $category = get_category_parents($parent, false, '/', true) . $category;
            }
            // show default category in permalinks, without
            // having to assign it explicitly
            if ( empty($category) ) {
                $default_category = get_category( get_option( 'default_category' ) );
                $category = is_wp_error( $default_category ) ? '' : $default_category->slug;
            }
        }

        $replacements = array(
            $category,
            date( 'Y', $unixtime ),
            date( 'm', $unixtime ),
            $post->post_name
        );

        // finish off the permalink
        $permalink = home_url( str_replace( $rewritecodes, $replacements, $struct ) );
        $permalink = user_trailingslashit($permalink, 'single');
    }

    return $permalink;
}

As mentioned this a very simplified case for generating a custom rewrite ruleset and permalinks, and is not particularly flexible but it should be enough to get you started.

Cheating

I wrote a plugin that lets you define permastructs for any custom post types, but like you can use %category% in the permalink structure for posts my plugin supports %custom_taxonomy_name% for any custom taxonomies you have too where custom_taxonomy_name is the name of your taxonomy eg. %club%.

It will work as you'd expect with hierarchical/non-hierarchical taxonomies.

http://wordpress.org/extend/plugins/wp-permastructure/

  • 1
    the plugin is great, but could you explain how to fix the issue in the question without your plugin? – Eugene Manuilov Dec 20 '12 at 13:39
  • I agree that it's great there is plugin to address it (I have it bookmarked and it is first that came to my mind on this Q), but the answer would benefit from brief summary of what the issue is and how plugin conquered it. :) – Rarst Dec 22 '12 at 14:30
  • @EugeneManuilov Alright, sorry it's a lengthy answer. That's with me stripping it down to the basics too! – sanchothefat Dec 24 '12 at 2:01
  • It looks like the first $permalink = home_url(... gets overridden by $permalink = user_trailingslashit(... and never used. Or am I missing something? $post_link isn't even defined. Was it supposed to be $permalink = user_trailingslashit( $permalink, 'single' ); ? – Ian Dunn Feb 5 '13 at 23:07
  • Good catch, it should be $permalink not $post_link. Cheers :) – sanchothefat Feb 6 '13 at 10:50
1

Got the solution!

To have hierarchical permalinks for custom post type install Custom Post Type Permalinks(https://wordpress.org/plugins/custom-post-type-permalinks/) plugin.

Update registered post type. I have post type name as help center

function help_centre_post_type(){
    register_post_type('helpcentre', array( 
        'labels'            =>  array(
            'name'          =>      __('Help Center'),
            'singular_name' =>      __('Help Center'),
            'all_items'     =>      __('View Posts'),
            'add_new'       =>      __('New Post'),
            'add_new_item'  =>      __('New Help Center'),
            'edit_item'     =>      __('Edit Help Center'),
            'view_item'     =>      __('View Help Center'),
            'search_items'  =>      __('Search Help Center'),
            'no_found'      =>      __('No Help Center Post Found'),
            'not_found_in_trash' => __('No Help Center Post in Trash')
                                ),
        'public'            =>  true,
        'publicly_queryable'=>  true,
        'show_ui'           =>  true, 
        'query_var'         =>  true,
        'show_in_nav_menus' =>  false,
        'capability_type'   =>  'page',
        'hierarchical'      =>  true,
        'rewrite'=> [
            'slug' => 'help-center',
            "with_front" => false
        ],
        "cptp_permalink_structure" => "/%help_centre_category%/%post_id%-%postname%/",
        'menu_position'     =>  21,
        'supports'          =>  array('title','editor', 'thumbnail'),
        'has_archive'       =>  true
    ));
    flush_rewrite_rules();
}
add_action('init', 'help_centre_post_type');

And here is registered taxonomy

function themes_taxonomy() {  
    register_taxonomy(  
        'help_centre_category',  
        'helpcentre',        
        array(
            'label' => __( 'Categories' ),
            'rewrite'=> [
                'slug' => 'help-center',
                "with_front" => false
            ],
            "cptp_permalink_structure" => "/%help_centre_category%/",
            'hierarchical'               => true,
            'public'                     => true,
            'show_ui'                    => true,
            'show_admin_column'          => true,
            'show_in_nav_menus'          => true,
            'query_var' => true
        ) 
    );  
}  
add_action( 'init', 'themes_taxonomy');

This is line makes your permalink work

"cptp_permalink_structure" => "/%help_centre_category%/%post_id%-%postname%/",

you can remove %post_id% and can keep /%help_centre_category%/%postname%/"

Don't forget to flush permalinks from dashboard.

  • +1 the simplest solution is to just use this plugin: wordpress.org/plugins/custom-post-type-permalinks works perfectly – Jules Aug 16 '17 at 10:49
  • Yes, but that is for if you have single custom post type but if you have multiple custom post type in single theme then above is the solution. Moreover it also changes your category slug same as your post type slug. – Varsha Dhadge Aug 18 '17 at 6:41
1

I have found a SOLUTION!!!

(After endless research.. I can have CUSTOM POST TYPE permalinks like:
example.com/category/sub_category/my-post-name

here the code (in functions.php or plugin):

//===STEP 1 (affect only these CUSTOM POST TYPES)
$GLOBALS['my_post_typesss__MLSS'] = array('my_product1','....');

//===STEP 2  (create desired PERMALINKS)
add_filter('post_type_link', 'my_func88888', 6, 4 );

function my_func88888( $post_link, $post, $sdsd){
    if (!empty($post->post_type) && in_array($post->post_type, $GLOBALS['my_post_typesss']) ) {  
        $SLUGG = $post->post_name;
        $post_cats = get_the_category($id);     
        if (!empty($post_cats[0])){ $target_CAT= $post_cats[0];
            while(!empty($target_CAT->slug)){
                $SLUGG =  $target_CAT->slug .'/'.$SLUGG; 
                if  (!empty($target_CAT->parent)) {$target_CAT = get_term( $target_CAT->parent, 'category');}   else {break;}
            }
            $post_link= get_option('home').'/'. urldecode($SLUGG);
        }
    }
    return  $post_link;
}

// STEP 3  (by default, while accessing:  "EXAMPLE.COM/category/postname"
// WP thinks, that a standard post is requested. So, we are adding CUSTOM POST
// TYPE into that query.
add_action('pre_get_posts', 'my_func4444',  12); 

function my_func4444($q){     
    if ($q->is_main_query() && !is_admin() && $q->is_single){
        $q->set( 'post_type',  array_merge(array('post'), $GLOBALS['my_post_typesss'] )   );
    }
    return $q;
}
-2

You have several errors with your code. I cleaned up your existing code:

<?php
function jcj_club_post_types() {
  $labels = array(
    'name' => __( 'Jazz Clubs' ),
    'singular_name' => __( 'Jazz Club' ),
    'add_new' => __( 'Add New' ),
    'add_new_item' => __( 'Add New Jazz Club' ),
    'edit' => __( 'Edit' ),
    'edit_item' => __( 'Edit Jazz Clubs' ),
    'new_item' => __( 'New Jazz Club' ),
    'view' => __( 'View Jazz Club' ),
    'view_item' => __( 'View Jazz Club' ),
    'search_items' => __( 'Search Jazz Clubs' ),
    'not_found' => __( 'No jazz clubs found' ),
    'not_found_in_trash' => __( 'No jazz clubs found in Trash' ),
    'parent' => __( 'Parent Jazz Club' ),
    );
  $args = array(
    'public' => true,
    'show_ui' => true,
    'publicly_queryable' => true,
    'exclude_from_search' => false,
    'menu_position' => 5,
    'query_var' => true,
    'supports' => array( 'title','editor','comments','revisions','trackbacks','author','excerpt','thumbnail','custom-fields' ),
    'rewrite' => array( 'slug' => 'jazz-clubs-in', 'with_front' => true ),
    'has_archive' => true
    );
  register_post_type( 'jcj_club', $args );
  }
add_action( 'init','jcj_club_post_types' );
?>

Replace your code with the code above and see if it works. Reply back if you have further questions and I'll try to help.

EDIT:

I noticed I left out 'has_archive' => true.

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.