Удалить слаг из URL-адресов сообщений пользовательских типов


48

Похоже, что все веб-ресурсы основаны на удалении пользовательского типа поста, т.е.

yourdomain.com/CPT-SLUG/post-name 

В настоящее время это очень устаревшие решения, часто ссылающиеся на предварительные версии WP 3.5. Обычным является:

'rewrite'   => array( 'slug' => false, 'with_front' => false ),  

в вашей функции register_post_type. Это больше не работает и вводит в заблуждение. Поэтому я прошу сообщество в 3 квартале 2018 года на грани WordPress 5 ...

Каковы современные и эффективные способы удаления слагаемого типа сообщения из URL-адреса сообщения настраиваемого типа сообщения из аргумента перезаписи или где-либо еще?

ОБНОВЛЕНИЕ: Кажется, есть несколько способов заставить это работать с регулярным выражением. В частности, ответ от Яна Бека, если вы хотите постоянно следить за созданием контента, чтобы убедиться, что конфликтующие имена страниц / постов не создаются .... Однако я убежден, что это серьезный недостаток в ядре WP, где его следует обрабатывать для нас. , И как опция / ловушка при создании CPT, или как расширенный набор опций для постоянных ссылок. Пожалуйста, поддержите трек билет.

Сноска: Пожалуйста, поддержите этот билет, проследив / продвигая его: https://core.trac.wordpress.org/ticket/34136#ticket


Я думаю, я почесал голову, почему вы хотели бы сделать это? Смущенный.
Майкл Эклунд

3
@MichaelEcklund, потому что любой CPT, который используется для создания общедоступных веб-страниц, имеет имя принудительного слаг в URL. На самом деле многие разработчики wp хотят безопасно удалить слизняк.
Бен Расикот

Ответы:


60

Следующий код будет работать, но вы просто должны помнить, что конфликты могут легко возникнуть, если слаг для вашего пользовательского типа поста такой же, как слаг для страницы или поста ...

Сначала мы удалим слизняк из постоянной ссылки:

function na_remove_slug( $post_link, $post, $leavename ) {

    if ( 'events' != $post->post_type || 'publish' != $post->post_status ) {
        return $post_link;
    }

    $post_link = str_replace( '/' . $post->post_type . '/', '/', $post_link );

    return $post_link;
}
add_filter( 'post_type_link', 'na_remove_slug', 10, 3 );

Одного удаления слизняка недостаточно. Прямо сейчас вы получите страницу 404, потому что WordPress ожидает, что посты и страницы будут вести себя именно так. Вам также необходимо добавить следующее:

function na_parse_request( $query ) {

    if ( ! $query->is_main_query() || 2 != count( $query->query ) || ! isset( $query->query['page'] ) ) {
        return;
    }

    if ( ! empty( $query->query['name'] ) ) {
        $query->set( 'post_type', array( 'post', 'events', 'page' ) );
    }
}
add_action( 'pre_get_posts', 'na_parse_request' );

Просто измените «события» на свой тип сообщения, и все готово. Возможно, вам придется обновить ваши постоянные ссылки.


Благодарю. Как вы думаете, это лучше, чем создавать переписанные вручную? Я видел это решение, и оно может держать конфликты, о которых вы упомянули, в страхе?
Бен Расикот

1
Это терпит неудачу с nginx, потому что условие 2 != count( $query->query ). С nginx вы можете иметь $ query-> query as array('page' => '', 'name' => '...', 'q' => '...'). @NateAllen, что означает это условие?
Фабио Монтефусколо

3
Нам нужно что-то лучше, чем это. Поддержка удаления встроенного пула, чтобы мы не могли создавать конфликтующие URL в дальнейшем. То, как обычные посты и страницы создают свои URL.
Бен Расикот

3
Это только я или это нарушает некоторые условные теги WordPress, такие как is_single () и is_singular ()?
-Гордон

1
Это решение, к сожалению, вызвало некоторые неработающие ссылки, и мой блог перестал показывать сообщения и был просто нормальной страницей. Смотрите лучшее решение ниже от Мэтта Киса.
Рэдли Сустер

20

Запишите следующий код в регистрацию таксономии.

'rewrite' => [
  'slug' => '/',
  'with_front' => false
]

