I've been working with Custom Post Types and Custom Taxonomies for ages but one thing I can never get right are the rewrites when applying a shared taxonomy to multiple CPTs.
For instance, I have CPTs for 'Events' and 'Courses'. Each has individual taxonomies and also one shared taxonomy.
The individual taxonomies work fine:
/events/topic/{term}/
/courses/type/{term}/
However, I have a shared taxonomy called 'Accessibility Criteria'. With the custom taxonomy defined as it is below, I want (and would expect) to be able to filter each CPT by the shared taxonomy at different URLs:
/events/accessibility/{term}/
/courses/accessibility/{term}/
But whenever I try and use /events/accessibility/{term}/ it gets rewritten as /accessibility/{term}/
. Surprisingly (to me), the context is actually retained correctly e.g. if I trigger the taxonomy from the Events archive then only Event CPTs are shown, so the end-user experience still works fine. However, it doesn't sit comfortably with me as I feel it should work differently (as well as potentially better SEO etc).
I have defined the custom taxonomies before defining the CPTs as I have read that affects the ability of a CPT to use the taxonomy rewrite.
Any help much appreciated.
$labels = array(
"name" => __('Accessibility Criteria', ''),
"singular_name" => __('Accessibility Criteria', ''),
"add_new_item" => __('Add Accessibility Criteria', ''),
"new_item_name" => __('Add Accessibility Criteria', ''),
"edit_item" => __('Edit Accessibility Criteria', ''),
"view_item" => __('View Accessibility Criteria', ''),
"update_item" => __('Update Accessibility Criteria', ''),
"parent_item" => __('Parent Criteria', '')
);
$args = array(
"label" => __('Accessibility Criteria', ''),
"labels" => $labels,
"public" => true,
"hierarchical" => true,
"label" => "Accessibility Criteria",
"show_ui" => true,
"show_in_menu" => true,
"show_in_nav_menus" => false,
"capabilities" => array(
'manage_terms' => 'manage_accessibility_criteria',
'edit_terms' => 'edit_accessibility_criteria',
'delete_terms' => 'delete_accessibility_criteria',
'assign_terms' => 'assign_accessibility_criteria'
),
"query_var" => 'accessibility_criteria',
"rewrite" => array('slug' => 'accessibility', 'with_front' => true),
"show_admin_column" => true,
"show_in_rest" => false,
"rest_base" => "",
"show_in_quick_edit" => true,
);
register_taxonomy("accessibility_criteria", array("event", "course"), $args);
I've been working with Custom Post Types and Custom Taxonomies for ages but one thing I can never get right are the rewrites when applying a shared taxonomy to multiple CPTs.
For instance, I have CPTs for 'Events' and 'Courses'. Each has individual taxonomies and also one shared taxonomy.
The individual taxonomies work fine:
/events/topic/{term}/
/courses/type/{term}/
However, I have a shared taxonomy called 'Accessibility Criteria'. With the custom taxonomy defined as it is below, I want (and would expect) to be able to filter each CPT by the shared taxonomy at different URLs:
/events/accessibility/{term}/
/courses/accessibility/{term}/
But whenever I try and use /events/accessibility/{term}/ it gets rewritten as /accessibility/{term}/
. Surprisingly (to me), the context is actually retained correctly e.g. if I trigger the taxonomy from the Events archive then only Event CPTs are shown, so the end-user experience still works fine. However, it doesn't sit comfortably with me as I feel it should work differently (as well as potentially better SEO etc).
I have defined the custom taxonomies before defining the CPTs as I have read that affects the ability of a CPT to use the taxonomy rewrite.
Any help much appreciated.
$labels = array(
"name" => __('Accessibility Criteria', ''),
"singular_name" => __('Accessibility Criteria', ''),
"add_new_item" => __('Add Accessibility Criteria', ''),
"new_item_name" => __('Add Accessibility Criteria', ''),
"edit_item" => __('Edit Accessibility Criteria', ''),
"view_item" => __('View Accessibility Criteria', ''),
"update_item" => __('Update Accessibility Criteria', ''),
"parent_item" => __('Parent Criteria', '')
);
$args = array(
"label" => __('Accessibility Criteria', ''),
"labels" => $labels,
"public" => true,
"hierarchical" => true,
"label" => "Accessibility Criteria",
"show_ui" => true,
"show_in_menu" => true,
"show_in_nav_menus" => false,
"capabilities" => array(
'manage_terms' => 'manage_accessibility_criteria',
'edit_terms' => 'edit_accessibility_criteria',
'delete_terms' => 'delete_accessibility_criteria',
'assign_terms' => 'assign_accessibility_criteria'
),
"query_var" => 'accessibility_criteria',
"rewrite" => array('slug' => 'accessibility', 'with_front' => true),
"show_admin_column" => true,
"show_in_rest" => false,
"rest_base" => "",
"show_in_quick_edit" => true,
);
register_taxonomy("accessibility_criteria", array("event", "course"), $args);
I think you need to add a rewrite rule as the "normal" rewrite rules don't support your wanted permalink structure. For your use case, you can try it like this:
First: remove the "rewrite" argument from your register_taxonomy
arguments (not sure if you definitely need to do this, but it works on my site)
Second: Add the rewrite rule like this in your functions.php
add_action( 'init', 'wpse13483_init' );
function wpse292188_init(){
add_rewrite_rule('(events|courses)/accessibility/([^/]+)(/page/?([0-9]{1,}))?/?$','index.php?post_type=$matches[1]&taxonomy=accessibility&term=$matches[2]&paged=$matches[3]','top');
}
Don't forget to save permalinks after uploading!
Happy Coding!
You have to add these permalinks by your own. I do not see any code in WordPress which is creating $post_type/$taxonomy/$term
permalinks. I do not see such a rules also in my rewrite rule array after creating custom taxonomy assigned only to one custom post type. Behavior of your application might be caused by some plugin. Below you will find code which will add custom permalinks for $post_type/$taxonomy/$term
structure and load custom template when such a permalink is visited.
<?php
/**
* Register event post type
*
* Method is used by init hook
*/
function wpse_292188_register_event_post_type() {
$labels = array(
'name' => __( 'Events' ),
'singular_name' => __( 'Event' ),
'add_new' => __( 'Add new' ),
'add_new_item' => __( 'Add new' ),
'edit_item' => __( 'Edit' ),
'new_item' => __( 'New' ),
'view_item' => __( 'View' ),
'search_items' => __( 'Search' ),
'not_found' => __( 'Not found' ),
'not_found_in_trash' => __( 'Not found Events in trash' ),
'parent_item_colon' => __( 'Parent' ),
'menu_name' => __( 'Events' ),
);
$args = array(
'labels' => $labels,
'hierarchical' => false,
'supports' => array( 'title', 'page-attributes' ),
'taxonomies' => array(),
'public' => true,
'show_ui' => true,
'show_in_menu' => true,
'show_in_nav_menus' => false,
'publicly_queryable' => true,
'exclude_from_search' => false,
'has_archive' => true,
'query_var' => true,
'can_export' => true,
'rewrite' => array('slug' => 'event'),
'capability_type' => 'post',
);
register_post_type( 'event', $args );
}
add_action( 'init', 'wpse_292188_register_event_post_type' );
/**
* Register course post type
*
* Method is used by init hook
*/
function wpse_292188_register_course_post_type() {
$labels = array(
'name' => __( 'Courses' ),
'singular_name' => __( 'Course' ),
'add_new' => __( 'Add new' ),
'add_new_item' => __( 'Add new' ),
'edit_item' => __( 'Edit' ),
'new_item' => __( 'New' ),
'view_item' => __( 'View' ),
'search_items' => __( 'Search' ),
'not_found' => __( 'Not found' ),
'not_found_in_trash' => __( 'Not found Courses in trash' ),
'parent_item_colon' => __( 'Parent' ),
'menu_name' => __( 'Courses' ),
);
$args = array(
'labels' => $labels,
'hierarchical' => false,
'supports' => array( 'title', 'page-attributes' ),
'taxonomies' => array(),
'public' => true,
'show_ui' => true,
'show_in_menu' => true,
'show_in_nav_menus' => false,
'publicly_queryable' => true,
'exclude_from_search' => false,
'has_archive' => true,
'query_var' => true,
'can_export' => true,
'rewrite' => array('slug' => 'course'),
'capability_type' => 'post',
);
register_post_type( 'course', $args );
}
add_action( 'init', 'wpse_292188_register_course_post_type' );
/**
* Register accessibility_criteria taxonomy
*
* Method is used by init hook
*/
function wpse_292188_register_accessibility_criteria_taxonomy() {
$labels = array(
'name' => __( 'Accessibility Criteria', 'textdomain' ),
'singular_name' => __( 'Accessibility Criteria', 'textdomain' ),
'search_items' => __( 'Search Accessibility Criteria', 'textdomain' ),
'all_items' => __( 'All Accessibility Criteria', 'textdomain' ),
'parent_item' => __( 'Parent Accessibility Criteria', 'textdomain' ),
'parent_item_colon' => __( 'Parent Accessibility Criteria:', 'textdomain' ),
'edit_item' => __( 'Edit Accessibility Criteria', 'textdomain' ),
'update_item' => __( 'Update Accessibility Criteria', 'textdomain' ),
'add_new_item' => __( 'Add New Accessibility Criteria', 'textdomain' ),
'new_item_name' => __( 'New Accessibility Criteria Name', 'textdomain' ),
'menu_name' => __( 'Accessibility Criteria', 'textdomain' ),
);
$args = array(
'hierarchical' => true,
'labels' => $labels,
'show_ui' => true,
'show_admin_column' => true,
'query_var' => true,
'rewrite' => array( 'slug' => 'accessibility' ),
);
register_taxonomy( 'accessibility_criteria', array( 'event', 'course' ), $args );
}
add_action( 'init', 'wpse_292188_register_accessibility_criteria_taxonomy' );
/**
* Add rewrite rules for common taxonomy
*
* @return void
*/
function wpse_292188_add_taxonomy_and_post_type_rewrite_rule() {
/**
* This rewrite rule will match sample following link
*
* /event/accessibility/criteria-1
* /event/accessibility/criteria-1/page/2
*
* /course/accessibility/criteria-1
* /course/accessibility/criteria-1/page/2
*
* and "redirect" it to index.php with such a arguments
*
* index.php?post_type=event&accessibility_criteria=criteria-1
* index.php?post_type=event&accessibility_criteria=criteria-1&paged=2
*
* index.php?post_type=course&accessibility_criteria=criteria-1
* index.php?post_type=course&accessibility_criteria=criteria-1&paged=2
*/
add_rewrite_rule( 'event/accessibility/([^/]+)/?', 'index.php?post_type=event&accessibility_criteria=$matches[1]', 'top' );
add_rewrite_rule( 'event/accessibility/([^/]+)/page/?([0-9]{1,})/?', 'index.php?pots_type=eventaccessibility_criteria=$matches[1]&paged=$matches[2]', 'top' );
add_rewrite_rule( 'course/accessibility/([^/]+)/?', 'index.php?post_type=course&accessibility_criteria=$matches[1]', 'top' );
add_rewrite_rule( 'course/accessibility/([^/]+)/page/?([0-9]{1,})/?', 'index.php?pots_type=courseaccessibility_criteria=$matches[1]&paged=$matches[2]', 'top' );
}
add_action( 'init', 'wpse_292188_add_taxonomy_and_post_type_rewrite_rule' );
/**
* Register activation hooks. Only works in plugin file.
*/
register_activation_hook( __FILE__ , 'wpse_292188_activate' );
register_deactivation_hook( __FILE__ , 'wpse_292188_deactivate' );
/**
* Activation function
*/
function wpse_292188_activate() {
wpse_292188_add_taxonomy_and_post_type_rewrite_rule();
flush_rewrite_rules();
}
/**
* Deactivation function
*/
function wpse_292188_deactivate() {
flush_rewrite_rules();
}
/**
* Load proper template for taxonomy and post type rewrite rule
*
* @return string
*
* @throws \Exception When template do not exist.
*/
function wpse_292188_load_proper_template_for_taxonomy_and_post_type_rewrite_rule( $template ) {
$post_type = get_query_var('post_type');
$accessibility_criteria = get_query_var('accessibility_criteria');
/**
* Check if current page has post type and accessibility_criteria custom
* query var. If yest load our template.
*/
if( $post_type && !empty( $post_type ) && $accessibility_criteria && !empty( $accessibility_criteria ) ) {
/**
* Locate our custom template. You can use $post_type and/or
* $accessibility_criteria variables to create custom template.
*/
$template = locate_template( array( 'taxonomy.php' ) );
}
if( $template == '' ) {
throw new \Exception('No template found');
}
return $template;
}
add_filter( 'template_include', 'wpse_292188_load_proper_template_for_taxonomy_and_post_type_rewrite_rule' );
I'm using register_activation_hook
and register_deactivation_hook
so please put this code in your custom plugin.
I'm clearly late to the party but just in case someone else is looking for a solution I managed to do something similar by using some ideas from kierzniak's answer above.
I have 3 CPT's (news, events, downloads) that share the same taxonomy group (strategy) but I could not figure out the rewrite rules to make it all work.
Finally here's what I set up in my init function which works just fine:
add_rewrite_rule( '(news|events|downloads)/strategy/([^/]+)/?', 'index.php?post_type=$matches[1]&strategy=$matches[2]', 'top' );
add_rewrite_rule( '(news|events|downloads)/strategy/([^/]+)/page/?([0-9]{1,})/?', 'index.php?post_type=$matches[1]&strategy=$matches[2]&paged=$matches[3]', 'top' );
add_rewrite_rule( '(news|events|downloads)/strategy/([^/]+)/feed/(feed|rdf|rss|rss2|atom)/?', 'index.php?post_type=$matches[1]&strategy=$matches[2]&feed=$matches[3]', 'top' );
add_rewrite_rule( '(news|events|downloads)/strategy/([^/]+)/(feed|rdf|rss|rss2|atom)/?', 'index.php?post_type=$matches[1]&strategy=$matches[2]&feed=$matches[3]', 'top' );