Prevent page slug from interfering with custom post type archive permalink?

In my plugin, I’m creating a custom post type that has an archive and uses the rewrite parameter to choose a slug:

$args = array (
    'public' => true,
    'has_archive' => true,
    'rewrite' => array('slug' => 'lessons', 'with_front' => false)
);
register_post_type ('my_plugin_lesson', $args);

With this code, I get an archive of the ‘my_plugin_lesson’ posts at example.com/lessons/.

However, I can also create a Page with the title “Lessons”, and WordPress will assign it the slug ‘lessons’. The View Page link on the admin panel for this page will be example.com/lessons/, but the page is inaccessible because WordPress shows the archive for my custom post type.

Is there a way to prevent WordPress from choosing a slug for pages or posts that interfere with the slug of my custom post types?

2 Answers
2

I know you already find the solution, because you figured out this question was already asked and answered…

However, once this question is a bounty one and cannot be closed as duplicate, I want to make sense to this, and add (a bit of) additional value on the answers already posted.

What is done in those answers (the one by @RachelCarden and the one posted here by @matthew-boynes) is what you need, but it is hardcoded, i.e. you have to manually add the cpt archive slug in the filter function.

What I want to obtain is a filter that works in the same way, but dinamically, something like:

add_filter('wp_unique_post_slug_is_bad_hierarchical_slug', 'prevent_slug_override', 99, 3);
add_filter('wp_unique_post_slug_is_bad_flat_slug', 'prevent_slug_override', 99, 3);

function prevent_slug_override( $now, $slug, $type ) {
  if ( in_array($type, array('nav_menu_item', 'revision', 'attachment') ) )
    return $now;
  $cpts = get_post_types( array('_builtin' => false), 'objects' );
  $check_bad = array();
  foreach ( $cpts as $cpt => $object ) {
    if ( $object->has_archive ) {
      if (
        ! empty($object->rewrite) && isset($object->rewrite['slug']) &&
        ! empty($object->rewrite['slug']) && ($object->rewrite['slug'] != $cpt)
      ) {
        $check_bad[] = $object->rewrite['slug'];
      } else {
        $check_bad[] = is_string($object->has_archive) ? $object->has_archive : $cpt;
      }
    }
  }
  if ( in_array($slug, $check_bad) ) return true;
  return $now;
} 

Leave a Comment