Самое важное, что вы должны сделать после изменения кода

После того, как вы изменили свой документ таксономии с пользовательским типом записи, попробуйте зайти в « Настройки»> «Постоянные ссылки» и заново сохранить настройки , иначе вы получите страницу 404, которая не найдена.

Проверьте здесь для лучшего решения: http://www.krazzycodes.com/how-to-remove-custom-post-type-taxonomy-base-from-url-in-wordpress/


Это на самом деле работает, я не знаю, как никто не заметил это раньше. Конечно, это может мешать другим страницам, если они имеют одинаковую постоянную ссылку, но если нет, то это отличное решение.
Александар

4
Попробовал это. Это дает желаемый результат для моих пользовательских ссылок типа сообщений. Однако он «ловит» всех слагов типа POST или PAGE и пытается разрешить их как URL для моего пользовательского типа записи, затем 404. (да, я сохранил постоянные ссылки).
Мэтт Киз

4
Это не работает Дает 404, даже когда вы обновили постоянные ссылки.
Кристина Купер

3
Опять же, даже после повторного сохранения настроек постоянной ссылки, сообщения и страницы больше не работают (404)
amklose

1
Это решение работает для удаления слаг из URL. Но страницы архива больше не работают.
Аннапурна

13

Я пытался понять это не так давно, и короткий ответ из того, что я знаю, нет . По крайней мере, не из аргумента перезаписи.

Длинное объяснение становится очевидным, если вы посмотрите на реальный код register_post_typeв строке 1454 wp-includes / post.php :

add_permastruct( $post_type, "{$args->rewrite['slug']}/%$post_type%", $permastruct_args );

Вы можете видеть его префиксы $args->rewrite['slug']к %$post_type%тегу перезаписи. Кто-то может подумать «давайте просто установим слаг null», пока вы не посмотрите несколько строк вверх:

if ( empty( $args->rewrite['slug'] ) )
    $args->rewrite['slug'] = $post_type;

Вы можете видеть, что функция всегда ожидает значение slug, которое не является пустым, и иначе использует тип записи.


Спасибо @JanBeck. Есть ли главная причина для этого? Почему бы не взломать этот основной файл с условным пропуском определенных типов записей из этого правила?
Бен Расикот

9
Вы должны присудить ответ Ян Бек. WordPress нужен слаг post_type для правильной маршрутизации запросов. Это правило предотвращает конфликты имен между собственными страницами WP (которые отображаются без слага) и любыми пользовательскими типами записей. Если вы взломаете слаг, WordPress не узнает разницы между страницей с именем «пикник» и событием (пользовательский тип записи) с именем «пикник».
dswebsme

3
@dswebsme Согласен, но есть ситуации, когда вам абсолютно необходимо изменить URL. Итак, кроме того, почему вы не можете изначально и не должны, как вы делаете это эффективно?
Бен Рацикот

7

В ответ на мой предыдущий ответ : вы, конечно, могли бы установить rewriteпараметр falseпри регистрации нового типа поста и самостоятельно обрабатывать правила перезаписи.

