如何为特定类别中的帖子创建固定链接结构

时间:2016-01-19 作者:Travis Pflanz

我正在建立一个单一作者的博客,将包括多个类别。其中一个主要类别是“电影评论”

作者每次看电影都会留下一个简短的评论,即使他们过去看过这部电影并在自己的网站上看过。

对于“电影评论”类别(仅此类别),我需要设置永久链接结构,例如:

这将为同一部电影的每次评论提供一个唯一的URL。

其余类别将只使用/%postname%/

我99%肯定我几年前做过这件事,但那个网站已经不活跃了,我的笔记中没有任何关于这个过程的信息,我似乎也无法通过谷歌搜索WordPress找到任何方向。org论坛或WordPress答案。

2 个回复
SO网友:тнє Sufi

我不确定这是否是最好的解决方案,但它确实有效:

function movie_review_permalink( $url, $post, $leavename ) {
    $category = get_the_category($post->ID); 
    if (  !empty($category) && $category[0]->slug == "test" ) { //change \'test\' to your category slug
        $date=date_create($post->post_date);
        $my_date = date_format($date,"dmY");
        $url= trailingslashit( home_url(\'/\'. $category[0]->slug .\'/\'. $post->post_name .\'-\'. $my_date .\'/\' ) );
    }
    return $url;
}
add_filter( \'post_link\', \'movie_review_permalink\', 10, 3 );
以上代码将使您的帖子成为分类的永久链接testhttp://wpHomeURL/test/post-name-ddmmyyyy 结构

现在,您需要添加重写规则来实现这一点。

function movie_review_rewrite_rules( $wp_rewrite ) {
    $new_rules[\'^test/([^/]+)-([0-9]+)/?\'] = \'index.php?name=$matches[1]\'; //change \'test\' to your category slug
    $wp_rewrite->rules = $new_rules + $wp_rewrite->rules;
    return $wp_rewrite;
}
add_action(\'generate_rewrite_rules\', \'movie_review_rewrite_rules\');
希望这有帮助!

SO网友:gmazzap

我想给你一个“替代”的方法。我很确定你不会明白这一点,但我觉得读起来很有趣。

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

相关推荐

WP JSON list all permalinks

Scenario我正在考虑编写一个自定义的wp json端点,以列出我的wordpress中每个帖子的所有永久链接。Question是否可以使用rest查询+筛选器来完成此操作?例如:。http://wpsite.com/wp-json/wp/v2/posts?filter[only-permalinks]当前的解决方案最后我编写了一个自定义端点,代码如下:添加到函数的底部。phpfunction get_all_slugs() { $posts = get_posts( array(&#