9

There is earlier post with similar title, but it does not look in to the WordPress 3.3, and that is important as 3.3 advertises interestingly: "Use the postname permalink structure without a performance penalty"

Problem with Wordpress 3.2 and earlier was that first it looked page names, and then 404. It didn't check the arbitrary post types first. 3.3 on the other hand must look post types, then pages, and finally 404 (as it advertises this feature). This implies that custom post types without slug should be simple, if they didn't hard code post_type=post somewhere.

I can't find a 3.3 specific solution yet though.

Question: How can I define permalink struct "/%postname%/" for any given custom post type "xyz"?

Thanks.

  • I don't see a question - what are you actually asking? – Travis Northcutt Jan 5 '12 at 14:37
  • For clarity, you want to define a new custom post type that uses the permalink structure of /%postname%/? Are you planning on having posts also use this same permastructure, or will they have a prefix? – prettyboymp Jan 6 '12 at 19:34
  • Following this to see if anyone comes up with an answer. I've tried the above approaches too along with simply setting the rewrite slug to '/' which also breaks page permalinks. Le sigh... – user16458 May 25 '12 at 3:19
2

This isn't easily done in WP 3.3 unless you trick the rewrite rules to be in the correct spot and make wp_rewrite think verbose rules are being used in the front end. The class below works.

class Test_Post_Type {
    const POST_TYPE = 'test';

    public static function init() {
        global $wp_rewrite;

        $post_type_obj = register_post_type( self::POST_TYPE, array(
            'labels' => array(
                'name' => __( 'Tests' ),
                'singular_name' => __( 'Test' ),
                'add_new' => __( 'Add New' ),
                'add_new_item' => __( 'Add New Test' ),
                'edit_item' => __( 'Edit Test' ),
                'new_item' => __( 'New Test' ),
                'all_items' => __( 'All Tests' ),
                'view_item' => __( 'View Test' ),
                'search_items' => __( 'Search Tests' ),
                'not_found' => __( 'No Tests found' ),
                'not_found_in_trash' => __( 'No Tests found in Trash' ),
                'menu_name' => __( 'Tests' )
            ),
            'publicly_queryable' => true,
            'exclude_from_search' => true,
            'hierarchical' => false,
            'public' => true,
            'rewrite' => false,
            'has_archive' => true,
            'supports' => array( 'title', 'editor', 'thumbnail', 'test_source' ),
            'taxonomies' => array( 'category', 'post_tag' ),
        ) );

        $post_type_obj = get_post_type_object(self::POST_TYPE);

        //register the rewrite tag for permalink building
        $wp_rewrite->add_rewrite_tag( '%' . $post_type_obj->query_var . '%', '([^/]+)', $post_type_obj->query_var . '=' );

        //we have to add the permastruct here in order to build the permalink, otherwise we'll need to filter the post_type link
        add_permastruct(self::POST_TYPE, '%' . $post_type_obj->query_var . '%/', false );

        //add a filter to remove the permastructs generated above
        add_filter(self::POST_TYPE . '_rewrite_rules', array(__CLASS__, '_remove_default_rules')); 

        //now we add a filter to put the generated rewrite rules in the correct spot
        add_action('generate_rewrite_rules', array(__CLASS__, '_filter_rewrite_rules'));

        if(!is_admin()) {
            //we need verbose_page_rules to be on on the front end in order for pages to be process properly
            $wp_rewrite->use_verbose_page_rules = true;
        }
    }

    /**
     * Filter to remove the rules for this post type when they're automatically generated due to the permastruct registration
     * @param type $rules
     * @return type 
     */
    public static function _remove_default_rules($rules) {
        return array();
    }

    /**
     * Filters the rules at the end to add back the ones for this post type at the bottom
     * @param WP_Rewrite $wp_rewrite 
     */
    public static function _filter_rewrite_rules($wp_rewrite) {
        $post_type_obj = get_post_type_object(self::POST_TYPE);
        $my_rules = $wp_rewrite->generate_rewrite_rules('%' . $post_type_obj->query_var . '%', EP_NONE);
        $wp_rewrite->rules += $my_rules;
    }

}

