The following 2 filters allow you to hook into when WordPress checks the slug and are found in the function wp_unique_post_slug() in the wp-includes/post.php file.
There are 2 filters, one for hierarchical posts and one for non-hierarchical. The hierarchical filter provides the ID for the post parent so if the $post_parent is 0, you know this is a “base” post.
The filters also pass the $post_type if you wanted to add specific post types to your logic.
By returning “true”, you’re telling WordPress that this slug is bad so WordPress adds on a suffix, just like it would do if you were trying to us a post name/slug that is already taken.
add_filter( 'wp_unique_post_slug_is_bad_hierarchical_slug', 'portfolio_is_bad_hierarchical_slug', 10, 4 );
function portfolio_is_bad_hierarchical_slug( $is_bad_hierarchical_slug, $slug, $post_type, $post_parent ) {
if ( !$post_parent && $slug == 'portfolio' )
return true;
return $is_bad_hierarchical_slug;
}
add_filter( 'wp_unique_post_slug_is_bad_flat_slug', 'portfolio_is_bad_flat_slug', 10, 3 );
function portfolio_is_bad_flat_slug( $is_bad_flat_slug, $slug, $post_type ) {
if ( $slug == 'portfolio' )
return true;
return $is_bad_flat_slug;
}