<?php
function wpsx203951_custom_init() {

    $post_type = 'event';
    $args = (object) array(
        'public'      => true,
        'label'       => 'Events',
        'rewrite'     => false, // always set this to false
        'has_archive' => true
    );
    register_post_type( $post_type, $args );

    // these are your actual rewrite arguments
    $args->rewrite = array(
        'slug' => 'calendar'
    );

    // everything what follows is from the register_post_type function
    if ( is_admin() || '' != get_option( 'permalink_structure' ) ) {

        if ( ! is_array( $args->rewrite ) )
            $args->rewrite = array();
        if ( empty( $args->rewrite['slug'] ) )
            $args->rewrite['slug'] = $post_type;
        if ( ! isset( $args->rewrite['with_front'] ) )
            $args->rewrite['with_front'] = true;
        if ( ! isset( $args->rewrite['pages'] ) )
            $args->rewrite['pages'] = true;
        if ( ! isset( $args->rewrite['feeds'] ) || ! $args->has_archive )
            $args->rewrite['feeds'] = (bool) $args->has_archive;
        if ( ! isset( $args->rewrite['ep_mask'] ) ) {
            if ( isset( $args->permalink_epmask ) )
                $args->rewrite['ep_mask'] = $args->permalink_epmask;
            else
                $args->rewrite['ep_mask'] = EP_PERMALINK;
        }

        if ( $args->hierarchical )
            add_rewrite_tag( "%$post_type%", '(.+?)', $args->query_var ? "{$args->query_var}=" : "post_type=$post_type&pagename=" );
        else
            add_rewrite_tag( "%$post_type%", '([^/]+)', $args->query_var ? "{$args->query_var}=" : "post_type=$post_type&name=" );

        if ( $args->has_archive ) {
            $archive_slug = $args->has_archive === true ? $args->rewrite['slug'] : $args->has_archive;
            if ( $args->rewrite['with_front'] )
                $archive_slug = substr( $wp_rewrite->front, 1 ) . $archive_slug;
            else
                $archive_slug = $wp_rewrite->root . $archive_slug;

            add_rewrite_rule( "{$archive_slug}/?$", "index.php?post_type=$post_type", 'top' );
            if ( $args->rewrite['feeds'] && $wp_rewrite->feeds ) {
                $feeds = '(' . trim( implode( '|', $wp_rewrite->feeds ) ) . ')';
                add_rewrite_rule( "{$archive_slug}/feed/$feeds/?$", "index.php?post_type=$post_type" . '&feed=$matches[1]', 'top' );
                add_rewrite_rule( "{$archive_slug}/$feeds/?$", "index.php?post_type=$post_type" . '&feed=$matches[1]', 'top' );
            }
            if ( $args->rewrite['pages'] )
                add_rewrite_rule( "{$archive_slug}/{$wp_rewrite->pagination_base}/([0-9]{1,})/?$", "index.php?post_type=$post_type" . '&paged=$matches[1]', 'top' );
        }

        $permastruct_args = $args->rewrite;
        $permastruct_args['feed'] = $permastruct_args['feeds'];
        add_permastruct( $post_type, "%$post_type%", $permastruct_args );
    }
}
add_action( 'init', 'wpsx203951_custom_init' );

Вы можете видеть, что add_permastructзвонок теперь не включает слизняк. Я проверил два сценария:

  1. Когда я создал страницу со слагом «календарь», эта страница перезаписывается архивом типа поста, который также использует слаг «календарь».

введите описание изображения здесь

  1. Когда я создал страницу с слагом «my-event» и событием (CPT) с слагом «my-event», отображается пользовательский тип записи.

введите описание изображения здесь

  1. Любые другие страницы тоже не работают. Если вы посмотрите на картинку выше, станет понятно, почему: пользовательское правило типа записи всегда будет совпадать со слагом страницы. Поскольку WordPress не может определить, является ли это страница или пользовательский тип сообщения, который не существует, он вернет 404. Вот почему вам нужен слаг для идентификации либо страницы, либо CPT. Возможным решением было бы перехватить ошибку и найти страницу, которая может существовать аналогично этому ответу .

Так что, если цель состоит в том, чтобы убрать слаг для CPT, мы не можем назвать CPT чем-то уникальным, что не будет конфликтовать, так как в любом случае его никогда не увидят в URL? Или почтовое имя является возможным конфликтом, если названо так же, как страница?
Бен Расикот

Я обновил свой ответ, чтобы показать, что это на самом деле ломает все страницы. Без slug WP будет искать CPT вместо страницы и, если не найдет его, вернет ошибку. Так что на самом деле это не связано с именем.
Ян Бек

1
Понимаю. Должны быть правила переписывания, которые добавляют «-1» к будущим конфликтующим URL-адресам, как родные сообщения WP против страниц. Я создал трейк- билет core.trac.wordpress.org/ticket/34136#ticket, и вам понравятся ваши мысли.
Бен Расикот

7

Просматривая ответы здесь, я думаю, что есть место для лучшего решения, которое объединяет некоторые вещи, которые я изучил выше, и добавляет автоматическое обнаружение и предотвращение дублирования почтовых слагов.

ПРИМЕЧАНИЕ. Убедитесь, что вы изменили «custom_post_type» для своего собственного имени CPT в моем примере ниже. Есть много случаев, и «найти / заменить» - это простой способ поймать их всех. Весь этот код может идти в вашем functions.php или в плагине.

