Ask your WordPress questions! Pay money and get answers fast! Comodo Trusted Site Seal
Official PayPal Seal

(Hierarchical) Custom Post Type & Taxonomy Permalinks WordPress

  • SOLVED

Hello!

I've a custom post type 'books', a registered taxonomy 'book_taxonomy' and some trouble with my permalinks:

I'd like my permalinks for the cpt 'books' look like this:
<blockquote>
domain.com/baseurl/--> static page with custom template
domain.com/baseurl/[book genre]/[book name]/ --> book-article
domain.com/baseurl/[book genre]/ --> archive of all book-articles matching 'book genre'
</blockquote>

The Cherry on the cake would be a support of hierarchical taxonomies. E.g.:
<blockquote>
domain.com/baseurl/[book genre]/[book sub genre]/[book name]/ --> book-article
domain.com/baseurl/[book genre]/[book sub genre]/[...]/[book name]/ --> book-article
</blockquote>

After 'some' unsuccessful trial and error attempts based on snippets and tutorials I found on the web, I would be very thankfull for your help:-)

I think the only PHP you can use from my attempts, is this:
add_action('init', 'post_type_books');
function post_type_books() {
register_post_type('books',
array(
'labels' => array('name' => __('Books')),
'public' => true,
'capability_type' => 'post',
'has_archive' => true,
'hierarchical' => false,
'query_var' => true,
'rewrite' => array('with_front'=>false)
)
);
}

add_action( 'init', 'taxonomy_books', 0 );
function taxonomy_books() {
register_taxonomy ('book_taxonomy',array('books'),
array(
'labels' => array('name' => __('Genre')),
'show_ui' => true,
'hierarchical' => true,
'query_var' => true,
'rewrite' => false

)
);
}


Supplement: Well, I've forgot to say that I'm not looking for a plugin-based solution... I'd like to "hardcode" it to my functions.php :-)

Answers (2)

2012-06-16

Daniel Yoen answers:

try this plugin :

http://wordpress.org/extend/plugins/custom-post-type-ui/

hope this help :)


Daniel Yoen comments:

try this :

<?php
function swi_post_type_books()
{
register_post_type('books', array(
'labels' => array(
'name' => __('Books')
),
'public' => true,
'capability_type' => 'post',
'has_archive' => true,
'hierarchical' => false,
'query_var' => true,
'publicly_queryable' => true,
'query_var' => true,
'rewrite' => false
));
register_taxonomy('books_taxonomy', array(
'books'
), array(
'labels' => array(
'name' => __('Genre')
),
'show_ui' => true,
'hierarchical' => true,
'query_var' => true,
'rewrite' => false
));
}
function swi_custom_rewrite_post_type()
{
global $wp_rewrite;

add_rewrite_tag('%books_cat%', '([^/]+)');
add_rewrite_tag('%books_subcat%', '([^/]+)');
$books_structure = '/mybooks/%books_cat%/%books_subcat%/%books%';
$wp_rewrite->add_rewrite_tag("%books%", '([^/]+)', "books=");
$wp_rewrite->add_permastruct('books', $books_structure, false);
}
function swi_books_permalink($permalink, $post_id, $leavename)
{
$post = get_post($post_id);
$rewritecode = array(
'%year%',
'%monthnum%',
'%day%',
'%hour%',
'%minute%',
'%second%',
$leavename ? '' : '%postname%',
'%post_id%',
'%category%',
'%author%',
$leavename ? '' : '%pagename%',
'%books_cat%',
'%books_subcat%'
);
if ('' != $permalink && !in_array($post->post_status, array(
'draft',
'pending',
'auto-draft'
)))
{
$unixtime = strtotime($post->post_date);
$category = '';
if (strpos($permalink, '%category%') !== false)
{
$cats = get_the_category($post->ID);
if ($cats)
{
usort($cats, '_usort_terms_by_ID');
$category = $cats[0]->slug;
if ($parent = $cats[0]->parent)
$category = get_category_parents($parent, false, '/', true) . $category;
}
if (empty($category))
{
$default_category = get_category(get_option('default_category'));
$category = is_wp_error($default_category) ? '' : $default_category->slug;
}
}
$post_type = $post->post_type;
$books_cat = '';
$cat_parent = '';
if (strpos($permalink, '%books_cat') !== false)
{
$terms = wp_get_object_terms($post->ID, 'books_taxonomy');
if (!is_wp_error($terms) && !empty($terms))
{
foreach ($terms as $term)
{
if ($term->parent == 0)
{
$books_cat = $term->slug;
$cat_parent = $term->term_id;
break;
}
}
}
}
$books_subcat = '';
if (strpos($permalink, '%books_subcat%') !== false && !empty($cat_parent))
{
foreach ($terms as $term)
{
if ($term->parent == $cat_parent)
{
$books_subcat = $term->slug;
break;
}
}
}
$author = '';
if (strpos($permalink, '%author%') !== false)
{
$authordata = get_userdata($post->post_author);
$author = $authordata->user_nicename;
}
$date = explode(" ", date('Y m d H i s', $unixtime));
$rewritereplace = array(
$date[0],
$date[1],
$date[2],
$date[3],
$date[4],
$date[5],
$post->post_name,
$post->ID,
$category,
$author,
$post->post_name,
$books_cat,
$books_subcat
);
$permalink = str_replace($rewritecode, $rewritereplace, $permalink);
}
else
{
}
return $permalink;
}
add_action('init', 'swi_post_type_books');
add_action('init', 'swi_custom_rewrite_post_type');
add_filter('post_type_link', 'swi_books_permalink', 10, 3);
?>

