似乎所有基于删除自定义帖子类型子句即
yourdomain.com/CPT-SLUG/post-name 

的主题的Web资源现在都是非常过时的解决方案,通常引用WP 3.5之前的版本安装。常见的做法是:在您的register_post_type函数中使用
'rewrite'   => array( 'slug' => false, 'with_front' => false ),  

。这不再起作用,并且具有误导性。因此,我问2020年第4季度的社区...
有什么现代有效的方法可以从rewrite参数或其他任何地方从自定义帖子类型帖子的URL中删除帖子类型插件?
更新:
似乎有几种方法可以迫使它与正则表达式一起使用。具体来说,如果您始终愿意监视内容创建以确保不会创建冲突的页面/帖子名称,Jan Beck会给出答案。但是,我坚信这是WP core的主要弱点,应该为我们处理。为永久链接创建CPT时作为选项/挂钩,或作为高级选项集使用。请提供跟踪票。
脚注:请观看/促销此跟踪票以支持此跟踪票:https://core.trac.wordpress.org/ticket/34136#ticket

评论

我想我为什么要这么做呢?困惑。

@MichaelEcklund,因为用于创建面向公众的网页的任何CPT在URL中都有一个强制的子弹名称。实际上,有很多wp开发人员希望安全地清除该弹头。

#1 楼

以下代码将起作用,但是您只需要记住,如果您自定义帖子类型的子句与页面或帖子的子句相同,则冲突很容易发生...

首先,我们将从永久链接中删除该子弹:

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


只需将“事件”更改为自定义帖子类型,就可以了。您可能需要刷新永久链接。

评论


您能否更新,如何将此代码用于多种帖子类型

–阿宾
16年1月25日在11:29

Nginx失败,因为条件2!= count($ query-> query)。使用nginx,您可以将$ query-> query作为array('page'=>``,'name'=>'...','q'=>'...')。那么@NateAllen,该条件的含义是什么?

–法比奥·蒙特福斯科洛(Fabio Montefuscolo)
16年11月8日在12:25



我们需要比这更好的东西。支持删除内置的Slug,以便以后我们无法创建有冲突的URL。常规帖子和页面创建其URL的方式。

– Ben Racicot
17年7月18日在0:13

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

– rob-gordon
17年7月18日在18:53

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

–拉德利·斯蒂斯塔(Radley Sustaire)
'18 -10-8在0:10

#2 楼

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

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


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

自定义帖子类型分类法文档,请尝试转到“设置”>“固定链接”并重新保存设置,否则将找不到404页面。

评论


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

–马特键
17-10-5在20:49

这行不通。即使更新了永久链接也给出404。

–克里斯汀·库珀♦
17年11月12日在18:02

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

–安克洛斯
18年2月13日在19:18

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

–安纳布尔纳
18-09-25在4:42

正如其他人所述,这确实对CPT帖子有效。但这现在导致常规页面的404错误。

– Garconis
19年7月3日在16:52

#3 楼

我不久前试图弄清楚这一点,据我所知,简短的答案是“否”。至少不是从rewrite参数内部进行的。

如果您在wp-includes / post.php第1454行中查看register_post_type的实际代码,则很长的解释就显而易见了:

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;


您可以看到该函数始终期望一个从不为空,否则使用帖子类型。

评论


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

– Ben Racicot
15年9月30日在18:02

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

–dswebsme
2015年9月30日19:34



@dswebsme同意,但是在某些情况下您绝对必须更改URL。因此,除了为什么您不能本来也不应这样做之外,您如何有效地做到这一点?

– Ben Racicot
2015年10月1日13:27



#4 楼

在这里寻找答案,我认为有一个更好的解决方案,它结合了我上面学到的一些知识,并增加了自动检测和防止重复的的能力。

注意:请确保更改“ custom_post_type”在下面的示例中使用您自己的CPT名称。发生了很多事情,“查找/替换”是一种轻松捕获所有问题的简便方法。所有这些代码都可以放入您的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:再次永久链接到自定义帖子类型“ 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:防止重复的帖子post

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'附加到任何重复的子弹的末尾。如果在实施此解决方案之前已经存在重复的段,则此代码无法阻止它们。请务必先检查是否有重复项。

我很乐意听取其他任何给予此建议的人的回信,看看它是否同样对他们也有效。

评论


刚刚对其进行了测试,到目前为止,它似乎仍在工作。

–克里斯汀·库珀♦
17年11月12日在18:02

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

– Garconis
18年8月7日在18:32

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

–马特键
18年8月7日在20:54



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

–拉德利·斯蒂斯塔(Radley Sustaire)
18-10-8在0:08



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

– Garconis
19年7月3日在14:39

#5 楼

响应我以前的回答:
当然,您可以在注册新帖子类型时将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呼叫现在不再包含该子弹了。
我测试了两种情况:


当我创建带有“ calendar”标签的页面时,该页面被也使用“ calendar”标签的帖子类型档案覆盖。




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




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


评论


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

– Ben Racicot
2015年10月2日14:35



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

– Jan Beck
2015年10月2日在15:49

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

– Ben Racicot
2015年10月2日23:22

#6 楼

插件综述

快到2020年了,其中许多答案都行不通。这是我自己对当前选项的总结:




