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 :-)
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
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
? In the past I've only used the first one...
function my_em_rewrite_flush(){
global $wp_rewrite;
$wp_rewrite->flush_rules();
}
add_action('init','my_em_rewrite_flush');
Plugin: As I wrote, I'm not looking for a plugin-based solution... I realy NEED to hardcode the rules into my functions.php :-/