My Site has 2 Custom Post Types with a 2 Taxonomies. One taxonomy is “Location” which is 2 level / hierarchical (Country, City). My goal is to get our Post Types & Taxonomies in a SEO Pretty Permalink structure. I have completed most of the Permalink structure but I am still getting 404’s errors on the Post / Slug Landing pages. I am halfway there and I have ran out of patience, I have tried dozens of solutions and they all seem to break one thing or another.
Post Types: Excursion, Stay-Dive
Taxonomies: Location, Excursion-Category
Taxonomy Pages:
Working - /location/%LocationCountry%/
Working - /location/%LocationCountry%/%LocationCity%/
Post Type Pages:
Working - /stay-dive/%LocationCountry%/
Working - /stay-dive/%LocationCountry%/%LocationCity%/
Not Working /stay-dive/%LocationCountry%/%LocationCity%/Slug - Not Working
Working - /excursion/%LocationCountry%/
Not Working - /excursion/%LocationCountry%/%LocationCity%/
Not Working - /excursion/%LocationCountry%/%LocationCity%/Slug
Working - /excursion/%excursion-category%/
Not Working - /excursion/%excursion-category%/%LocationCountry%/
Not Working - /excursion/%excursion-category%/%LocationCountry%/%LocationCity%/
Taxonomy Code:
// excursion category
register_taxonomy('pdm_excursion_category', array('pdm_excursion'), array(
'labels' => array(
'name' => _x('Categories', 'taxonomy general name', 'pdm'),
'singular_name' => _x('Category', 'taxonomy singular name', 'pdm'),
'menu_name' => __('Categories', 'pdm'),
'all_items' => __('All Categories', 'pdm'),
'edit_item' => __('Edit Category', 'pdm'),
'view_item' => __('View Category', 'pdm'),
'update_item' => __('Update Category', 'pdm'),
'add_new_item' => __('Add New Category', 'pdm'),
'new_item_name' => __('New Category Name', 'pdm'),
'parent_item' => __('Parent Category', 'pdm'),
'parent_item_colon' => __('Parent Category:', 'pdm'),
'search_items' => __('Search Categories', 'pdm'),
'popular_items' => __('Popular Categories', 'pdm'),
'separate_items_with_commas' => __('Separate categories with commas', 'pdm'),
'add_or_remove_items' => __('Add or remove categories', 'pdm'),
'choose_from_most_used' => __('Choose from the most used categories', 'pdm'),
'not_found' => __('No categories found.', 'pdm'),
),
'show_ui' => true,
'show_admin_column' => true,
'hierarchical' => true,
'query_var' => true,
'rewrite' => array('slug' => 'excursions', 'with_front' => true, 'hierarchical' => false),
));
// location
register_taxonomy('pdm_location', array('pdm_hotel', 'pdm_excursion', 'pdm_dive_site'), array(
'labels' => array(
'name' => _x('Locations', 'taxonomy general name', 'pdm'),
'singular_name' => _x('Location', 'taxonomy singular name', 'pdm'),
'menu_name' => __('Locations', 'pdm'),
'all_items' => __('All Locations', 'pdm'),
'edit_item' => __('Edit Location', 'pdm'),
'view_item' => __('View Location', 'pdm'),
'update_item' => __('Update Location', 'pdm'),
'add_new_item' => __('Add New Location', 'pdm'),
'new_item_name' => __('New Location Name', 'pdm'),
'parent_item' => __('Parent Location', 'pdm'),
'parent_item_colon' => __('Parent Location:', 'pdm'),
'search_items' => __('Search Locations', 'pdm'),
'popular_items' => __('Popular Locations', 'pdm'),
'separate_items_with_commas' => __('Separate locations with commas', 'pdm'),
'add_or_remove_items' => __('Add or remove locations', 'pdm'),
'choose_from_most_used' => __('Choose from the most used locations', 'pdm'),
'not_found' => __('No locations found.', 'pdm'),
),
'show_ui' => true,
'show_admin_column' => true,
'hierarchical' => true,
'query_var' => true,
'rewrite' => array('slug' => 'location', 'with_front' => false, 'hierarchical' => true),
));
Post Type Code:
// hotel
register_post_type('pdm_hotel' , array(
'labels' => array(
'name' => _x('Stay & Dive Hotels', 'post type general name', 'pdm'),
'singular_name' => _x('Stay & Dive', 'post type singular name', 'pdm'),
'menu_name' => _x('Stay & Dive Hotels', 'admin menu', 'pdm'),
'name_admin_bar' => _x('Stay & Dive Hotel', 'add new on admin bar', 'pdm'),
'add_new' => _x('Add New', 'book', 'pdm'),
'add_new_item' => __('Add New Hotel', 'pdm'),
'new_item' => __('New Stay & Dive Hotel', 'pdm'),
'edit_item' => __('Edit Stay & Dive Hotel', 'pdm'),
'view_item' => __('View Stay & Dive Hotel', 'pdm'),
'all_items' => __('All Stay & Dive Hotels', 'pdm'),
'search_items' => __('Search Stay & Dive Hotels', 'pdm'),
'parent_item_colon' => __('Parent Stay & Dive Hotel:', 'pdm'),
'not_found' => __('No hotels found.', 'pdm'),
'not_found_in_trash' => __('No hotels found in Trash.', 'pdm'),
),
'public' => true,
'publicly_queryable' => true,
'show_ui' => true,
'show_in_menu' => true,
'query_var' => true,
'rewrite' => array('slug' => 'stay-dive/%pdm_location%/%pdm_location2%', 'with_front' => true),
'capability_type' => 'post',
'has_archive' => true,
'hierarchical' => false,
'menu_position' => 101,
'menu_icon' => 'dashicons-admin-home',
'supports' => array('title','editor','thumbnail'),
'taxonomies' => array('pdm_location'),
));
// excursions
register_post_type('pdm_excursion' , array(
'labels' => array(
'name' => _x('Excursions', 'post type general name', 'pdm'),
'singular_name' => _x('Excursion', 'post type singular name', 'pdm'),
'menu_name' => _x('Excursions', 'admin menu', 'pdm'),
'name_admin_bar' => _x('Excursion', 'add new on admin bar', 'pdm'),
'add_new' => _x('Add New', 'book', 'pdm'),
'add_new_item' => __('Add New Excursion', 'pdm'),
'new_item' => __('New Excursion', 'pdm'),
'edit_item' => __('Edit Excursion', 'pdm'),
'view_item' => __('View Excursion', 'pdm'),
'all_items' => __('All Excursions', 'pdm'),
'search_items' => __('Search Excursions', 'pdm'),
'parent_item_colon' => __('Parent Excursion:', 'pdm'),
'not_found' => __('No excursions found.', 'pdm'),
'not_found_in_trash' => __('No excursions found in Trash.', 'pdm'),
),
'public' => true,
'publicly_queryable' => true,
'show_ui' => true,
'show_in_menu' => true,
'query_var' => true,
'rewrite' => array('slug' => 'excursion /%pdm_excursion_category%/%pdm_location%/%pdm_location2%','with_front' => true),
'capability_type' => 'post',
'has_archive' => true,
'hierarchical' => false,
'menu_position' => 102,
'menu_icon' => 'dashicons-location',
'supports' => array('title','editor','thumbnail'),
'taxonomies' => array('pdm_location','pdm_excursion_category'),
));
My Site has 2 Custom Post Types with a 2 Taxonomies. One taxonomy is “Location” which is 2 level / hierarchical (Country, City). My goal is to get our Post Types & Taxonomies in a SEO Pretty Permalink structure. I have completed most of the Permalink structure but I am still getting 404’s errors on the Post / Slug Landing pages. I am halfway there and I have ran out of patience, I have tried dozens of solutions and they all seem to break one thing or another.
Post Types: Excursion, Stay-Dive
Taxonomies: Location, Excursion-Category
Taxonomy Pages:
Working - /location/%LocationCountry%/
Working - /location/%LocationCountry%/%LocationCity%/
Post Type Pages:
Working - /stay-dive/%LocationCountry%/
Working - /stay-dive/%LocationCountry%/%LocationCity%/
Not Working /stay-dive/%LocationCountry%/%LocationCity%/Slug - Not Working
Working - /excursion/%LocationCountry%/
Not Working - /excursion/%LocationCountry%/%LocationCity%/
Not Working - /excursion/%LocationCountry%/%LocationCity%/Slug
Working - /excursion/%excursion-category%/
Not Working - /excursion/%excursion-category%/%LocationCountry%/
Not Working - /excursion/%excursion-category%/%LocationCountry%/%LocationCity%/
Taxonomy Code:
// excursion category
register_taxonomy('pdm_excursion_category', array('pdm_excursion'), array(
'labels' => array(
'name' => _x('Categories', 'taxonomy general name', 'pdm'),
'singular_name' => _x('Category', 'taxonomy singular name', 'pdm'),
'menu_name' => __('Categories', 'pdm'),
'all_items' => __('All Categories', 'pdm'),
'edit_item' => __('Edit Category', 'pdm'),
'view_item' => __('View Category', 'pdm'),
'update_item' => __('Update Category', 'pdm'),
'add_new_item' => __('Add New Category', 'pdm'),
'new_item_name' => __('New Category Name', 'pdm'),
'parent_item' => __('Parent Category', 'pdm'),
'parent_item_colon' => __('Parent Category:', 'pdm'),
'search_items' => __('Search Categories', 'pdm'),
'popular_items' => __('Popular Categories', 'pdm'),
'separate_items_with_commas' => __('Separate categories with commas', 'pdm'),
'add_or_remove_items' => __('Add or remove categories', 'pdm'),
'choose_from_most_used' => __('Choose from the most used categories', 'pdm'),
'not_found' => __('No categories found.', 'pdm'),
),
'show_ui' => true,
'show_admin_column' => true,
'hierarchical' => true,
'query_var' => true,
'rewrite' => array('slug' => 'excursions', 'with_front' => true, 'hierarchical' => false),
));
// location
register_taxonomy('pdm_location', array('pdm_hotel', 'pdm_excursion', 'pdm_dive_site'), array(
'labels' => array(
'name' => _x('Locations', 'taxonomy general name', 'pdm'),
'singular_name' => _x('Location', 'taxonomy singular name', 'pdm'),
'menu_name' => __('Locations', 'pdm'),
'all_items' => __('All Locations', 'pdm'),
'edit_item' => __('Edit Location', 'pdm'),
'view_item' => __('View Location', 'pdm'),
'update_item' => __('Update Location', 'pdm'),
'add_new_item' => __('Add New Location', 'pdm'),
'new_item_name' => __('New Location Name', 'pdm'),
'parent_item' => __('Parent Location', 'pdm'),
'parent_item_colon' => __('Parent Location:', 'pdm'),
'search_items' => __('Search Locations', 'pdm'),
'popular_items' => __('Popular Locations', 'pdm'),
'separate_items_with_commas' => __('Separate locations with commas', 'pdm'),
'add_or_remove_items' => __('Add or remove locations', 'pdm'),
'choose_from_most_used' => __('Choose from the most used locations', 'pdm'),
'not_found' => __('No locations found.', 'pdm'),
),
'show_ui' => true,
'show_admin_column' => true,
'hierarchical' => true,
'query_var' => true,
'rewrite' => array('slug' => 'location', 'with_front' => false, 'hierarchical' => true),
));
Post Type Code:
// hotel
register_post_type('pdm_hotel' , array(
'labels' => array(
'name' => _x('Stay & Dive Hotels', 'post type general name', 'pdm'),
'singular_name' => _x('Stay & Dive', 'post type singular name', 'pdm'),
'menu_name' => _x('Stay & Dive Hotels', 'admin menu', 'pdm'),
'name_admin_bar' => _x('Stay & Dive Hotel', 'add new on admin bar', 'pdm'),
'add_new' => _x('Add New', 'book', 'pdm'),
'add_new_item' => __('Add New Hotel', 'pdm'),
'new_item' => __('New Stay & Dive Hotel', 'pdm'),
'edit_item' => __('Edit Stay & Dive Hotel', 'pdm'),
'view_item' => __('View Stay & Dive Hotel', 'pdm'),
'all_items' => __('All Stay & Dive Hotels', 'pdm'),
'search_items' => __('Search Stay & Dive Hotels', 'pdm'),
'parent_item_colon' => __('Parent Stay & Dive Hotel:', 'pdm'),
'not_found' => __('No hotels found.', 'pdm'),
'not_found_in_trash' => __('No hotels found in Trash.', 'pdm'),
),
'public' => true,
'publicly_queryable' => true,
'show_ui' => true,
'show_in_menu' => true,
'query_var' => true,
'rewrite' => array('slug' => 'stay-dive/%pdm_location%/%pdm_location2%', 'with_front' => true),
'capability_type' => 'post',
'has_archive' => true,
'hierarchical' => false,
'menu_position' => 101,
'menu_icon' => 'dashicons-admin-home',
'supports' => array('title','editor','thumbnail'),
'taxonomies' => array('pdm_location'),
));
// excursions
register_post_type('pdm_excursion' , array(
'labels' => array(
'name' => _x('Excursions', 'post type general name', 'pdm'),
'singular_name' => _x('Excursion', 'post type singular name', 'pdm'),
'menu_name' => _x('Excursions', 'admin menu', 'pdm'),
'name_admin_bar' => _x('Excursion', 'add new on admin bar', 'pdm'),
'add_new' => _x('Add New', 'book', 'pdm'),
'add_new_item' => __('Add New Excursion', 'pdm'),
'new_item' => __('New Excursion', 'pdm'),
'edit_item' => __('Edit Excursion', 'pdm'),
'view_item' => __('View Excursion', 'pdm'),
'all_items' => __('All Excursions', 'pdm'),
'search_items' => __('Search Excursions', 'pdm'),
'parent_item_colon' => __('Parent Excursion:', 'pdm'),
'not_found' => __('No excursions found.', 'pdm'),
'not_found_in_trash' => __('No excursions found in Trash.', 'pdm'),
),
'public' => true,
'publicly_queryable' => true,
'show_ui' => true,
'show_in_menu' => true,
'query_var' => true,
'rewrite' => array('slug' => 'excursion /%pdm_excursion_category%/%pdm_location%/%pdm_location2%','with_front' => true),
'capability_type' => 'post',
'has_archive' => true,
'hierarchical' => false,
'menu_position' => 102,
'menu_icon' => 'dashicons-location',
'supports' => array('title','editor','thumbnail'),
'taxonomies' => array('pdm_location','pdm_excursion_category'),
));
Found a git repo that had the solution: https://github/hemal21/WordPress-CPT-Hierarchical-Taxonomy-Permalinks/blob/master/cpt-hierarchical-tax-permalinks.php
I needed do a rewrite, Here is an example of working pretty permalink code:
<?php
/**
* WordPress Custom Post Type and Hierarchical Taxonomy Permalinks
*
* Ever wanted to have custom taxonomy permalinks that reflect parent/child relation?
* Then this is for you. Still in development though.
*
* @version 1.0b
* @author Kenth Hagström
* @link http://www.facebook/keha76
* @license GNU General Public License v2
*
* Last updated 19 sep 2013, 15:30(GMT+1)
*
* KNOWN BUGS:
* - When a 'photo' is published without any taxonomy, the URL url.tld/photos/john does not work.
* - The full URL url.tld/photos/family/kids/john/ has an unwanted trailing slash.
*
* These URL structures work:
* url.tld/photos/ loads archive-photo.php
* url.tld/photos/family/ loads taxonomy-album.php
* url.tld/photos/family/kids/ loads taxonomy-album.php
* url.tld/photos/family/kids/john loads single-photo.php
* url.tld/photos/page/2 loads page 2 with archive-photo.php
*
* If you try this code, remember to flush your permalinks. Go to 'settings->permalinks' and it's done.
*
**/
function keha_register_photo_tax_album() {
$labels = array(
'name' => _x( 'Albums', 'taxonomy general name', 'blank' ),
'singular_name' => _x( 'Album', 'taxonomy singular name', 'blank' ),
'search_items' => __( 'Search Albums', 'blank' ),
'all_items' => __( 'All Albums', 'blank' ),
'parent_item' => __( 'Parent Album', 'blank' ),
'parent_item_colon' => __( 'Parent Album:', 'blank' ),
'edit_item' => __( 'Edit Album', 'blank' ),
'update_item' => __( 'Update Album', 'blank' ),
'add_new_item' => __( 'Add New Album', 'blank' ),
'new_item_name' => __( 'New Album Name', 'blank' ),
'menu_name' => __( 'Albums', 'blank' ),
);
$rewrite = array(
'slug' => 'album',
'with_front' => false,
'hierarchical' => true
);
$args = array(
'hierarchical' => true,
'labels' => $labels,
'show_ui' => true,
'query_var' => true,
'rewrite' => $rewrite
);
register_taxonomy( 'album', array( 'photo' ), $args );
}
add_action( 'init', 'keha_register_photo_tax_album' );
function keha_register_cpt_photo() {
$labels = array(
'name' => __( 'Photos', 'blank' ),
'singular_name' => _x( 'Photo', 'Photo post type singular', 'blank' ),
'add_new' => __( 'Add New', 'blank' ),
'add_new_item' => __( 'Add New Photo', 'blank' ),
'edit_item' => __( 'Edit Photo', 'blank' ),
'new_item' => __( 'New Photo', 'blank' ),
'all_items' => __( 'All Photos', 'blank' ),
'view_item' => __( 'View Photo', 'blank' ),
'search_items' => __( 'Search Photos', 'blank' ),
'not_found' => __( 'No photos found', 'blank' ),
'not_found_in_trash' => __( 'No photos found in Trash', 'blank' ),
'parent_item_colon' => '',
'menu_name' => __( 'Photos', 'blank' )
);
$rewrite = array(
'slug' => 'photos',
'hierarchical' => true,
'with_front' => true
);
$args = array(
'labels' => $labels,
'exclude_from_search' => false,
'publicly_queryable' => true,
'show_ui' => true,
'show_in_nav_menus' => true,
'show_in_menu' => true,
'show_in_admin_bar' => true,
'menu_position' => 20,
'menu_icon' => null, // Handled from CSS
'capability_type' => 'post',
'hierarchical' => false,
'public' => true,
'supports' => array( 'title', 'editor', 'thumbnail' ),
'taxonomies' => array( 'album' ),
'rewrite' => $rewrite,
'has_archive' => true,
'query_var' => true,
'can_export' => true
);
register_post_type( 'photo', $args );
}
add_action( 'init', 'keha_register_cpt_photo' );
function keha_add_rewrite_rules() {
add_rewrite_rule( '^photos/(.+?)/(.+?)/(.+?)$', 'index.php?album=$matches[1]&album=$matches[2]&photo=$matches[3]', 'top' );
add_rewrite_rule( '^photos/(.+?)/(.+?)/$', 'index.php?photo=$matches[2]', 'top' );
add_rewrite_rule( '^photos/(.+?)/(.+?)/(.+?)$', 'index.php?photo=$matches[3]', 'top' );
add_rewrite_rule( '^photos/(.+?)/(.+?)/?$', 'index.php?album=$matches[2]', 'top' );
add_rewrite_rule( '^photos/(.+?)$', 'index.php?album=$matches[1]', 'top' );
}
add_action('init', 'keha_add_rewrite_rules');
function keha_post_type_link( $post_link, $post, $leavename ) {
global $wp_rewrite;
$draft_or_pending = isset( $post->post_status ) && in_array( $post->post_status, array( 'draft', 'pending', 'auto-draft' ) );
if ( $draft_or_pending and !$leavename ) {
return $post_link;
}
if ( $post->post_type == 'photo' ) {
$post_type_object = get_post_type_object( $post->post_type );
$post_link = home_url() . '/' . $post_type_object->rewrite['slug'] . '/';
$parent_dirs = '';
if ( $terms = get_the_terms( $post->ID, 'album' ) ) {
foreach ( $terms as $term ) {
if ( $term->parent != 0 ) {
$dirs = keha_get_taxonomy_parents( $term->term_id, 'album', false, '/', true );
} else {
$dirs = $term->slug.'/';
}
}
}
$post_link = $post_link . $dirs . $post->post_name;
}
return $post_link;
}
add_filter( 'post_type_link', 'keha_post_type_link', 10, 3 );
/**
* Custom function based on WordPress own get_category_parents()
* @link http://core.trac.wordpress/browser/tags/3.6.1/wp-includes/category-template.php#L0
*
* @param integer $id
* @param string $taxonomy
* @param string $link
* @param string $separator
* @param string $nicename
* @param array $visited
* @return string
*/
function keha_get_taxonomy_parents( $id, $taxonomy = 'category', $link = false, $separator = '/', $nicename = false, $visited = array() ) {
$chain = '';
$parent = get_term( $id, $taxonomy, OBJECT, 'raw');
if ( is_wp_error( $parent ) ) {
return $parent;
}
if ( $nicename ){
$name = $parent->slug;
} else {
$name = $parent->name;
}
if ( $parent->parent && ( $parent->parent != $parent->term_id ) && !in_array( $parent->parent, $visited ) ) {
$visited[] = $parent->parent;
$chain .= keha_get_taxonomy_parents( $parent->parent, $taxonomy, $link, $separator, $nicename, $visited );
}
if ( $link ) {
$chain .= '<a href="' . get_term_link( $parent->term_id, $taxonomy ) . '" title="' . esc_attr( sprintf( __( "View all posts in %s" ), $parent->name ) ) . '">'.$name.'</a>' . $separator;
}else {
$chain .= $name.$separator;
}
return $chain;
}