hope this help :)


Daniel Yoen comments:

same code, better looks. :)


Daniel Yoen comments:

Sorry.


<?php
function swi_post_type_books()
{
register_post_type('books', array(
'labels' => array(
'name' => __('Books')
),
'public' => true,
'capability_type' => 'post',
'has_archive' => true,
'hierarchical' => false,
'query_var' => true,
'publicly_queryable' => true,
'query_var' => true,
'rewrite' => false
));
register_taxonomy('books_taxonomy', array(
'books'
), array(
'labels' => array(
'name' => __('Genre')
),
'show_ui' => true,
'hierarchical' => true,
'query_var' => true,
'rewrite' => false
));
}
function swi_custom_rewrite_post_type()
{
global $wp_rewrite;

add_rewrite_tag('%books_cat%', '([^/]+)');
add_rewrite_tag('%books_subcat%', '([^/]+)');
$books_structure = '/mybooks/%books_cat%/%books_subcat%/%books%';
$wp_rewrite->add_rewrite_tag("%books%", '([^/]+)', "books=");
$wp_rewrite->add_permastruct('books', $books_structure, false);
}
function swi_books_permalink($permalink, $post_id, $leavename)
{
$post = get_post($post_id);
$rewritecode = array(
'%year%',
'%monthnum%',
'%day%',
'%hour%',
'%minute%',
'%second%',
$leavename ? '' : '%postname%',
'%post_id%',
'%category%',
'%author%',
$leavename ? '' : '%pagename%',
'%books_cat%',
'%books_subcat%'
);
if ('' != $permalink && !in_array($post->post_status, array(
'draft',
'pending',
'auto-draft'
)))
{
$unixtime = strtotime($post->post_date);
$category = '';
if (strpos($permalink, '%category%') !== false)
{
$cats = get_the_category($post->ID);
if ($cats)
{
usort($cats, '_usort_terms_by_ID');
$category = $cats[0]->slug;
if ($parent = $cats[0]->parent)
$category = get_category_parents($parent, false, '/', true) . $category;
}
if (empty($category))
{
$default_category = get_category(get_option('default_category'));
$category = is_wp_error($default_category) ? '' : $default_category->slug;
}
}
$post_type = $post->post_type;
$books_cat = '';
$cat_parent = '';
if (strpos($permalink, '%books_cat') !== false)
{
$terms = wp_get_object_terms($post->ID, 'books_taxonomy');
if (!is_wp_error($terms) && !empty($terms))
{
foreach ($terms as $term)
{
if ($term->parent == 0)
{
$books_cat = $term->slug;
$cat_parent = $term->term_id;
break;
}
}
}
}
$books_subcat = '';
if (strpos($permalink, '%books_subcat%') !== false && !empty($cat_parent))
{
foreach ($terms as $term)
{
if ($term->parent == $cat_parent)
{
$books_subcat = $term->slug;
break;
}
}
}
$author = '';
if (strpos($permalink, '%author%') !== false)
{
$authordata = get_userdata($post->post_author);
$author = $authordata->user_nicename;
}
$date = explode(" ", date('Y m d H i s', $unixtime));
$rewritereplace = array(
$date[0],
$date[1],
$date[2],
$date[3],
$date[4],
$date[5],
$post->post_name,
$post->ID,
$category,
$author,
$post->post_name,
$books_cat,
$books_subcat
);
$permalink = str_replace($rewritecode, $rewritereplace, $permalink);
}
else
{
}
return $permalink;
}
add_action('init', 'swi_post_type_books');
add_action('init', 'swi_custom_rewrite_post_type');
add_filter('post_type_link', 'swi_books_permalink', 10, 3);
?>


mark comments:

Hey Daniel,