add_action( 'init', array( 'Test_Post_Type', 'init' ) );
  • Copy pasted this code in me theme, flushed rewrite rules. Added a new post, viewed the post (url is correct), got a 404... On WP 3.3.1. Any idea why this won't work for me? (Thanks for the code btw!) – Rob Vermeer Jan 6 '12 at 22:52
  • EP_NONE -> EP_PERMALINK to get comment pages working, and then to get multiple post types working with /%postname%/ you must also use parse_query filter. See my answer above. – Ciantic Jan 7 '12 at 12:44
  • Rob, did you add a new post or add a new 'test' post? This wasn't clear in the original question of whether posts needed to also have the permastructure of /%post_name%/. If that is the case why even create a new post type? Also, you'll have potential issues with name conflicts if more than one post type has the same permastructure. – prettyboymp Jan 9 '12 at 13:24
1

Holy car keys!

I think this works. It almost works, it's super simple, only one line:

global $wp_rewrite;
$args = array(
    'public' => true,
    'publicly_queryable' => true,
    'show_ui' => true,
    'show_in_menu' => true,
    'query_var' => true,
    'rewrite' => array('slug' => 'anything'),
    'capability_type' => 'post',
    'has_archive' => true,
    'hierarchical' => false,
    'menu_position' => null,
    'supports' => array('title','editor','thumbnail')
);
register_post_type('my_custom_post_type', $args);

$wp_rewrite->add_permastruct('my_custom_post_type', "%my_custom_post_type%");

P.S. If you try this at home, after adding this one line Go to "Settings" -> "Permalinks" and Save Changes, it refreshes the permalinks.

I was reading the WP register_post_type() source code and found a line:

$wp_rewrite->add_permastruct($post_type, "{$args->rewrite['slug']}/%$post_type%", $args->rewrite['with_front'], $args->permalink_epmask);

Needless to say but without slug I concluded it should work, and it did. Even the permalink editing underneath the title in editor works correctly!

Update: This breaks page permalinks, back to drawing board...

  • I also tried this one, with similar outcome. Would be very cool if this will work. Maybe someone else with an idea? – Rob Vermeer Jan 6 '12 at 11:51
  • @RobVermeer Noticed that without the line (with just default slug), the WordPress is already capable of redirecting to the url. E.g. "some-post" redirects to "anything/some-post". In codespeak, somewhere in the code there is a support for CPT's without slug it just defaults to redirect. face palm – Ciantic Jan 6 '12 at 12:26
1

prettyboymp answer is almost the same I got yesterday, but I'm not happy with it. prettyboymp's answer has a one flaw, it does not work when /%postname%/ is being used simultaenously on multiple post types.

Here is my answer, which also looks in to current structure, and creates array of post types to fallback on. There is one flaw in this too though, if two post types have same slug and both are /%postname%/ then it shows both.

class MyCustomPostType {
    /**
     * Register post type
     **/
    public static function register_post_type() {
        global $wp_rewrite;

        $args = array(
            'public' => true,
            'publicly_queryable' => true,
            'show_ui' => true,
            'show_in_menu' => true,
            'query_var' => true,
            'rewrite' => false,
            'capability_type' => 'post',
            'has_archive' => true,
            'hierarchical' => false,
            'menu_position' => null,
            'supports' => array('title','editor','thumbnail')
        );

        register_post_type('my_custom_post_type', $args);

        // Enables the pages to work simultaneously
        $wp_rewrite->use_verbose_page_rules = true;
        add_filter("rewrite_rules_array", array(__CLASS__, 'rewrite_rules_array'));
        add_action("parse_query", array(__CLASS__, 'parse_query'));
        add_filter("post_type_link", array(__CLASS__, 'post_type_link'), 1, 4);
    }

    public static function post_type_link($link, $post, $leavename=false, $sample=false) {
        if ($sample && ($begin = strpos($link, "?my_custom_post_type=")) !== false) {
            return substr($link, 0, $begin-1) . "/%my_custom_post_type%/";
        }
        return str_replace("?my_custom_post_type=", "", $link) . "/";
    }

