最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

permalinks - Order custom post type posts by custom field with pretty URL

programmeradmin4浏览0评论
  • I have a custom post type, let's call it my_post_type

  • It has an archive at /my_post_type/

  • I'd like to create a pretty URL that behaves like the archive page, but orders these posts by a custom field value (i.e. /my-post-type/ranked/). It should also include pagination (i.e. /my-post-type/ranked/2)

  • I understand I can use these query parameters:

    'post_type' => 'my_post_type',
    'meta_key' => 'rating',
    'orderby'   => 'meta_value_num',
    'order' => 'ASC',
    
  • But I'm a little confused on how to register the /my-post-type/ranked/ URL (without creating an individual post type and assigning it a template).


I tried using the following:

function rewriteOrderByRating() {
    add_rewrite_rule('^my_post_type/ranked/?', 'index.php?post_type=my_post_type&meta_key=rating&orderby=meta_value_num&order=ASC', 'top');
}
add_action('init', 'rewriteOrderByRating', 10, 0);

... but I believe there are two issues with it:

  1. The meta_key=rating and orderby=meta_value_num don't seem to affect the query. I've also tried orderby=meta_value. The rating custom field is stored as a string and I don't know if that would cause issues.

  2. The code above doesn't account for pagination (my regex is poor). Should I just account for pagination with an additional rewrite?

  • I have a custom post type, let's call it my_post_type

  • It has an archive at /my_post_type/

  • I'd like to create a pretty URL that behaves like the archive page, but orders these posts by a custom field value (i.e. /my-post-type/ranked/). It should also include pagination (i.e. /my-post-type/ranked/2)

  • I understand I can use these query parameters:

    'post_type' => 'my_post_type',
    'meta_key' => 'rating',
    'orderby'   => 'meta_value_num',
    'order' => 'ASC',
    
  • But I'm a little confused on how to register the /my-post-type/ranked/ URL (without creating an individual post type and assigning it a template).


I tried using the following:

function rewriteOrderByRating() {
    add_rewrite_rule('^my_post_type/ranked/?', 'index.php?post_type=my_post_type&meta_key=rating&orderby=meta_value_num&order=ASC', 'top');
}
add_action('init', 'rewriteOrderByRating', 10, 0);

... but I believe there are two issues with it:

  1. The meta_key=rating and orderby=meta_value_num don't seem to affect the query. I've also tried orderby=meta_value. The rating custom field is stored as a string and I don't know if that would cause issues.

  2. The code above doesn't account for pagination (my regex is poor). Should I just account for pagination with an additional rewrite?

Share Improve this question asked Jan 22, 2020 at 16:45 JonnyJonny 1456 bronze badges 3
  • You do need to create an individual post type with the "ranked" slug. You don't necessarily have to give it a custom template - you could use a pre_get_posts filter to alter the query instead. But as you mentioned, the meta value being stored as a string may be an issue - I would try to use pre_get_posts on the main archive working first, and once you determine whether it's possible to order by that field as desired, then you can build the new archive. – WebElaine Commented Jan 22, 2020 at 16:52
  • Was the 'you do need to create an individual post type with the "ranked" slug' a typo? It just feels odd to have to create another post type for this. – Jonny Commented Jan 22, 2020 at 17:55
  • No - I meant you should create a single post of your custom type, with the slug being "ranked." No separate CPT - just an individual instance of your existing CPT. – WebElaine Commented Jan 22, 2020 at 20:25
Add a comment  | 

1 Answer 1

Reset to default 1

This is possible, if a little involved. To achieve this you will need:

  • A custom query var.
  • A filter that sorts results based on the query var.
  • A rewrite rule for the archive that applies the query var for your pretty URL.
  • Another rewrite rule for the same thing, but including pagination.
  • Check for the query var to apply changes to any other parts of the page that require it.

So the first thing to do is add the query var. This means that the value of a ?ranked=1 query parameter added to the URL will be passed to the main WP_Query object.

add_filter(
    'query_vars',
    function( $query_vars ) {
        $query_vars[] = 'ranked';

        return $query_vars;
    }
);

Then we can check for that value inside the pre_get_posts hook, and if it's present, set the main query to order posts the way we want:

add_action(
    'pre_get_posts',
    function( WP_Query $query ) {
        if ( $query->is_post_type_archive( 'my_post_type' ) ) {
            if ( $query->get( 'ranked', false ) ) {
                $query->set( 'meta_key', 'rating' );
                $query->set( 'orderby', 'meta_value_num' );
            }
        }
    }
);

This much will give you the functionality you want, but with an ugly URL. Accessing /my_post_type/?ranked=1 will display the results formatted the way you want, and the pagination links should automatically keep the ranked parameter, so pagination should work.

The next step is adding the rewrite rules. You'll need two. One for the first page, and then one for the paginated pages. You can just take the default rewrite rules for the post type archive, and add ranked:

add_action(
    'init',
    function() {
        add_rewrite_rule( 'my_post_type/ranked/?$', 'index.php?post_type=my_post_type&ranked=1', 'top' );
        add_rewrite_rule( 'my_post_type/ranked/page/([0-9]{1,})/?$', 'index.php?post_type=my_post_type&ranked=1&paged=$matches[1]', 'top' );
    }
);

Now you will be able to load ranked posts at /my_post_type/ranked/ and /my_post_type/ranked/page/2. The default output of paginate_links() will automatically give you the correct links to paginate through ranked results.

So the above 3 blocks of code will give you the functionality you want. If you need to check whether you're viewing the ranked view within other fitlers or actions you can use this condition:

if ( get_query_var( 'ranked', false ) ) {

}

For example, you might want to filter the <title></title> tag of the page if the ranked view is loaded to add "Top Rated". You would do that like this (as long as your theme supports title-tag):

add_filter(
    'document_title_parts',
    function( $title_parts ) {
        if ( is_post_type_archive( 'my_post_type' ) ) {
            if ( get_query_var( 'ranked', false ) ) {
                $title_parts['title'] = 'Top Rated ' . $title_parts['title'];
            }
        }

        return $title_parts;
    }
);
发布评论

评论列表(0)

  1. 暂无评论