0
votes

In Wordpress I have three custom post types: exhibitions, books, movies. I have one taxonomy with two taxonomy terms (store-1 and store-2) that can be applied to all CPTs.

What I need to achieve is to have one page with the permalink www.mysite.com/exhibitions where posts tagged with both store-1 and store-2 are listed, and this can be done easily with CPTs. However, I would also like to have permalinks such as www.mysite.com/exhibitions/store-1 where all posts with the store-1 taxonomy from the exhibitions CPT are listed (and so on for each CPT and each taxonomy term). This however creates a mix of CPT and taxonomy terms in the permalink, and I don't know how to do that.

My guess would be to invert the logic and create three custom taxonomies named exhibitions, books and movies (of course I would need to change the CPT slugs to cpt-exhibitions, cpt-books and cpt-movies), and for each of them create the terms store-1 and store-2. In this way I would get permalinks such as www.mysite.com/exhibitions/store-1. Regarding the permalinks such as www.mysite.com/exhibitions, I was thinking about creating a page with the slug "exhibition" with a custom template for that page. In the php of the template I would add a loop to get all the cpt-exhibitions posts, no matter the taxonomy term.

This is not a really clean solution since I would have basically three empty pages in the backend used solely for permalink purposes. So I was wondering: is there a specific function or rewrite rule to achieve what I need with CPTs named exhibitions, books, movies and only one custom taxonomy (e.g. store) applied to all CPTs?

Thank you for the insights!

1
A pretty straight forward solution would be to go for www.mysite.com/exhibitions/store/store-1 as you didn't ask for that I won't put the answer just yet, but if it's something you're interested in let me know.amarinediary
@amarinediary I actually found this, sounds similar to what I wanted to achieve so I will give it a try ibenic.com/… but your solution would actually be ok toocloudspotting
Tried the solution above and it works. I still need to create a separate page if I want to have a full archive under www.mysite.com/exhibitions though. At this point I would be interested in knowing @amarinediary's suggestion to see if it's a cleaner solution, so it would be great if you could share it! Thanks!cloudspotting
If exhibitions is a custom post type you shouldn't have to. ...website/exhibitions/ is the equivalent to ...website/?post_type=exhibitions query. Make sure when registering your custom post type you hae has_archive set to true.amarinediary
However now I'm using the rewrite rule slug -> exhibitions on my custom taxonomy. I don't think I can add a rewrite rule with the same slug, exhibitions, also for my custom post type, do I?cloudspotting

1 Answers

0
votes

I once had a setup like that and solved it with some rewrite rules. Check out this code, it surely does provide permalinks you need but may require some testing on your end. And yes, don't be afraid of rewrite rules in WordPress. There is literally nothing you can't do with them, though some things might come with a price (i.e. performance issues)

add_filter('init', 'create_movies_taxonomies');
add_filter('init', 'create_movies_cpt');
add_action('init', 'movies_rewrite_rules');
add_filter('post_type_link', 'movies_post_permalinks', 10, 2);

/**
 * First create Stores taxonomy
 */
function create_movies_taxonomies() {

  register_taxonomy('stores', ['movies'],
    [
      'label'             => __('Stores', 'textdomain'),
      'public'            => true,
      'show_in_nav_menus' => false,
      'show_ui'           => true,
      'show_in_menu'      => true,
      'show_tagcloud'     => false,
      'show_in_rest'      => true,
      'hierarchical'      => false,
      'rewrite'           => ['slug' => 'store', 'hierarchical' => false, 'with_front' => false],
      'show_admin_column' => true,
      'query_var'         => true
    ]
  );
}

/**
 * Then create Movies CPT with support for Stores and proper rewrites
 */
function create_movies_cpt() {

  register_post_type('movies',
    [
      'label'             => __('Movies', 'textdomain'),
      'public'              => true,
      'publicly_queryable'  => true,
      'show_ui'             => true,
      'show_in_menu'        => true,
      'exclude_from_search' => false,
      'capability_type'     => 'post',
      'map_meta_cap'        => true,
      'show_in_rest'        => true,
      'hierarchical'        => false,
      'rewrite'             => [
        'slug'       => '/movies/%stores%',
        'with_front' => false,
        'feeds'      => false
      ],
      'query_var'           => true,
      'supports'            => ['title', 'tags', 'editor', 'comments', 'custom-fields', 'thumbnail'],
      'has_archive'         => 'movies',
      'taxonomies'          => ['stores'],
      'show_in_nav_menus'   => true,
      'menu_icon'           => 'dashicons-welcome-learn-more',
    ]
  );
}

/**
 * Add some rewrite rules for our archives (CPT, taxonomy archive)
 */
function movies_rewrite_rules() {

  add_rewrite_rule(
    'movies/([a-z]+)/page/?([0-9]{1,})/?$',
    'index.php?post_type=movies&stores=$matches[1]&paged=$matches[2]',
    'top'
  );

  add_rewrite_rule(
    'movies/([a-z]+)/([a-z0-9_-]+)/?$',
    'index.php?post_type=movies&stores=$matches[1]&movies=$matches[2]',
    'top'
  );

  add_filter('query_vars', function($vars) {
    $vars[] .= 'stores';
    $vars[] .= 'stores-page';

    return $vars;
  });

}


/**
 * Creates beautiful CPT permalinks, like site.com/movies/%term%/%post%/
 *
 * @param $permalink
 * @param $post
 *
 * @return string|string[]
 */
function movies_post_permalinks($permalink, $post) {
  // if it's not our CPT, return regular permalinks
  if (strpos($permalink, '%stores%') === false) {
    return $permalink;
  }

  // Get post taxonomy term
  $terms = get_the_terms($post, 'stores');
  // if a post has a term (store), push it into URL
  if ( ! is_wp_error($terms) && ! empty($terms) && is_object($terms[0])) {
    $term_slug = array_pop($terms)->slug;
  } // if it doesn't, push something there, let it be d
  else {
    $term_slug = 'd';
  }

  return str_replace('%stores%', $term_slug, $permalink);
}