Sub posts and non unique post_name

Hi @jonasl:

I asked a clarifying question but I’m going to go ahead and at least start to answer.

The function in WordPress core that controls post slugs and adds numbers to them in order to force them to be unique is wp_unique_post_slug(). In WordPress 3.0.3 you can find it on line 2530 in /wp-includes/post.php. I’ve copied it in full below for your review.

You’ll note it says “Attachment slugs must be unique across all types” so if your children are post_type="attachments" then this is what is forcing them to be unique. In addition you’ll note that the post types must have 'hierarchical'=>true or it will force them to be unique across all posts.

function wp_unique_post_slug( $slug, $post_ID, $post_status, $post_type, $post_parent ) {
  if ( in_array( $post_status, array( 'draft', 'pending', 'auto-draft' ) ) )
    return $slug;

  global $wpdb, $wp_rewrite;

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

  $hierarchical_post_types = get_post_types( array('hierarchical' => true) );
  if ( 'attachment' == $post_type ) {
    // Attachment slugs must be unique across all types.
    $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;
    }
  } elseif ( in_array( $post_type, $hierarchical_post_types ) ) {
    // Page slugs must be unique within their own trees. Pages are in a separate
    // namespace than posts so page slugs are allowed to overlap post slugs.
    $check_sql = "SELECT post_name FROM $wpdb->posts WHERE post_name = %s AND post_type IN ( '" . implode( "', '", esc_sql( $hierarchical_post_types ) ) . "' ) AND ID != %d AND post_parent = %d LIMIT 1";
    $post_name_check = $wpdb->get_var( $wpdb->prepare( $check_sql, $slug, $post_ID, $post_parent ) );

    if ( $post_name_check || in_array( $slug, $feeds ) || preg_match( '@^(page)?\d+$@', $slug ) ) {
      $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, $post_parent ) );
        $suffix++;
      } while ( $post_name_check );
      $slug = $alt_post_name;
    }
  } else {
    // Post slugs must be unique across all posts.
    $check_sql = "SELECT post_name FROM $wpdb->posts WHERE post_name = %s AND post_type = %s AND ID != %d LIMIT 1";
    $post_name_check = $wpdb->get_var( $wpdb->prepare( $check_sql, $slug, $post_type, $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_type, $post_ID ) );
        $suffix++;
      } while ( $post_name_check );
      $slug = $alt_post_name;
    }
  }

  return $slug;
}

If you are not using attachments as children (which I fear you are based on your question) then you can possibly just define the post type as being 'hierarchical'=>true. If you can’t do that I might be able to suggest a have to fake it during insert of the post but I can’t promise doing so will not cause other posts in WordPress to break.

If you are using attachments and need this maybe you can explain more what you are trying to accomplish so that maybe we can suggest an alternative.

Leave a Comment