Custom Taxonomy with two Custom Post Types, permalinks problem

I never used CPTs and Taxonomies on my wordpress themes (i’m still a newbie student) but with the latest project i’m working on (nothing commercial), I wanted to do something more and test the power of custom post types and custom taxonomies.

What I want to achieve?

In this test, I want to try to create a simple e-commerce style site, but without the use of woocommerce. Why? Because it’s not a really e-commerce, but it’s just a reviews and “top 10” style site.

To do so, I created two custom post types:

  1. Reviews CPT: for all the reviews with custom fields like “personal score” (the x/5 stars), “external link” to buy the product on manufacturer’s site, …
  2. Tops CPT: far all the “Top 5…” or “Top 10…”, that can be linked to the reviews CPT to get the reviews custom fields on tables.

After that, I created a custom taxonomy “Gear”, a single category for my 2 CPTs, with category classes like “headphones” or “cameras” etc.

More precisely, I want a structure like this:

  1. “.com/gear” : this is a page with all the two CPTs posts (displayed with 2 loops)
  2. “.com/gear/type_of_product”: the same page for the gear url above, but filtered with all the CPTs posts with the category selected (for example, all the headphones reviews and top style posts)
  3. “.com/gear/type_of_product/CPT_name”: still the same page above and above, but filtered with just the CPT type of post (for example, “.com/gear/headphones/reviews” will display all the reviews for the headphones category)
  4. “.com/gear/type_of_product/CPT_name/post”: this is another page, with just the post (for example, the single review)

To achieve this:

  1. the “gear” part of the url is the category_base displayed by wordpress for its categories, like “.com/category”.
  2. the “type_of_product” is the taxonomy name, like headphones or cameras.
  3. the “CPT_name” is the custom post type name, like “reviews”.
  4. the “post” is the name of the post
  5. a complete example: the post “product A” review inside the reviews CPT with the taxonomy name “headphones” of the taxonomy base “gear” will have this url – .com/gear/headphones/reviews/product_A.

The code

I started by writing a plugin for the two CPTs and the single taxonomy, here is the code:

The Review CPT:

function cpt_reviews(){

  $labels = array(
    'name' => __('Reviews', 'fv-cpt'),
    'singular_name' => __('Review', 'fv-cpt'),
    'add_new' => __('Add Review', 'fv-cpt'),
    'all_items' => __('All the Reviews', 'fv-cpt'),
    'add_new_item' => __('Add Review', 'fv-cpt'),
    'edit_item' => __('Modify Review', 'fv-cpt'),
    'new_item' => __('New Review', 'fv-cpt'),
    'view_item' => __('View Review', 'fv-cpt'),
    'search_item' => __('Find Review', 'fv-cpt'),
    'not_found' => __('Review not found', 'fv-cpt'),
  );
  $args = array(
    'labels' => $labels,
    'public' => true,
    'has_archive' => 'gear',
    'publicly_queryable' => true,
    'query_var' => true,
    'rewrite' => true,
    'capability_type' => 'post',
    'hierarchical' => false,
    'taxonomies' => array('gear'),
    'rewrite' => array(
      'slug' => 'reviews',
    ),
    'support' => array(
      'title',
      'editor',
      'thumbnail',
      'revisions',
      'custom-fields',
    ),
    'menu_position' => 4,
    'menu_icon' => 'dashicons-star-filled',
    'exclude_from_search' => false,
  );

  register_post_type('reviews', $args);
}
add_action('init', 'cpt_reviews');

The Top style Post CPT:

function cpt_tops(){
  $labels = array(
    'name' => __('Tops', 'fv-cpt'),
    'singular_name' => __('Top', 'fv-cpt'),
    'add_new' => __('Add Top', 'fv-cpt'),
    'all_items' => __('All the Tops', 'fv-cpt'),
    'add_new_item' => __('Add Top', 'fv-cpt'),
    'edit_item' => __('Modify Top', 'fv-cpt'),
    'new_item' => __('New Top', 'fv-cpt'),
    'view_item' => __('View Top', 'fv-cpt'),
    'search_item' => __('Find Top', 'fv-cpt'),
    'not_found' => __('Top not found', 'fv-cpt'),
  );
  $args = array(
    'labels' => $labels,
    'public' => true,
    'has_archive' => true,
    'publicly_queryable' => true,
    'query_var' => true,
    'rewrite' => true,
    'capability_type' => 'post',
    'hierarchical' => false,
    'taxonomies' => array('gear'),
    'support' => array(
      'title',
      'editor',
      'thumbnail',
      'revisions',
      'custom-fields',
    ),
    'menu_position' => 5,
    'menu_icon' => 'dashicons-chart-bar',
    'exclude_from_search' => false,
  );

  register_post_type('tops', $args);
}
add_action('init', 'cpt_tops');

The taxonomy code:

function tax_gear(){

    $labels = array(
      'name' => __('Product Categories', 'fv-cpt'),
      'singular_name' => __('Product Category', 'fv-cpt'),
      'add_new_item' => __('Add Product Category', 'fv-cpt'),
      'edit_item' => __('Modify Product Category', 'fv-cpt'),
      'new_item_name' => __('New Product Category', 'fv-cpt'),
      'all_items' => __('All the Product Categories', 'fv-cpt'),
      'search_items' => __('Find Product Category', 'fv-cpt'),
      'update_item' => __('Update Product Category', 'fv-cpt'),
    );

    $args = array(
      'labels' => $labels,
      'hierarchical' => true,
      'query_var' => true,
      'rewrite' => array(
        'slug' => 'gear',
        'hierarchical' => true,
      ),
    );

    register_taxonomy('gear',array('reviews', 'tops'), $args);
}
add_action('init', 'tax_gear');

After all of this, I know that I need to work on the rewrite section of the args, but I can’t find the right solution that works.

So, how can I achieve what I want? Can someone explain me what’s the logic behind the slug and permalink structure, please?

I’m trying to figure it out alone, but on the internet I can’t find this solution, with 2 CPTs and a single taxonomy that comes before the CPTs inside the URL.

Bonus question: is this a good practice for SEO?

Bonus question #2: do you know a better practice to achive what I want to do?

Thank you so much.