Шаг 1. Отключите перезаписи для своего пользовательского типа сообщения, установив для параметра rewrites значение «false» при регистрации сообщения:

register_post_type( 'custom_post_type',
    array(
        'rewrite' => false
    )
);

Шаг 2: Вручную добавить наши собственные переписывает в нижней части WordPress переписывает для нашего custom_post_type

function custom_post_type_rewrites() {
    add_rewrite_rule( '[^/]+/attachment/([^/]+)/?$', 'index.php?attachment=$matches[1]', 'bottom');
    add_rewrite_rule( '[^/]+/attachment/([^/]+)/trackback/?$', 'index.php?attachment=$matches[1]&tb=1', 'bottom');
    add_rewrite_rule( '[^/]+/attachment/([^/]+)/feed/(feed|rdf|rss|rss2|atom)/?$', 'index.php?attachment=$matches[1]&feed=$matches[2]', 'bottom');
    add_rewrite_rule( '[^/]+/attachment/([^/]+)/(feed|rdf|rss|rss2|atom)/?$', 'index.php?attachment=$matches[1]&feed=$matches[2]', 'bottom');
    add_rewrite_rule( '[^/]+/attachment/([^/]+)/comment-page-([0-9]{1,})/?$', 'index.php?attachment=$matches[1]&cpage=$matches[2]', 'bottom');
    add_rewrite_rule( '[^/]+/attachment/([^/]+)/embed/?$', 'index.php?attachment=$matches[1]&embed=true', 'bottom');
    add_rewrite_rule( '([^/]+)/embed/?$', 'index.php?custom_post_type=$matches[1]&embed=true', 'bottom');
    add_rewrite_rule( '([^/]+)/trackback/?$', 'index.php?custom_post_type=$matches[1]&tb=1', 'bottom');
    add_rewrite_rule( '([^/]+)/page/?([0-9]{1,})/?$', 'index.php?custom_post_type=$matches[1]&paged=$matches[2]', 'bottom');
    add_rewrite_rule( '([^/]+)/comment-page-([0-9]{1,})/?$', 'index.php?custom_post_type=$matches[1]&cpage=$matches[2]', 'bottom');
    add_rewrite_rule( '([^/]+)(?:/([0-9]+))?/?$', 'index.php?custom_post_type=$matches[1]', 'bottom');
    add_rewrite_rule( '[^/]+/([^/]+)/?$', 'index.php?attachment=$matches[1]', 'bottom');
    add_rewrite_rule( '[^/]+/([^/]+)/trackback/?$', 'index.php?attachment=$matches[1]&tb=1', 'bottom');
    add_rewrite_rule( '[^/]+/([^/]+)/feed/(feed|rdf|rss|rss2|atom)/?$', 'index.php?attachment=$matches[1]&feed=$matches[2]', 'bottom');
    add_rewrite_rule( '[^/]+/([^/]+)/(feed|rdf|rss|rss2|atom)/?$', 'index.php?attachment=$matches[1]&feed=$matches[2]', 'bottom');
    add_rewrite_rule( '[^/]+/([^/]+)/comment-page-([0-9]{1,})/?$', 'index.php?attachment=$matches[1]&cpage=$matches[2]', 'bottom');
    add_rewrite_rule( '[^/]+/([^/]+)/embed/?$', 'index.php?attachment=$matches[1]&embed=true', 'bottom');
}
add_action( 'init', 'custom_post_type_rewrites' );

ПРИМЕЧАНИЕ. В зависимости от ваших потребностей вы можете изменить приведенные выше варианты перезаписи (отключить обратные ссылки? Каналы? И т. Д.). Они представляют типы перезаписей по умолчанию, которые были бы сгенерированы, если бы вы не отключили перезаписи на шаге 1

Шаг 3. Снова сделайте постоянные ссылки на ваш пользовательский тип сообщения «pretty».

function custom_post_type_permalinks( $post_link, $post, $leavename ) {
    if ( isset( $post->post_type ) && 'custom_post_type' == $post->post_type ) {
        $post_link = home_url( $post->post_name );
    }

    return $post_link;
}
add_filter( 'post_type_link', 'custom_post_type_permalinks', 10, 3 );