Wow, what a monster*... and a realy good start! :-) :-) :-)
- this code works perfectly for cpt posts with extactly two selected hierachical (sub)categories :-)

but...
- it doesn't work if only a maincategory is selected (or the category-depth is deeper then 2)
- it doesn't show archive-pages for links like
<blockquote>domain.com/mybooks/[book genre]/ --> archive of all book-articles matching 'book genre'</blockquote>
Any Ideas how we can achieve that? This last point is very important for me. Currently the homepage shows up when requesting an URL like "mysite.com/mybooks/sciencefiction/", but I'd like to see an achive-page of all scifi-books (shown by the matching template 'achive-books.php')


*) I removed all the unnecessary code and 'slimmed' it a little bit (still works like the code above):
function swi_post_type_books(){
register_post_type('books', array(
'labels' => array(
'name' => __('Books')
),
'public' => true,
'capability_type' => 'post',
'has_archive' => true,
'hierarchical' => false,
'query_var' => true,
'publicly_queryable' => true,
'query_var' => true,
'rewrite' => false
)
);

register_taxonomy('books_taxonomy', array('books'),
array(
'labels' => array(
'name' => __('Genre')
),
'show_ui' => true,
'hierarchical' => true,
'query_var' => true,
'rewrite' => false
)
);
}

function swi_custom_rewrite_post_type(){
global $wp_rewrite;
add_rewrite_tag('%books_cat%', '([^/]+)');
add_rewrite_tag('%books_subcat%', '([^/]+)');
$books_structure = '/mybooks/%books_cat%/%books_subcat%/%books%';
$wp_rewrite->add_rewrite_tag("%books%", '([^/]+)', "books=");
$wp_rewrite->add_permastruct('books', $books_structure, false);
}


function swi_books_permalink($permalink, $post_id, $leavename){
$post = get_post($post_id);

$rewritecode = array(
'%books_cat%',
'%books_subcat%'
);

if ('' != $permalink && !in_array($post->post_status, array('draft','pending','auto-draft'))){
$books_cat = '';
$cat_parent = '';

if (strpos($permalink, '%books_cat') !== false){
$terms = wp_get_object_terms($post->ID, 'books_taxonomy');
if (!is_wp_error($terms) && !empty($terms)) {
foreach ($terms as $term){
if ($term->parent == 0) {
$books_cat = $term->slug;
$cat_parent = $term->term_id;
break;
}
}
}
}

$books_subcat = '';
if (strpos($permalink, '%books_subcat%') !== false && !empty($cat_parent)){
foreach ($terms as $term){
if ($term->parent == $cat_parent){
$books_subcat = $term->slug;
break;
}
}
}

$rewritereplace = array(
$books_cat,
$books_subcat
);

$permalink = str_replace($rewritecode, $rewritereplace, $permalink);

}

return $permalink;
}

add_action('init', 'swi_post_type_books');
add_action('init', 'swi_custom_rewrite_post_type');
add_filter('post_type_link', 'swi_books_permalink', 10, 3);


I'll have to say: I had no idea that all this might be that complicated! :)


Daniel Yoen comments:

Hello, Try this :)

<?php
function swi_post_type_books()
{
register_post_type('books', array(
'labels' => array(
'name' => __('Books')
),
'public' => true,
'capability_type' => 'post',
'has_archive' => true,
'hierarchical' => true,
'query_var' => true,
'publicly_queryable' => true,
'query_var' => true,
'rewrite' => array('slug' => '/mybooks/%cat%/%subcat%', 'with_front' => true)
));
register_taxonomy('mybooks', array('books'
), array(
'labels' => array(
'name' => __('Genre')
),
'show_ui' => true,
'hierarchical' => true,
'query_var' => true,
'rewrite' => true
));

global $wp_rewrite;

add_rewrite_tag('%cat%', '([^/]+)');
add_rewrite_tag('%subcat%', '([^/]+)');
add_rewrite_tag("%books%", '([^/]+)', "books=");
$wp_rewrite->add_permastruct('books', '/mybooks/%cat%/%subcat%/%books%', false);
}
add_action('init', 'swi_post_type_books');

