0

My use case is: I have a two pages, they each have a meta information "language". Posts and pages having same language cannot have the same slug, but with different slugs it is totally fine. Currently I have this code:

add_filter( 'wp_unique_post_slug', 'allow_duplicate_slugs', 10, 6 );
function allow_duplicate_slugs( $slug, $post_id, $post_status, $post_type, $post_parent, $original_slug ) {
  global $wpdb, $wp_rewrite;

  $post_language = get_post_meta( $post->ID, 'language', true);

  $pto = get_post_type_object( $post_type );
  # If our post type isn't hierarchical, we don't need to worry about it:
  if ( !$pto->hierarchical )
   return $slug;
  # If our slug doesn't end with a number, we don't need to worry about it:
  if ( !preg_match( '|[0-9]$|', $slug ) )
    return $slug;
  # Most of this code is pulled straight from wp_unique_post_slug(). Just the post type check has changed.
  $feeds = $wp_rewrite->feeds;

  if ( ! is_array( $feeds ) )
    $feeds = array();

  $check_sql = "
  SELECT post_name
  FROM $wpdb->posts
  JOIN $wpdb->postmeta
  ON $wpdb->posts.ID = $wpdb->postmeta.post_id
  AND $wpdb->postmeta.meta_key = 'language' AND $wpdb->postmeta.meta_value = '$post_language'
  WHERE post_name = %s
  AND post_type = %s
  AND ID != %d
  AND post_parent = %d
  LIMIT 1
  ";

  $post_name_check = $wpdb->get_var( $wpdb->prepare( $check_sql, $original_slug, $post_type, $post_id, $post_parent ) );

  if ( $post_name_check || in_array( $original_slug, $feeds ) || preg_match( "@^($wp_rewrite->pagination_base)?\d+$@", $original_slug )  || apply_filters( 'wp_unique_post_slug_is_bad_hierarchical_slug', false, $original_slug, $post_type, $post_parent ) ) {
    $suffix = 2;
    do {
    $alt_post_name = substr( $original_slug, 0, 200 - ( strlen( $suffix ) + 1 ) ) . "-$suffix";
    $post_name_check = $wpdb->get_var( $wpdb->prepare( $check_sql, $alt_post_name, $post_type, $post_id, $post_parent ) );
    $suffix++;
  } while ( $post_name_check );
    $slug = $alt_post_name;
  } else {
    $slug = $original_slug;
  }

  return $slug;
}

The problem is that when there are two pages with different slugs, only the one is being shown, the other one shows 404. Using Query Monitor (https://wordpress.org/plugins/query-monitor/) I could find out that the query actually finds always the same page with the same ID. Example debugged sql:

SELECT wp_posts.*
FROM wp_posts
INNER JOIN wp_postmeta
ON ( wp_posts.ID = wp_postmeta.post_id )
WHERE 1=1
AND (wp_posts.ID = '8')
AND ( ( wp_postmeta.meta_key = 'language'
AND wp_postmeta.meta_value = 'en' ) )
AND wp_posts.post_type = 'page'
GROUP BY wp_posts.ID
ORDER BY wp_posts.post_date DESC

The debugged sql is always the same, if visiting pages with the same slug.

Then I wrote:

add_action('pre_get_posts', function($query) {
  if(!is_admin()) {
    $meta_query = [
      'RELATION' => 'AND',
      [
        'key' => 'language',
        'value' => get_current_language(),
        'compare' => '='
      ]
    ];
    $query->set('meta_query', $meta_query);
  }
});

But it does not seem to work. I am trying to tell the query, that it has to include also this meta information, but debugging the sql (as shown above) gives always the same result when visiting two pages with the same slug.

Maybe someone here has an idea an could help me out? Many thanks!

  • This all depends on your permalink structure. It sounds like you have pretty permalinks enabled, and your permalink structure must be /postname/ for posts and the default /pagename/ for pages. When you set it up this way, WordPress has no way to tell whether you're requesting the Post or the Page. The easiest solution would be to change your permalink structure for posts - include the post ID, or date, so WordPress can clearly identify "Oh, you're asking for the Post" rather than the Page. – WebElaine Oct 21 at 14:11
  • Well how can I tell WordPress that it should require meta_key when looking for posts and pages? I have an optional prefix (like /en/%slug%). – Кристиян Кацаров Oct 21 at 14:53
  • You could create a custom post type for each prefix. For example, create an "en" custom post type where the archive is at http://example.com/en and posts are at http://example.com/en/%slug%/, and create "es" cpt with archive http://example.com/es and posts ate http://example.com/es/%slug%/. That way WP would know from the prefix which post type to grab from. – WebElaine Oct 21 at 15:18
  • It won't be in the same UI in the admin panel and explicitly need it to be posts and pages :) – Кристиян Кацаров Oct 21 at 15:23

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.