如果想要自定义代码解决方案,Matt Keys答案似乎是正确的唯一答案。我发现所有插件都无法执行此处列出的所有功能,尤其是重复检查。如果有人想使用这种方法,那么对于插件来说,这似乎是一个非常好的机会。

Permalink Manager Lite


👍我尝试过的最好的免费插件。
👍完全控制所有Page / Post / CPT完整的永久链接结构,并使它们相同。 GUI是迄今为止功能最丰富的。
👍还允许对每个帖子进行完全覆盖,并让您查看原始/默认设置是什么,并在需要时重置为默认设置。
👍支持多种功能-site。
👎不检查帖子类型之间是否重复,这很可悲。如果页面和CPT具有相同的URL,则页面将加载且CPT无法访问。没有警告或错误,您只需要手动检查重复项即可。
💵所有分类法功能都在PRO版本中。升级功能非常沉重。




自定义永久链接


👍免费版本有很多作用。专业版永久不支持分类学永久链接和高级支持。
👍允许您更改任何单个页面/帖子/ CPT的完整永久链接。
👍支持多站点。
👎不允许您更改默认结构,因此您可以自定义帖子类型仍然是example.com/cpt-slug/post-title,但是您可以单独更改它们。
👎不检查帖子类型之间的重复,这很可悲。



自定义帖子类型永久链接


👍允许非开发人员用户使用register_post_type更改易于更改的内容

👎不允许您更改CPT基础块-仅更改其后的部分-因此对于开发人员和此问题中的问题几乎没有用。



删除基础弹头...-已经死了几年...请不要使用。


评论


插件Permalink Manager Lite绝对是最好的解决方案:稳定,强大,干净,免费版本允许您删除子弹底座。它也适用于Polylang!在TwentyTwenty主题下,在Wordpress 5.4上进行了测试,未激活任何其他插件。无论您是否创建了层次结构(带有子帖子和孙子帖子),“自定义帖子类型”都像魅力一样工作。任何想要干净解决方案的人。

– PhpDoe
4月4日11:57



#7 楼

我们可以对上述功能进行一些更改:

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值。

#8 楼

这对我有用:
'rewrite' => array('slug' => '/')

评论


这行不通。即使更新了永久链接也给出404。

–克里斯汀·库珀♦
17年11月12日在18:02

#9 楼

对于像我这样阅读子帖子时遇到麻烦的任何人,我发现最好的方法是添加自己的重写规则。

我遇到的主要问题是WordPress处理深2级(子帖子)页面的重定向与处理3级深(子帖子的孩子)有所不同。

这意味着当我拥有/ post-type / post-name / post-child /时,我可以使用/ post-name / post-child,它将重定向到具有post-type的前面,但是如果我有post-type / post-name / post-child / post-grandchild,那么我就不能使用post-name / post-child / post-grandchild。

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

您需要做的第一件事就是也从子级中删除帖子类型链接。如果您查看上面的内特·艾伦的回答,则此逻辑应在这里发生:

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


我自己我使用了多种不同的条件来检查该帖子中是否有子对象以及其他顺序以获得正确的永久链接。这部分不是很棘手,您会在其他地方找到其他人这样做的例子。

下一步是让给定答案发生变化。与其向主要查询中添加内容(该方法适用于自定义帖子及其子级,而不能用于其他子级),我添加了一个重写,该重写进入了WordPress规则的底部,因此如果pagename不检出并且将要命中404,它会做最后一次检查,以查看自定义帖子类型中的页面是否具有相同的名称,否则它将抛出404。

这是我假设“事件”使用的重写规则是您的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);


希望这能对其他人有所帮助,我找不到与子职位相关的其他任何东西,并删除了那些子条目。

评论


正则表达式中似乎有一个错字。在'(:'之间需要一个'?'才能将其用作非捕获子模式=>'(?:'。第三个?似乎放错了位置,因为它允许使用空的第一个子模式。应该将其放置在(和:之间。如果没有这种错字,则该表达式将与内置帖子类型“页面”中的表达式相同。

– jot
19-10-28在19:46

#10 楼

在这里有同样的问题,WordPress网站上似乎没有动静。在我的特殊情况下,对于单个博客文章,需要结构/ blog /%postname%/这个解决方案

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

以一堆404结束

,但是再加上这种绝妙的方法,即不使用后端永久链接结构作为博客文章,它最终的工作方式就像charme。 > https://www.bobz.co/add-blog-prefix-permalink-structure-blog-posts/

非常感谢。

评论


不鼓励仅链接的答案。如果您在本文中找到的答案与页面上的其他答案不同,请在答案中放入一个摘要和代码示例。

– squarecandy
19年12月16日在22:50

#11 楼

您不需要那么多硬代码。只需使用轻巧的插件:


从自定义帖子中删除基础弹头

它具有可自定义的选项。

评论


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

–沃尔夫
17年9月14日在2:36

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

–T.Todua
17/09/14在8:17



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

–沃尔夫
17/09/14在11:45



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

–T.Todua
17年9月15日在7:31

“此插件已于2018年9月19日关闭,无法下载。此关闭是永久的。”

– squarecandy
19/12/16在22:51

#12 楼

现在对我而言工作:
'rewrite' => array( 
'slug' => '/',
'with_front' => false
)

插入register_post_type()函数内。