Force unique page slugs across all post types

A unique slug for posts is done by wp_unique_post_slug(). Looking at the source the returned slug is filtered by wp_unique_post_slug. So we can replace this generated slug with our own.

You’ll notice in the source that attachments are required to have unique slugs across all types, so we’ll just use the code from that.

The examples you gave seemed to be non-hierarchal – and it’s not clear if you want the slugs to be unique for hierarchal post types (which by default only need to be unique within their tree) – so in the example below I ignore hierarchal post types.

add_filter('wp_unique_post_slug', 'wpse72553_cross_type_unique_slugs',10,5);
function wpse72553_cross_type_unique_slugs( $slug, $post_ID, $post_status, $post_type, $post_parent ){

      global $wpdb, $wp_rewrite;

     //Don't touch hierarchical post types
     $hierarchical_post_types = get_post_types( array('hierarchical' => true) );
     if( in_array( $post_type, $hierarchical_post_types ) )
          return $slug;

     if( 'attachment' == $post_type ){
         //These will be unique anyway
         return $slug;
     }


    $feeds = $wp_rewrite->feeds;
    if ( ! is_array( $feeds ) )
         $feeds = array();


     //Lets make sure the slug is really unique:
     $check_sql = "SELECT post_name FROM $wpdb->posts WHERE post_name = %s AND ID != %d LIMIT 1";
     $post_name_check = $wpdb->get_var( $wpdb->prepare( $check_sql, $slug, $post_ID ) );

     if ( $post_name_check || in_array( $slug, $feeds) ) {
          $suffix = 2;

          do {
              $alt_post_name = substr ($slug, 0, 200 - ( strlen( $suffix ) + 1 ) ) . "-$suffix";
              $post_name_check = $wpdb->get_var( $wpdb->prepare($check_sql, $alt_post_name, $post_ID ) );
              $suffix++;
          } while ( $post_name_check );

          $slug = $alt_post_name;
      }

    return $slug;
}

Leave a Comment