Option 1: Enable the post type archives and use a post type archive template.
-
Enable the archives; e.g. when registering the post type using
register_post_type()
:register_post_type( 'shop', [ 'has_archive' => 'shop', // the archives are accessible at example.com/shop // ... ] );
-
Then create a custom archive template just for that post type; i.e.
archive-shop.php
. -
And you can use
add_rewrite_rule()
to rewriteexample.com/shop/category/<category slug>
toexample.com/?category_name=<category slug>&post_type=shop
:// This should be called in `init`. add_rewrite_rule( '^shop/category/([^/]+)/?$', 'index.php?category_name=$matches[1]&post_type=shop', 'top' );
-
You’d need to use the
pre_get_posts
hook to filter the main WordPress query (since I could see you’re using some custom query vars/values). But in the archive template, you wouldn’t need the customWP_Query
query (i.e.$shop = new WP_Query
).
Option 2: If you need to use a custom Page which displays posts in the custom post type.
And by “custom Page”, I’m referring to a post having the type page
. Also, the advantage of using this option is that you don’t need to enable the default post type archives.
-
So firstly, get the ID of the page which is using the custom template (
template-shop.php
). -
You’d use
add_rewrite_rule()
to rewriteexample.com/shop/category/<category slug>
toexample.com/?shop_cat=<category slug>&page_id=<page ID>
— “shop_cat” can be changed to other non-reserved name, but you’ll also need to change the name in step #3 & #4 below:// This should be called in `init`. add_rewrite_rule( '^shop/category/([^/]+)/?$', // Make sure to use the correct Page ID. 'index.php?shop_cat=$matches[1]&page_id=2', 'top' );
-
You’d use the
query_vars
hook to add theshop_cat
variable to the public WordPress query vars:add_filter( 'query_vars', function ( $vars ) { $vars[] = 'shop_cat'; return $vars; } );
-
In
template-shop.php
or wherever necessary, just callget_query_var( 'shop_cat' )
to retrieve the category slug from the URL:$args = array( 'post_type' => array( 'shop' ), 'order' => 'ASC', 'posts_per_page' => -1, ); if ( $category = get_query_var( 'shop_cat' ) ) { $args['category_name'] = $category; } $shop = new WP_Query( $args );
Last but not least, there are other variations to both the above options, but the ones I provided should help you get started.