ПРИМЕЧАНИЕ. Вы можете остановиться здесь, если вы не беспокоитесь о том, что ваши пользователи создают конфликтующие (дублирующие) записи в сообщениях другого типа, что создаст ситуацию, при которой только одна из них сможет загружаться при запросе страницы.

Шаг 4: Предотвратите повторяющихся пост-слагов

function prevent_slug_duplicates( $slug, $post_ID, $post_status, $post_type, $post_parent, $original_slug ) {
    $check_post_types = array(
        'post',
        'page',
        'custom_post_type'
    );

    if ( ! in_array( $post_type, $check_post_types ) ) {
        return $slug;
    }

    if ( 'custom_post_type' == $post_type ) {
        // Saving a custom_post_type post, check for duplicates in POST or PAGE post types
        $post_match = get_page_by_path( $slug, 'OBJECT', 'post' );
        $page_match = get_page_by_path( $slug, 'OBJECT', 'page' );

        if ( $post_match || $page_match ) {
            $slug .= '-duplicate';
        }
    } else {
        // Saving a POST or PAGE, check for duplicates in custom_post_type post type
        $custom_post_type_match = get_page_by_path( $slug, 'OBJECT', 'custom_post_type' );

        if ( $custom_post_type_match ) {
            $slug .= '-duplicate';
        }
    }

    return $slug;
}
add_filter( 'wp_unique_post_slug', 'prevent_slug_duplicates', 10, 6 );

ПРИМЕЧАНИЕ. Это добавит строку «-duplicate» в конец любых дублирующих слагов. Этот код не может предотвратить дублирование слагов, если они уже существуют до реализации этого решения. Не забудьте сначала проверить наличие дубликатов.

Я хотел бы услышать от кого-то еще, кто попробует проверить, хорошо ли это сработало и для них.


Только что проверил, и кажется, что он работает до сих пор.
Кристина Купер

Был полон надежд на такой подход, но дает мне 404 на моих постах CPT, даже после спасения Постоянных ссылок.
Гарконис

Извините, это не сработало для вас, Гарконис. Я говорил с кем-то об этом некоторое время назад, и у них также были проблемы с этим на их сайте. Кажется, я помню, что имело значение, если у ваших постоянных ссылок в блоге есть префикс. На сайте, который я разработал, для блогов используются постоянные ссылки: / blog /% postname% /. Если у вас нет префикса в сообщениях блога, и это приемлемо, попробуйте его и дайте мне знать, как это происходит!
Мэтт Киз

2
Это сработало для меня. В отличие от других решений на странице, он не нарушал нормальные страницы или макет блога и не вызывал бесконечных перенаправлений. Он даже показывает правильный URL-адрес в области «Постоянная ссылка» при редактировании этих страниц cpt. Довольно хорошее решение здесь, только предостережение в том, что страница архива не работает. ПОМНИТЕ, чтобы поменять "custom_post_type" и обновить ваши постоянные ссылки после этого .
Рэдли Сустер

@MattKeys, настройки постоянных ссылок по умолчанию имеют пользовательскую структуру /%category%/%postname%/. При добавлении вашего кода слагаемые CPT выглядят нормально (хотя и не имеют завершающего слеша) ... и проверка конфликтов также работает. Но фактическое сообщение приводит к 404.
Гарконис

1

Вам не нужно так много жесткого кода. Просто используйте легкий плагин:

Имеет настраиваемые параметры.


Теперь я знаю, почему за вас проголосовали, это мешает нормальному разрешению ссылок на страницы. Я не видел этого, потому что я получал кэшированные копии существующих страниц, несмотря на обновление.
Уолф

@Walf Можете ли вы рассказать мне о проблеме в деталях?
Т.Тодуа

Следующие ссылки на страницы (которые не были пользовательскими типами записей) из главного меню выдавали 404 ошибки, как если бы страница не существовала; это оно.
Уолф

@ Уолф, не могли бы вы привести пример URL вашего случая? (Вы можете указать доменное имя, если хотите, мне просто нужен пример ex) спасибо, я
обновлю

1

Были те же проблемы здесь и там, кажется, нет движения на сайте WordPress. В моей конкретной ситуации, когда для отдельных постов в блоге была нужна структура / blog /% postname% /

