更新
自从编写此WordPress核心以来,添加了'do_parse_request'
挂钩,该挂钩允许优雅地处理URL路由,而无需扩展WP
类。我在2014年亚特兰大WordCamp演讲中深入探讨了该主题,主题为“ 硬核URL路由 ”;可以在链接上找到幻灯片。
原始答案
十多年来,URL设计一直很重要。几年前,我什至写了一个博客。而且,尽管WordPress是一个很棒的软件,但是 不幸的是,它的URL重写系统简直是脑筋急转(当然,恕我直言:)。无论如何,很高兴看到人们关心URL设计!
我要提供的答案是一个我要调用的插件WP_Extended
,它是Trac上该提案 的概念证明(请注意,该提案最初是一件事,后来演变为另一件事,因此您必须阅读整个文章才能了解其中的内容。它是前进的。)
基本上,这个想法是对类进行子WP
类化,重写parse_request()
方法,然后为全局$wp
变量分配子类的实例。然后,在其中parse_request()
实际检查每个路径段的路径,而不是使用必须完全匹配URL的正则表达式列表。
因此,要明确指出,该技术会在其前面插入逻辑,parse_request()
以检查URL到RegEx匹配项,然后首先查找分类术语匹配项,但仅替换parse_request()
并保留整个WordPress URL路由系统的其余部分,包括和尤其是$query_vars
变量的使用。
对于您的用例,此实现仅将URL路径段与分类术语进行比较,因为这就是您所需要的。此实现检查分类术语尊重父子长期合作关系,并在找到匹配其指定的URL路径(减去开头和结尾的斜杠)来$wp->query_vars['category_name']
,$wp->query_vars['tag']
或$wp->query_vars['taxonomy']
与$wp->query_vars['term']
它绕过parse_request()
的方法WP
类。
另一方面,如果URL路径与您指定的分类法中的术语不匹配,则可以通过调用类的parse_request()
方法将URL路由逻辑委托给WordPress重写系统WP
。
要WP_Extended
用于用例,您需要register_url_route()
从主题functions.php
文件中调用该函数,如下所示:
add_action('init','init_forum_url_route');
function init_forum_url_route() {
register_url_route(array('taxonomy'=>'forum'));
}
以下是该插件的源代码:
<?php
/*
Filename: wp-extended.php
Plugin Name: WP Extended for Taxonomy URL Routes
Author: Mike Schinkel
*/
function register_url_route($args=array()) {
if (isset($args['taxonomy']))
WP_Extended::register_taxonomy_url($args['taxonomy']);
}
class WP_Extended extends WP {
static $taxonomies = array();
static function on_load() {
add_action('setup_theme',array(__CLASS__,'setup_theme'));
}
static function register_taxonomy_url($taxonomy) {
self::$taxonomies[$taxonomy] = get_taxonomy($taxonomy);
}
static function setup_theme() { // Setup theme is 1st code run after WP is created.
global $wp;
$wp = new WP_Extended(); // Replace the global $wp
}
function parse_request($extra_query_vars = '') {
$path = $_SERVER['REQUEST_URI'];
$domain = str_replace('.','\.',$_SERVER['SERVER_NAME']);
//$root_path = preg_replace("#^https?://{$domain}(/.*)$#",'$1',WP_SITEURL);
$root_path = $_SERVER['HTTP_HOST'];
if (substr($path,0,strlen($root_path))==$root_path)
$path = substr($path,strlen($root_path));
list($path) = explode('?',$path);
$path_segments = explode('/',trim($path,'/'));
$taxonomy_term = array();
$parent_id = 0;
foreach(self::$taxonomies as $taxonomy_slug => $taxonomy) {
$terms = get_terms($taxonomy_slug);
foreach($path_segments as $segment_index => $path_segment) {
foreach($terms as $term_index => $term) {
if ($term->slug==$path_segments[$segment_index]) {
if ($term->parent!=$parent_id) { // Make sure we test parents
$taxonomy_term = array();
} else {
$parent_id = $term->term_id; // Capture parent ID for verification
$taxonomy_term[] = $term->slug; // Collect slug as path segment
unset($terms[$term_index]); // No need to scan it again
}
break;
}
}
}
if (count($taxonomy_term))
break;
}
if (count($taxonomy_term)) {
$path = implode('/',$taxonomy_term);
switch ($taxonomy_slug) {
case 'category':
$this->query_vars['category_name'] = $path;
break;
case 'post_tag':
$this->query_vars['tag'] = $path;
break;
default:
$this->query_vars['taxonomy'] = $taxonomy_slug;
$this->query_vars['term'] = $path;
break;
}
} else {
parent::parse_request($extra_query_vars); // Delegate to WP class
}
}
}
WP_Extended::on_load();
PS CAVEAT#1
尽管对于特定网站我认为该技术效果很好,但是绝不应该使用该技术来将插件分发到WordPress.org上供其他人使用。如果它是基于WordPress的软件包的核心,那么这可能还可以。否则,该技术应仅限于改进特定站点的URL路由。
为什么?因为只有一个插件可以使用此技术。如果两个插件尝试使用它,它们将彼此冲突。
顺便说一句,该策略可以扩展为一般性地处理几乎所有可能需要的用例模式,这就是我打算在找到空闲时间或可以赞助时间的客户后立即实施的目标。构建完全通用的实现。
小穴#2
我写了这个重写来覆盖parse_request()
这是一个非常大的函数,很可能我错过了本$wp
应设置的一个或两个全局对象的属性。因此,如果发生了一些奇怪的事情,请告诉我,我很乐意研究它并根据需要修改答案。
无论如何...
'slug' => 'forums'
空白完全删除而只留空白'rewrite' => array('with_front' => false, 'hierarchical' => true)
?我认为过去对我有用。另外,请确保您冲洗了永久链接。