解释 `get_adjacent_post()` 函数的源码,它是如何获取上一篇和下一篇文章的?

哈喽,大家好!今天咱们来聊聊 WordPress 里面的 get_adjacent_post() 函数,看看它到底是怎么“穿梭”于文章之间,找到上一篇和下一篇的。相信很多小伙伴都用过这个函数,但可能对它的内部机制不是特别清楚。没关系,今天咱们就来扒一扒它的“底裤”,保证让你看得明明白白。

一、get_adjacent_post() 函数简介

get_adjacent_post() 函数,顾名思义,就是用来获取当前文章的相邻文章(上一篇或下一篇)的。它返回的是一个 WP_Post 对象,包含了相邻文章的所有信息,比如标题、内容、链接等等。如果找不到相邻文章,就返回 null

函数原型:

<?php
get_adjacent_post( bool $in_same_term = false, string $excluded_terms = '', bool $previous = true, string $taxonomy = 'category' ) : WP_Post|null
?>

参数说明:

  • $in_same_term (bool, optional): 是否只在相同分类/标签下的文章中查找。默认为 false,表示在所有文章中查找。
  • $excluded_terms (string, optional): 排除的分类/标签 ID 列表,多个 ID 用逗号分隔。仅当 $in_same_termtrue 时有效。
  • $previous (bool, optional): true 获取上一篇文章,false 获取下一篇文章。默认为 true
  • $taxonomy (string, optional): 分类/标签的类型。默认为 category,表示分类。如果需要使用标签,可以设置为 post_tag。 仅当 $in_same_termtrue 时有效。

返回值:

  • WP_Post|null: 如果找到相邻文章,则返回一个 WP_Post 对象,否则返回 null

二、get_adjacent_post() 函数的核心逻辑

get_adjacent_post() 函数的底层实现涉及不少 WordPress 的核心函数和数据库查询。为了方便大家理解,我们把它拆解成几个关键步骤:

  1. 全局变量获取:
    首先,函数会尝试从全局变量 $post 中获取当前文章对象。如果 $post 不存在(比如在某些特殊情况下),函数会直接返回 null

    global $post;
    
    if ( ! is_a( $post, 'WP_Post' ) ) {
        return null;
    }
  2. 参数处理:
    接下来,函数会根据传入的参数,构建相应的查询条件。比如,如果 $in_same_termtrue,就需要根据 $taxonomy$excluded_terms 构建分类/标签的查询条件。

  3. SQL 查询构建:
    这是最核心的部分。函数会根据 $previous 参数,构建不同的 SQL 查询语句。

    • 获取上一篇文章($previous = true):
      SQL 查询会查找发布日期小于当前文章的发布日期,并且符合其他查询条件的文章。查询结果按照发布日期降序排列,取第一条记录。

      SELECT  p.ID, p.post_title, p.post_date, p.post_name, p.guid
      FROM {$wpdb->posts} AS p
      WHERE p.post_date < %s
      AND p.post_type = %s
      AND p.post_status = 'publish'
      -- 其他查询条件(例如,分类/标签)
      ORDER BY p.post_date DESC
      LIMIT 1
    • 获取下一篇文章($previous = false):
      SQL 查询会查找发布日期大于当前文章的发布日期,并且符合其他查询条件的文章。查询结果按照发布日期升序排列,取第一条记录。

      SELECT  p.ID, p.post_title, p.post_date, p.post_name, p.guid
      FROM {$wpdb->posts} AS p
      WHERE p.post_date > %s
      AND p.post_type = %s
      AND p.post_status = 'publish'
      -- 其他查询条件(例如,分类/标签)
      ORDER BY p.post_date ASC
      LIMIT 1
  4. 数据库查询执行:
    构建好 SQL 查询语句后,函数会使用 $wpdb->get_row() 方法执行查询,并返回查询结果。

  5. 结果处理:
    如果查询结果不为空,函数会根据查询结果创建一个 WP_Post 对象,并返回该对象。如果查询结果为空,则返回 null

三、代码示例及详细解释

下面我们通过一些代码示例,来更深入地理解 get_adjacent_post() 函数的使用:

示例 1:获取上一篇文章

<?php
$prev_post = get_adjacent_post(); // 默认获取上一篇
if ( $prev_post ) {
    echo '<a href="' . get_permalink( $prev_post->ID ) . '">' . $prev_post->post_title . '</a>';
} else {
    echo '没有上一篇文章了';
}
?>