https://kellenmace.com/remove-custom-post-type-slug-from-permalinks/

закончилась кучей 404-х

Но вместе с этим замечательным подходом, который не использует структуру постоянных ссылок бэкенда для поста блога, он, наконец, работает как шарм. https://www.bobz.co/add-blog-prefix-permalink-structure-blog-posts/

Огромное спасибо.


0

и мы можем внести некоторые изменения в вышеупомянутую функцию:

function na_parse_request( $query ) {

if ( ! $query->is_main_query() || 2 != count( $query->query ) || ! isset( $query->query['page'] ) ) {
    return;
}

if ( ! empty( $query->query['name'] ) ) {
    $query->set( 'post_type', array( 'post', 'events', 'page' ) );
}
}

чтобы:

function na_parse_request( $query ) {

if ( ! $query->is_main_query() || 2 != count( $query->query ) || ! isset( $query->query['page'] ) ) {
    return;
}

if ( ! empty( $query->query['name'] ) ) {

    global $wpdb;
    $pt = $wpdb->get_var(
        "SELECT post_type FROM `{$wpdb->posts}` " .
        "WHERE post_name = '{$query->query['name']}'"
    );
    $query->set( 'post_type', $pt );
}
}

для того, чтобы установить правильное значение post_type.



0

Для тех, кто читает это, у кого были проблемы с дочерними записями, как я, я нашел лучший способ - добавить свои собственные правила переписывания.

Основная проблема, с которой я столкнулся, заключалась в том, что WordPress обрабатывает перенаправление со страниц, которые имеют глубину 2 уровня (дочерние сообщения), немного по-другому, чем он обрабатывает 3 уровня (дочерние элементы дочерних сообщений).

Это означает, что когда у меня есть / post-type / post-name / post-child /, я могу использовать / post-name / post-child, и он перенаправит меня на тот, у которого post-type впереди, но если у меня есть post-type / post-name / post-child / post-внучка, тогда я не могу использовать post-name / post-child / post-внучка.

Взглянув на правила перезаписи, он выглядит так, как будто он соответствует другим вещам, кроме pagename на первом и втором уровнях (я думаю, что второй уровень соответствует вложению), а затем делает что-то там, чтобы перенаправить вас на нужный пост. На трех уровнях это не работает.

Первое, что вам нужно сделать, это также удалить ссылку на тип записи у детей. Эта логика должна произойти здесь, если вы посмотрите на ответ Нейта Аллена выше:

$post_link = str_replace( '/' . $post->post_type . '/', '/', $post_link );

Я сам использовал комбинацию различных условных выражений, чтобы проверить, есть ли у поста дети и еще много чего, чтобы получить правильную постоянную ссылку. Эта часть не слишком сложна, и вы найдете примеры людей, делающих это в других местах.

Следующий шаг - это то, где все меняется от данного ответа. Вместо того, чтобы что-то добавлять в основной запрос (который работал для пользовательских сообщений и их дочерних элементов, но не для последующих дочерних элементов), я добавил переписывание, которое перешло в конец правил WordPress, чтобы, если pagename не проверялось, и оно собиралось если вы нажмете 404, он выполнит одну последнюю проверку, чтобы увидеть, имеет ли страница в пользовательском типе записи то же имя, в противном случае он выбросит 404.

Вот правило перезаписи, которое я использовал, предполагая, что 'event' - это имя вашего CPT

function rewrite_rules_for_removing_post_type_slug()
{
    add_rewrite_rule(
        '(.?.+?)?(:/([0-9]+))?/?$',
        'index.php?event=$matches[1]/$matches[2]&post_type=event',
        'bottom'
    );
}

add_action('init', 'rewrite_rules_for_removing_post_type_slug', 1, 1);

Надеюсь, это поможет кому-то еще, я не смог найти ничего другого, связанного с дочерними постами и удалением слизняков из них.


Кажется, в регулярном выражении есть опечатка. Между '(:' a '?' Необходимо использовать его в качестве необработанного subpattern => '(?:'. Третий? Кажется неуместным, так как допускает пустой первый подшаблон. Вероятно, он должен быть расположен между (и:. Без этой опечатки выражение будет таким же, какое можно найти для встроенного поста типа "page".
Jot
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.