function swi_books_permalink($permalink, $post_id, $leavename)
{
$post = get_post($post_id);
$rewritecode = array('%cat%', '%subcat%');
if ('' != $permalink && !in_array($post->post_status, array('draft', 'pending', 'auto-draft')))
{
$post_type = $post->post_type;
$cat = '';
$cat_parent = '';
if (strpos($permalink, '%cat%') !== false)
{
$terms = wp_get_object_terms($post->ID, 'mybooks');
if (!is_wp_error($terms) && !empty($terms))
{
foreach ($terms as $term)
{
if ($term->parent == 0)
{
$cat = $term->slug;
$cat_parent = $term->term_id;
break;
}
}
}
}
$subcat = '';
if (strpos($permalink, '%subcat%') !== false && !empty($cat_parent))
{
foreach ($terms as $term)
{
if ($term->parent == $cat_parent)
{
$subcat = $term->slug;
break;
}
}
}
$rewritereplace = array($cat, $subcat);
$permalink = str_replace($rewritecode, $rewritereplace, $permalink);
}
else
{
}
return $permalink;
}
add_filter('post_type_link', 'swi_books_permalink', 10, 3);

function swi_rewrite_flush()
{
global $wp_rewrite;
$wp_rewrite->flush_rules();
}
add_action('init','swi_rewrite_flush');
?>


Hope this help :)


mark comments:

Hey Daniel,
sorry to say, but: no effect :-/

The code isn't able to "decide" whether to call an archive OR an post...

As I read the php the book-permalink structure is still "static" (/mybooks/%cat%/%subcat%). So it works only if exactly 2 hierachical categories are selected:-(

I think we need somthing more flexible, maybe something completly different?

Hm...


Daniel Yoen comments:

I've tested it and succeeded in wordpress 3.4

/mybooks/%cat%/%subcat%/%postname%/ | works
/mybooks/%cat%/ | works

In my view. :D


mark comments:

hey Daniel,

I just send you an message with the live-example.. it realy doesn't work... :-( and if it would, I would wonder why?
I can't see a function/ section which could do what I've requested... maybe I'm wrong.. but could you please explain how an archive will be displayed by the code posted above? :-)

Thanks
:-))


mark comments:

Hey Daniel,

thanks again. The new code works fine for me - no sub-(sub...)-categories, but I can live with that at the moment.

Thanks a lot!
Mark

2012-06-16

Arnav Joy answers:

try this

add_action('init', 'post_type_books');

function post_type_books() {

register_post_type('books',

array(

'labels' => array('name' => __('Books')),

'public' => true,

'capability_type' => 'post',

'has_archive' => true,

'hierarchical' => false,

'query_var' => true,

'rewrite' => array('with_front'=>false)

)

);

}



add_action( 'init', 'taxonomy_books', 0 );

function taxonomy_books() {

register_taxonomy ('book_taxonomy',array('books'),

array(

'labels' => array('name' => __('Genre')),

'show_ui' => true,

'hierarchical' => true,

'query_var' => true,

'rewrite' => true



)

);

}


mark comments:

Well, no: that doesn't have any effect... I think it's not that simple:-(
I'm sure we'll have to add a filter with some new rewrite rules for the cpt. But I don't know how this filter should look like...


Arnav Joy comments:

write this after the above code in functions.php

flush_rewrite_rules();


Arnav Joy comments:

or try this

function my_em_rewrite_flush(){

global $wp_rewrite;

$wp_rewrite->flush_rules();

}

add_action('init','my_em_rewrite_flush');


Arnav Joy comments:

try this

add_action( 'init', 'taxonomy_books', 0 );

function taxonomy_books() {

register_taxonomy ('book_taxonomy',array('books'),

array(

'labels' => array('name' => __('Genre')),

'show_ui' => true,

'hierarchical' => true,

'query_var' => true,

'rewrite' => array( 'slug' => 'books/type' ),



)

);

}


Arnav Joy comments:

you can check this plugin

http://wordpress.org/extend/plugins/permalinks-for-hierarchical-custom-post-types/


mark comments:

Hey Arnav,

thanks for your help, but I think this isn't the way we'll get it to work :-) I'm quite sure we'll HAVE to set up and add some specific rewrite rules.
Somthink like this or maybe not:

global $wp_rewrite;
$book_structure = '/mybooks/%book_taxonomy%/%books%';
$wp_rewrite->add_rewrite_tag("%books%", '([^/]+)', "books=");
$wp_rewrite->add_permastruct('books', $book_structure, false);


And we'll have to add a filter to translate the permalink tags. Somthink like that could be found here:
http://shibashake.com/wordpress-theme/custom-post-type-permalinks-part-2
But as I said: I already worked on an trial and error 'method' on this for 'some' hours and couldn't get it working.


Flushing the rules without defining new ones can not have any effect - am I wrong?

BTW, another question: Is there any difference between flush_rewrite_rules(); and
function my_em_rewrite_flush(){
global $wp_rewrite;
$wp_rewrite->flush_rules();
}
add_action('init','my_em_rewrite_flush');
? In the past I've only used the first one...


Plugin: As I wrote, I'm not looking for a plugin-based solution... I realy NEED to hardcode the rules into my functions.php :-/