0

I've created a simple CPT named shop which contains some shop items. After that I've added category support to it which works fine ('taxonomies' => array( 'category' )). Then I created template-shop.php where I keep all my logic (I use template because I have multilingual support via polylang).

So lets say that currently my URL looks like: www.foo.com/shop

And now I want to "filter" my categories and I currently use the URL params to filter:

<?php 

  $current_url = strtok($_SERVER["REQUEST_URI"], '?');

  // if param category exists filter by it

  if (isset($_GET['category'])) {
    $category = $_GET['category'];

    $args = array (
      'post_type' => array( 'shop' ),
      'order' => 'ASC',
      'posts_per_page' => -1,
      'category_name' => $category
    );

  } else {

    $args = array (
      'post_type' => array( 'shop' ),
      'order' => 'ASC',
      'posts_per_page' => -1
    );

  }

  $shop = new WP_Query( $args );
?>

And it works fine

www.foo.com/shop/?category=bar
www.foo.com/shop/?category=baz
etc.

My question is - can I somehow "update" the logic so the URLs looks better? I'd wanted to for it to be:

www.foo.com/shop/category/bar
www.foo.com/shop/category/baz
etc.

Or something like that.

1

Option 1: Enable the post type archives and use a post type archive template.

  1. 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
        // ...
    ] );
    
  2. Then create a custom archive template just for that post type; i.e. archive-shop.php.

  3. And you can use add_rewrite_rule() to rewrite example.com/shop/category/<category slug> to example.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'
    );
    
  4. 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 custom WP_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.

  1. So firstly, get the ID of the page which is using the custom template (template-shop.php).

  2. You'd use add_rewrite_rule() to rewrite example.com/shop/category/<category slug> to example.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'
    );
    
  3. You'd use the query_vars hook to add the shop_cat variable to the public WordPress query vars:

    add_filter( 'query_vars', function ( $vars ) {
        $vars[] = 'shop_cat';
        return $vars;
    } );
    
  4. In template-shop.php or wherever necessary, just call get_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.

  • Tnx for the answer, will test it now. Just a question, will the rewrite work with polylang? – Vucko Oct 2 at 7:42
  • The rewrite rules in the answer should work, but only for the default language. How your Polylang URLs look - is it the default like example.com/en/my-post? Does the URL include language as in example.com/language/en/my-post? Do you want /en/shop/category/foo (note the /en/ - language path) or that you want /shop/category/foo (no language path) to show posts in any languages? – Sally CJ Oct 2 at 16:23

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.