Custom Post Types and independent Categories – complex Taxonomy

In wordpress, the slug (post name) is unique per post type.

So, an url like /company-1/items/item-type-1/product-1 and one like /company-2/items/item-type-1/product-1 where product-1 is the post name, cannot address to 2 different products.

If you create 2 products with same title WordPress on saving will set an unique slug.

Same thing is for articles.

For this reason in a url like:

http://example.com/company-1/items/item-type-1/product-1

can simply be rewritten in

http://example.com/index.php?post_type=products&name=product-1 

and a url like:

http://example.com/company-1/articles/article-1-1

can simply be rewritten in

http://example.com/index.php?post_type=articles&name=article-1-1

So, as you can see, no matter for category.

You need only 2 rewrite rules:

add_action('init','my_rewrite_rules');

function my_rewrite_rules() {
    add_rewrite_rule( '[^/]+/items/[^/]+/(.+)/?$' , 'index.php?post_type=products&name=$matches[1]' , 'top' );
    add_rewrite_rule( '[^/]+/articles/(.+)/?$' , 'index.php?post_type=articles&name=$matches[1]' , 'top' );
}

After that you have to flushing rewrite rules going Settings->Permalink section in your backend and saving changes.

Now the urls work as you expect, if you manually write them on browser, but the problem is generate the right url using the_permalink function. You have to use the ‘post_link’ filter and generate the right url:

add_filter('post_link', 'my_custom_permalink', 99, 3);

function my_custom_permalink($permalink, $post, $leavename) {
  if ( $post->post_type == 'products' )
     return products_permalink($permalink, $post, $leavename);
  if ( $post->post_type == 'articles' )
     return articles_permalink($permalink, $post, $leavename);
  return $permalink;
}

function products_permalink($permalink, $post, $leavename) {
  if ( $post->post_type != 'products' ) return $permalink;
  $cats = get_the_category( $post->ID );
  $companies = get_term_by('slug', 'companies', 'category');
  $items= get_term_by('slug', 'items', 'category');
  if ( empty($cats) || empty($companies) || empty($items) ) return $permalink;
  $item = '';
  $company = '';
  while ( ! empty($cats) ) {
    if ( $item && $company )
      return home_url() . "https://wordpress.stackexchange.com/" . $company . '/items/' . $item . "https://wordpress.stackexchange.com/" . $post->name;
    $cat = array_pop($cats);
    if ( $cat->parent == $companies->term_id ) $company = $cat->slug;
    if ( $cat->parent == $items->term_id ) $item = $cat->slug;
  }
  return $permalink;
}

function articles_permalink($permalink, $post, $leavename) {
  if ( $post->post_type != 'articles' ) return $permalink;
  $cats = get_the_category( $post->ID );
  $companies = get_term_by('slug', 'companies', 'category');
  if ( empty($cats) || empty($companies) ) return $permalink;
  $company = '';
  while ( ! empty($cats) ) {
    if ( $company )
      return home_url() . "https://wordpress.stackexchange.com/" . $company . '/articles/' . $post->name;
    $cat = array_pop($cats);
    if ( $cat->parent == $companies->term_id ) $company = $cat->slug;
  }
  return $permalink;
}

Here I assume you are using only standard categories, where the category ‘Companies’ is the parent category for companies and ‘Items’ is the parent category for items.

Also assumed cpt are named ‘products’ and ‘articles’ (note the plural).

Ajust the functions if something is different in your configuration.