从自定义帖子类型帖子网址中删除


48

似乎所有网络资源都基于删除自定义帖子类型的主题,即

yourdomain.com/CPT-SLUG/post-name 

现在是非常过时的解决方案,通常引用WP 3.5版之前的安装。常见的做法是:

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

在您的register_post_type函数中。这不再起作用并且具有误导性。所以我问2018年第三季度WordPress 5濒临崩溃的社区...

有什么现代有效的方法可以从rewrite参数或其他任何地方从自定义帖子类型帖子的URL中删除帖子类型标签?

更新:似乎有几种方法可以迫使它与正则表达式一起使用。具体来说,如果您始终愿意监视内容创建以确保不会创建冲突的页面/帖子名称,那么Jan Beck的答案就可以了。...但是我坚信这是WP core的主要弱点,应该为我们处理。为永久链接创建CPT时作为选项/挂钩,或作为高级选项集使用。请支持火车票。

脚注:请通过观看/推广此票证来支持此票证:https : //core.trac.wordpress.org/ticket/34136#ticket


我想我为什么要这么做呢?困惑。
Michael Ecklund '17

3
@MichaelEcklund,因为用于创建面向公众的网页的任何CPT在URL中都有一个强制的子弹名称。实际上,有许多wp开发人员希望安全地删除该段塞。
本·拉西科特

Answers:


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作为array('page' => '', 'name' => '...', 'q' => '...')。那么@NateAllen,该条件的含义是什么?
Fabio Montefuscolo

3
我们需要比这更好的东西。支持删除内置的插件,以便以后我们无法创建冲突的URL。常规帖子和页面创建其URL的方式。
本·拉西科特

3
就是我还是这会破坏某些wordpress条件标签,例如is_single()和is_singular()?
rob-gordon

1
不幸的是,此解决方案导致一些链接断开,我的博客停止显示帖子,并且只是一个正常页面。请参阅下面的Matt Keys提供的更好解决方案。
Radley Sustaire '18 -10-8

20

将以下代码写入分类注册。

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

更改代码后必须要做的最重要的事情

更改自定义帖子类型分类法文档后,尝试转到“设置”>“永久链接”重新保存设置,否则将找不到404页。

在此处查看最佳解决方案:http : //www.krazzycodes.com/how-to-remove-custom-post-type-taxonomy-base-from-url-in-wordpress/


这实际上有效,我不知道以前没有人注意到这一点。当然,如果它们具有相同的永久链接,则可能会干扰其他页面,但是如果没有,这是一个很好的解决方案。
AleksandarĐorđević17年

4
试了一下。它为我的自定义帖子类型链接提供了所需的结果。但是,它“捕获”所有POST或PAGE帖子类型的标签,并尝试将它们解析为我的自定义帖子类型的URL,然后是404。(是的,我已经保存了永久链接)。
马特·基斯

4
这行不通。即使更新了永久链接也给出404。
克里斯汀·库珀

3
同样,即使重新保存了永久链接设置后,帖子和页面也不再起作用(404)
amklose

1
此解决方案适用于从URL删除该段。但是存档页面不再起作用。
安纳布尔纳

13

我试图不久前弄清楚了这一点,从我知道简单的答案是没有。至少不在rewrite参数内。

如果你看一下实际的代码长的解释变得很明显register_post_typeWP-包括/ post.php中线路1454

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;

您可以看到该函数始终期望一个不为空的值,否则将使用post类型。


谢谢@JanBeck。是否存在这个主要原因?为什么不通过有条件地从此规则中忽略某些帖子类型来破解此核心文件?
Ben Racicot

9
您应该将答案授予扬·贝克(Jan Beck)。WordPress需要post_type插件来正确路由请求。该规则可防止本机WP页面(不带子弹的呈现)与任何自定义的帖子类型之间的命名冲突。如果您将这些内容破解掉,那么WordPress将不会知道名为“野餐”的页面与名为“野餐”的事件(自定义帖子类型)之间的区别。
dswebsme 2015年

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. 当我使用“ calendar”插件创建页面时,该帖子类型存档也将覆盖该页面,该存档也使用“ calendar”插件。

在此处输入图片说明

  1. 当我创建一个带有“ my-event”的页面和一个带有“ my-event”的事件(CPT)时,将显示自定义帖子类型。

在此处输入图片说明

  1. 其他任何页面也不起作用。如果您看一下上面的图片,就会清楚为什么:自定义帖子类型规则将始终与页面提示匹配。由于WordPress无法识别是页面还是自定义帖子类型不存在,因此它将返回404。这就是为什么您需要使用块来识别页面或CPT的原因。一种可能的解决方案是拦截错误并查找可能存在与此答案相似的页面。