解释:

  • get_adjacent_post() 函数没有传入任何参数,因此使用默认值:$in_same_term = false$previous = true
  • 函数会查找发布日期小于当前文章的所有文章,并返回发布日期最近的一篇。
  • 如果找到了上一篇文章,就输出一个链接,链接到上一篇文章的页面,并显示上一篇文章的标题。
  • 如果没有找到上一篇文章,就输出“没有上一篇文章了”。

示例 2:获取同一分类下的下一篇文章

<?php
$next_post = get_adjacent_post( true, '', false ); // 获取同一分类下的下一篇
if ( $next_post ) {
    echo '<a href="' . get_permalink( $next_post->ID ) . '">' . $next_post->post_title . '</a>';
} else {
    echo '没有下一篇文章了';
}
?>

解释:

  • get_adjacent_post( true, '', false ) 函数传入了三个参数:
    • $in_same_term = true:表示只在相同分类下的文章中查找。
    • $excluded_terms = '':表示不排除任何分类。
    • $previous = false:表示获取下一篇文章。
  • 函数会查找与当前文章属于同一分类,并且发布日期大于当前文章的所有文章,并返回发布日期最近的一篇。
  • 如果找到了下一篇文章,就输出一个链接,链接到下一篇文章的页面,并显示下一篇文章的标题。
  • 如果没有找到下一篇文章,就输出“没有下一篇文章了”。

示例 3:获取同一标签下,排除特定标签的上一篇文章

<?php
$excluded_tags = '5,10,15'; // 排除标签 ID 为 5, 10, 15 的标签
$prev_post = get_adjacent_post( true, $excluded_tags, true, 'post_tag' ); // 获取同一标签下,排除特定标签的上一篇

if ( $prev_post ) {
    echo '<a href="' . get_permalink( $prev_post->ID ) . '">' . $prev_post->post_title . '</a>';
} else {
    echo '没有上一篇文章了';
}
?>

解释:

  • get_adjacent_post( true, $excluded_tags, true, 'post_tag' ) 函数传入了四个参数:
    • $in_same_term = true:表示只在相同标签下的文章中查找。
    • $excluded_terms = $excluded_tags:表示排除标签 ID 为 5, 10, 15 的标签。
    • $previous = true:表示获取上一篇文章。
    • $taxonomy = 'post_tag':表示使用标签进行查找。
  • 函数会查找与当前文章属于同一标签,并且不属于标签 ID 为 5, 10, 15 的标签,并且发布日期小于当前文章的所有文章,并返回发布日期最近的一篇。
  • 如果找到了上一篇文章,就输出一个链接,链接到上一篇文章的页面,并显示上一篇文章的标题。
  • 如果没有找到上一篇文章,就输出“没有上一篇文章了”。

四、深入探究:SQL 查询语句的构建

让我们更深入地了解一下 SQL 查询语句的构建过程,这能帮助你更好地理解 get_adjacent_post() 函数的工作原理。

首先,我们需要了解几个 WordPress 的全局变量:

  • $wpdb: WordPress 数据库对象,用于执行 SQL 查询。
  • $post: 当前文章对象。

假设我们有以下参数:

  • $in_same_term = true
  • $excluded_terms = '3,7'
  • $previous = true
  • $taxonomy = 'category'

那么,SQL 查询语句的构建过程大致如下:

  1. 基本查询语句:

    SELECT  p.ID, p.post_title, p.post_date, p.post_name, p.guid
    FROM {$wpdb->posts} AS p
    WHERE p.post_type = 'post'
    AND p.post_status = 'publish'
  2. 添加日期条件:
    由于 $previous = true,我们需要查找发布日期小于当前文章的文章。

    AND p.post_date < %s

    这里的 %s 会被 $wpdb->prepare() 函数替换为当前文章的发布日期。

  3. 添加分类条件:
    由于 $in_same_term = true,我们需要添加分类条件。这需要用到 WordPress 的 wp_term_relationships 表和 wp_term_taxonomy 表。

    AND EXISTS (
        SELECT 1
        FROM {$wpdb->term_relationships} AS tr
        INNER JOIN {$wpdb->term_taxonomy} AS tt ON (tr.term_taxonomy_id = tt.term_taxonomy_id)
        WHERE tt.taxonomy = %s
        AND tr.object_id = p.ID
        AND tt.term_id IN ( /* 获取当前文章的所有分类 ID */ )
    )

    这里的 %s 会被 $wpdb->prepare() 函数替换为 $taxonomy 的值(即 ‘category’)。/* 获取当前文章的所有分类 ID */ 部分需要根据当前文章的 ID,查询 wp_term_relationships 表和 wp_term_taxonomy 表,获取当前文章的所有分类 ID。

  4. 添加排除分类条件:
    由于 $excluded_terms = '3,7',我们需要排除分类 ID 为 3 和 7 的分类。

    AND NOT EXISTS (
        SELECT 1
        FROM {$wpdb->term_relationships} AS tr
        INNER JOIN {$wpdb->term_taxonomy} AS tt ON (tr.term_taxonomy_id = tt.term_taxonomy_id)
        WHERE tt.taxonomy = %s
        AND tr.object_id = p.ID
        AND tt.term_id IN (3,7)
    )

    这里的 %s 同样会被 $wpdb->prepare() 函数替换为 $taxonomy 的值(即 ‘category’)。

  5. 添加排序和限制:

    ORDER BY p.post_date DESC
    LIMIT 1

    由于 $previous = true,我们按照发布日期降序排列,并只取第一条记录。

