我想给你一个“替代”的方法。我很确定你不会明白这一点,但我觉得读起来很有趣。
OOP“路由”方法在WordPress中,“漂亮”URL与“丑陋”URL匹配。
但大多数web框架(不仅仅是PHP)都使用“路由”的概念:将url与“操作”(或控制器)匹配。
我想告诉您如何使用OOP方法将这种旋转技术应用于WordPress。
接口首先,我们将编写一个接口来明确路由对象应该做什么:
namespace MySite;
interface RouteInterface
{
/**
* Returns true when the route matches for the given url
*
* @return bool
*/
public function matched();
/**
* Returns the WP_Query variables connected to the route for current url.
* Should be empty if route not matched
*
* @return array
*/
public function getQueryArgs();
/**
* Return the url for the route.
* Variable parts of the url should be provided via $args param.
*
* @param array $args
* @return string
*/
public function getUrl(array $args = []);
}
非常简单。
有关详细信息,请阅读文档块。
url对象要“匹配”url,我们首先需要知道它。WordPress没有为此提供函数或am方法。
add_query_arg()
, 当用于传递空数组时,距离足够近。
但是,当WordPress安装在子文件夹中时,子文件夹路径也会返回。。,我们应该删除它,因为它不是我们想要匹配的url的一部分。
让我们为作用域编写一个对象。
namespace MySite;
class WordPressUri
{
public function getUrl()
{
$url = trim(esc_url_raw(add_query_arg([])), \'/\');
// check if wp is in a sub folder
$homePath = trim(parse_url(home_url(), PHP_URL_PATH), \'/\');
// remove WordPress subfolder if any
if ($homePath) {
$url = preg_replace(\'~^(\'.preg_quote($homePath, \'~\').\'){1}~\', \'\', $url);
}
return trim($url, \'/\');
}
}
我觉得还是很简单。
具体路由对象现在,我们可以开始编写实现路由接口的具体路由对象了。
namespace MySite;
final class MovieReviewRoute implements RouteInterface {
const REGEX = \'^movie-review/([\\w]+)-([0-9]{2})([0-9]{2})([0-9]{4})$\';
private $uri;
private $postname = \'\';
public function __construct(WordPressUri $uri = null) {
$this->uri = $uri ? : new WordPressUri();
}
public function matched() {
$matches = [];
if (preg_match(self::REGEX, $this->uri->getUrl(), $matches) !== 1) {
return false;
}
list(, , $day, $month, $year) = array_map(\'intval\', $matches);
if (checkdate($month, $day, $year)) {
$this->postname = $matches[1];
return true;
}
return false;
}
public function getQueryArgs() {
return $this->postname ? [\'name\' => $this->postname] : [];
}
public function getUrl(array $args = []) {
// check if postname was given, or as alternative a post object / post id
$post = empty($args[\'post\']) ? \'\' : get_post($args[\'post\']);
$postname = empty($args[\'postname\']) ? \'\' : $args[\'postname\'];
if ( ! $postname && $post instanceof \\WP_Post) {
$postname = $post->post_name;
}
// if no date given, use post date if post was given, or just today
if (empty($args[\'date\'])) {
$timestamp = $post instanceof \\WP_Post
? strtotime($post->post_date)
: current_time(\'timestamp\');
$args[\'date\'] = date(\'dmY\', $timestamp);
}
return home_url("movie-review/{$postname}-{$args[\'date\']}");
}
}
很抱歉,如果一个地方有很多代码,但它没有任何“特殊”功能。
它只是按照接口的指示,针对特定的情况。
有关方法的一些详细信息:
matched()
使用正则表达式将url与所需格式匹配。它还检查匹配的日期是否为有效日期。在此过程中,它保存了postname
对象变量,因此可以使用它来知道匹配的是哪个帖子。getQueryArgs()
只需返回查询变量“name”,就足以找到帖子getUrl()
组合给定的参数数组,以创建与路由匹配的url。界面需要什么。右钩拳几乎完成了。我们拥有所有的对象,现在我们需要使用它们。我们首先需要一个钩子来拦截请求。
正确的位置是\'do_parse_request\'.
在这个钩子上返回false,我们可以防止WordPress使用重写规则解析url。此外,钩子将的一个实例作为第二个参数传递WP
类:我们可以使用它来设置我们需要的查询变量,以及我们的路由在匹配时可以提供的查询变量。
实际上,我们需要匹配路线的代码:
namespace MySite;
add_filter(\'do_parse_request\', function ($bool, \\WP $wp) {
$movieRoute = new MovieReviewRoute();
// if route matched, let\'s set query vars and stop WP to parse rules
if ($movieRoute->matched()) {
$wp->query_vars = $movieRoute->getQueryArgs();
$wp->matched_rule = MovieReviewRoute::REGEX;
// avoid WordPress to apply canonical redirect
remove_action(\'template_redirect\', \'redirect_canonical\');
// returning false WP will not parse the url
return false;
}
return $bool;
}, 10, 2);
我认为这应该很容易理解。需要注意两件事:
我从中删除了“redirect\\u canonical”函数\'template_redirect\'
否则,WordPress(对我们的路由一无所知)可以将帖子重定向到其“规范”url,即在标准permalink结构中设置的url。
我设置$wp->matched_rule
这使我们能够知道何时通过路由对象设置查询参数。
生成URL
我们的路由正常,但我们需要将用户发送到我们的路由。所以我们需要过滤永久链接。rote对象有一个生成url的方法,我们可以利用该方法来实现作用域。
namespace MySite;
add_filter(\'post_link\', function ($permalink, \\WP_Post $post) {
if (has_category(\'movie-review\', $post)) {
$movieRoute = new MovieReviewRoute();
$permalink = $movieRoute->getUrl([\'post\' => $post]);
}
return $permalink;
}, 10, 2);
使用此代码,任何具有“电影评论”类别的帖子都将获得与我们的路线匹配的url。
目前,“电影评论”类别中的帖子可以通过两个不同的URL查看,一个是标准URL,另一个是与我们的路线匹配的URL。
我们应该防止这种情况,这对搜索引擎优化(SEO)非常有害,尤其是其他方面。
namespace MySite;
add_action(\'template_redirect\', function() {
if (
is_single()
&& has_category(\'movie-review\', get_queried_object())
&& $GLOBALS[\'wp\']->matched_rule !== MovieReviewRoute::REGEX
) {
$movieRoute = new MovieReviewRoute();
wp_redirect($movieRoute->getUrl([\'post\' => get_queried_object()]), 301);
exit();
}
});
由于我们设置了变量
WP
类当我们的路线匹配时,我们能够识别何时使用标准url查看电影评论帖子。
如果发生这种情况,我们只需重定向到路由url。
注:主要问题是:“这值得麻烦吗?”?我可以说,这种方法与重写规则方法没有任何问题。
例如,您可以在管理界面中自由更改帖子slug,并且在该特定类别中仍然有两个不同的帖子url结构。
当然,不仅仅是将两个函数连接到两个挂钩中,而是更多的代码。更多的代码,从来都不是一件好事。
但是,您应该注意到,在使用此方法之后,添加越来越多的路由就容易多了,只需编写一个类,因为接口和匹配的“机制”已经存在。
所以,要回答这个问题:可能no: 就一条路线而言,可能不值得这么麻烦,但如果你有几条,也许值得。
考虑到存在cool libraries 您可以在这种方法中进行集成,以使匹配机制更容易、更强大。
明白了,代码是completely untested. 由于数组语法较短,它需要PHP 5.4+。