因此,如果目标是删除CPT的代码段,那么我们是否不能为CPT命名一个不会冲突的独特名称,因为它永远不会在URL中显示?或者,如果名称与页面相同,则后名称是否可能发生冲突?
本·拉西科特

我已经更新了答案,以表明这确实破坏了所有页面。如果没有任何提示,WP将查找CPT而不是页面,如果找不到,则返回错误。因此,它实际上与后名无关。
Jan Beck '10

1
我懂了。应该有重写规则,将“ -1”附加到以后的冲突URL(例如本机WP帖子与页面)上。我创建了一个追踪core.trac.wordpress.org/ticket/34136#ticket会喜欢您的想法。
本·拉西科特

7

通过这里的答案,我认为有一个更好的解决方案的余地,该解决方案结合了我上面学到的一些知识,并增加了自动检测和防止重复塞子的能力。

注意:在下面的示例中,请确保将自己的CPT名称更改为“ custom_post_type”。有很多情况发生,“查找/替换”是捕获所有问题的简便方法。所有这些代码都可以放在您的functions.php或插件中。

步骤1:在注册帖子时将重写设置为“ false”,以禁用自定义帖子类型的重写:

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

步骤2:将我们的自定义重写手动添加到我们的custom_post_type的WordPress重写的底部

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步:再次使您的自定义帖子类型为“漂亮”,

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”附加到任何重复的段末尾。如果在实施此解决方案之前已经存在重复的段,则此代码无法阻止它们。确保首先检查重复项。

我很乐意听到其他任何人的回音,他们愿意这样做,看看它们是否对他们也有效。


刚刚对其进行了测试,看来到目前为止它正在工作。
克里斯汀·库珀

希望这种方法,但即使在保存永久链接之后,我在CPT帖子上也给了我404。
Garconis '18年

抱歉,它对您不起作用Garconis。一段时间以前,我一直在与其他人谈论这个问题,他们在他们的网站上也遇到了问题。我似乎记得这很重要,如果您的博客帖子固定链接上带有前缀。在为博客帖子开发的网站上,我使用的是永久链接结构:/ blog /%postname%/。如果您的博客文章中没有前缀,并且可以接受,请尝试一下,让我知道它的进展!
马特·基斯

2
这对我有用。与页面上的其他解决方案不同,它不会破坏常规页面或博客布局,也不会引起无限重定向。当编辑这些cpt页面时,它甚至在“永久链接”区域中显示正确的URL。这里的解决方案很好,唯一的警告是存档页面不起作用。请记住,换出“ custom_post_type”,然后刷新您的永久链接
Radley Sustaire '18 -10-8

@MattKeys,默认的永久链接设置的自定义结构为/%category%/%postname%/。添加代码时,CPT子弹看起来还可以(尽管缺少尾部斜杠)...并且冲突检查器也可以工作。但在404的实际效果后
Garconis

1

您不需要那么多硬编码。只需使用轻量级插件:

它具有可自定义的选项。


现在,我知道您为什么会投票了,它阻止了正常的页面链接解析。我没有看到它,因为尽管进行了刷新,但仍在缓存现有页面的副本。
Walf

@Walf您能详细介绍一下这个问题吗?
T.Todua

从主菜单访问页面链接(不是自定义帖子类型)会产生404错误,就好像该页面不存在一样;而已。
Walf

@Walf您能给我您例子的例子网址吗?(如果需要,您可以提供域名,我只需要一个例子),谢谢,我将对其进行更新
T.Todua

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-孙子。

看一下重写规则,它看起来像在第一层和第二层上都与页面名称之外的东西匹配(我认为第二层与附件匹配),然后在那里进行一些操作以将您重定向到正确的帖子。在三个层次上,它不起作用。

您需要做的第一件事就是也从子级中删除帖子类型链接。如果您查看上面的Nate Allen的回答,那么应该在这里发生这种逻辑:

$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);

希望这对其他人有帮助,我找不到与子职位相关的任何其他信息,并且无法删除其中的任何内容。


正则表达式中似乎有一个错字。在'(:'之间需要一个'?'才能将其用作非捕获子模式=>'(?:'。第三个?似乎放错了位置,因为它允许使用空的第一个子模式。应该将其放置在(和:之间。如果没有这个错字表达将是相同的一个,其可以为内置的支柱式“页面”上找到。
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.