    public static function parse_query($query) {
        global $wp, $wp_rewrite;

        // Is this query for /%post_name%/? Is it main request query?
        if (isset($query->query['name'])
            && substr($wp->matched_rule, 0, 7) == "([^/]+)"
            && isset($query->query)
            && isset($wp->query_vars)
            && $query->query == $wp->query_vars)
        {
            //echo '<p><h1>hit!</h1></p>';
            if (!($post_types = get_query_var("post_type"))) {
                if ($wp_rewrite->permalink_structure == "/%postname%/")
                    $post_types = array("post");
                else
                    $post_types = array();
            }

            if (is_array($post_types))
                $post_types[] = "my_custom_post_type";

            set_query_var("post_type", $post_types);
            //set_query_var("posts_per_page", 1);
        }
    }

    public static function rewrite_rules_array($array) {
        global $wp_rewrite;
        // Same rules as in /%post_name%/
        return array_merge($array, $wp_rewrite->generate_rewrite_rules("/%postname%/", EP_PERMALINK));
    }
}


add_action('init', array("MyCustomPostType", "register_post_type"));
  • Is it possible that certain post-types get hierarchical. I tried it myself, but nothing seems to work... It thinks the post is an attachment with parent/child/... And if I do parent/child/grandchild/ it gets a 404. – Rob Vermeer Jan 12 '12 at 16:26
1

I created a solution and i couldnt find a problem with it. Please try and tell me if you find a problem

add_action('init', 'firmasite_resimlitarif_cpt', 0);
function firmasite_resimlitarif_cpt() 
{

// Yemek Tarifi

  $args = array(
    'public' => true,
    'show_in_menu' => true, 
    'permalink_epmask' => EP_NONE,
    'rewrite' => array('slug'=>'/','with_front'=>false),
    'has_archive' => false,
    'supports' => array('title','editor','thumbnail')
  ); 
  register_post_type('yemek',$args);

}


// http://wordpress.stackexchange.com/questions/37650/wordpress-3-3-custom-post-type-with-postname-permastruct
add_action("parse_query", 'firmasite_resimlitarif_parse_query');
function firmasite_resimlitarif_parse_query($query) {
    global $wp, $wp_rewrite;


    // Is this query for /%post_name%/? Is it main request query?
    if (isset($query->query['name'])
        && substr($wp->matched_rule, 0, 7) == "([^/]+)"
        && isset($query->query)
        && isset($wp->query_vars)
        && $query->query == $wp->query_vars)
    {
        if (!($post_types = get_query_var("post_type"))) {
            if ($wp_rewrite->permalink_structure == "/%postname%/")
                $post_types = array("post");
            else
                $post_types = array();
        }

        if (is_array($post_types)){ 
            $post_types[] = 'yemek';
            $post_types[] = 'page';
        }


        set_query_var("post_type", $post_types);
    } 
}

Change 'yemek' with your post type name.

0

This link should answer your question:

http://ottopress.com/2011/how-the-postname-permalinks-in-wordpress-3-3-work/

  • No it does not, it only explains how it works in terms of SQL. But no mention of custom post types, which is the problem. – Ciantic Jan 6 '12 at 9:24
0

The cleanest answer I could come up with for this (I'm building out a plugin that really needs a custom post type without any leading slug) is to use a custom page template instead of using a custom post type.

By doing this, your "custom post type" can have urls such as /whatever without having to worry about stepping on page or post permalinks.

To do this, I ended up doing the following:

  • Adding a custom page template inside of my plugin
  • Setting up the page template so that it could be selected in the page editor
  • Creating custom meta boxes that only show up for my page template

This allowed me to:

The down sides

Of course, while this does not stomp on page or post links, it does have a couple of obvious downsides.

No archive You won't have an archive (if you want such), though that can be solved by creating another page template to draw an archive of all pages using your custom template.

Managed under Pages You don't get the nice left hand navigation in the admin that groups all of the post type together.

This could be partially solved by adding a filter to the page list (to let you filter by the page template being used), showing any page template used in a new column, etc.


That being said, I wanted something that would not cause users to wonder why they created a new custom page and found that they could either no longer reach normal pages or the new custom page caused an existing page on their site to disappear.

I know it isn't a real solution, but it is an alternative that worked great for my needs.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.