Adding categories to custom post type in permalink

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

Read More

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,
    )
);

Related posts

Leave a Reply

4 comments

  1. 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/

  2. 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;
    }
    
  3. 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.

  4. 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.