最终,完整的 SQL 查询语句如下:

SELECT  p.ID, p.post_title, p.post_date, p.post_name, p.guid
FROM {$wpdb->posts} AS p
WHERE p.post_type = 'post'
AND p.post_status = 'publish'
AND p.post_date < %s
AND EXISTS (
    SELECT 1
    FROM {$wpdb->term_relationships} AS tr
    INNER JOIN {$wpdb->term_taxonomy} AS tt ON (tr.term_taxonomy_id = tt.term_taxonomy_id)
    WHERE tt.taxonomy = %s
    AND tr.object_id = p.ID
    AND tt.term_id IN ( /* 获取当前文章的所有分类 ID */ )
)
AND NOT EXISTS (
    SELECT 1
    FROM {$wpdb->term_relationships} AS tr
    INNER JOIN {$wpdb->term_taxonomy} AS tt ON (tr.term_taxonomy_id = tt.term_taxonomy_id)
    WHERE tt.taxonomy = %s
    AND tr.object_id = p.ID
    AND tt.term_id IN (3,7)
)
ORDER BY p.post_date DESC
LIMIT 1

这个 SQL 查询语句可能看起来有点复杂,但它实际上就是根据我们传入的参数,构建了一个精确的查询条件,用于找到符合条件的上一篇文章。

五、性能优化建议

get_adjacent_post() 函数在某些情况下可能会导致性能问题,特别是在文章数量很多,或者分类/标签结构很复杂的情况下。以下是一些性能优化建议:

  • 谨慎使用 $in_same_term = true
    如果不需要在相同分类/标签下查找,尽量不要设置 $in_same_term = true。因为这会导致更复杂的 SQL 查询,从而降低查询效率。
  • 合理使用缓存:
    get_adjacent_post() 函数的结果可以缓存起来,避免重复查询数据库。可以使用 WordPress 的 Transient API 或者其他缓存插件来实现。
  • 考虑使用自定义查询:
    如果 get_adjacent_post() 函数无法满足你的需求,或者性能太差,可以考虑自己编写 SQL 查询语句,并进行优化。
  • 确保数据库索引正确:
    确保 wp_posts 表的 post_datepost_type 字段,以及 wp_term_relationships 表的 object_idterm_taxonomy_id 字段都建立了索引。这可以大大提高查询效率。

六、总结与展望

get_adjacent_post() 函数是 WordPress 中一个非常实用的函数,可以方便地获取相邻文章。但是,在使用时需要注意其内部机制和性能问题。希望通过今天的讲解,你对 get_adjacent_post() 函数有了更深入的了解。

功能点 描述
基本功能 获取当前文章的上一篇或下一篇文章。
同分类/标签查找 可以限制只在相同分类或标签下的文章中查找,使用 $in_same_term 参数。
排除分类/标签 可以排除特定的分类或标签,使用 $excluded_terms 参数。
性能优化 谨慎使用 $in_same_term,合理使用缓存,考虑自定义查询,确保数据库索引正确。
SQL 查询 函数内部会根据参数构建 SQL 查询语句,用于查找符合条件的文章。理解 SQL 查询语句的构建过程有助于更好地理解函数的工作原理。
返回值 返回 WP_Post 对象或 null

当然,WordPress 还在不断发展,未来可能会有更高效、更灵活的获取相邻文章的方法。让我们一起期待吧!

好啦,今天的讲座就到这里。希望对大家有所帮助!如果有任何问题,欢迎在评论区留言讨论。咱们下期再见!

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注