分析 WordPress `WP_Comment` 类的源码:它如何封装评论数据,并与 `WP_Comment_Query` 类交互。

大家好,今天咱们来聊聊WordPress评论系统里两个重要的角色:WP_CommentWP_Comment_Query。别被这些名字吓跑,其实它们就像一对好基友,一个负责评论的数据,一个负责把评论找出来。

一、WP_Comment:评论数据的容器

WP_Comment类,顾名思义,就是用来封装一条评论数据的。它就像一个精心设计的盒子,把评论的各种信息都装在里面,方便我们使用。

1.1 类的定义和属性

首先,我们来看看WP_Comment类的基本结构(简化版):

/**
 * Core class used to implement the WP_Comment object.
 *
 * @since 4.4.0
 */
class WP_Comment {

    /**
     * Comment ID.
     *
     * @since 4.4.0
     * @var int
     */
    public $comment_ID = 0;

    /**
     * Comment post ID.
     *
     * @since 4.4.0
     * @var int
     */
    public $comment_post_ID = 0;

    /**
     * Comment author name.
     *
     * @since 4.4.0
     * @var string
     */
    public $comment_author = '';

    /**
     * Comment author email address.
     *
     * @since 4.4.0
     * @var string
     */
    public $comment_author_email = '';

    /**
     * Comment author URL.
     *
     * @since 4.4.0
     * @var string
     */
    public $comment_author_url = '';

    /**
     * Comment author IP address.
     *
     * @since 4.4.0
     * @var string
     */
    public $comment_author_IP = '';

    /**
     * Comment date and time (Y-m-d H:i:s).
     *
     * @since 4.4.0
     * @var string
     */
    public $comment_date = '0000-00-00 00:00:00';

    /**
     * Comment date (localized).
     *
     * @since 4.4.0
     * @var string
     */
    public $comment_date_gmt = '0000-00-00 00:00:00';

    /**
     * Comment content.
     *
     * @since 4.4.0
     * @var string
     */
    public $comment_content = '';

    /**
     * Comment karma.
     *
     * @since 4.4.0
     * @var int
     */
    public $comment_karma = 0;

    /**
     * Is this comment approved?
     *
     * @since 4.4.0
     * @var string
     */
    public $comment_approved = '1';

    /**
     * Comment agent.
     *
     * @since 4.4.0
     * @var string
     */
    public $comment_agent = '';

    /**
     * Comment type.
     *
     * @since 4.4.0
     * @var string
     */
    public $comment_type = 'comment';

    /**
     * Parent comment ID.
     *
     * @since 4.4.0
     * @var int
     */
    public $comment_parent = 0;

    /**
     * Comment user ID.
     *
     * @since 4.4.0
     * @var int
     */
    public $user_id = 0;

    /**
     * Stores the raw comment data array.
     *
     * @since 4.4.0
     * @var array
     */
    public $fields = array();

    //... 其他方法
}

可以看到,这个类定义了一系列的属性,每个属性对应评论表(wp_comments)中的一个字段。 比如$comment_author对应评论者的名字,$comment_content对应评论的内容等等。

用表格的形式总结一下:

属性名 类型 描述 对应数据库字段
$comment_ID int 评论 ID comment_ID
$comment_post_ID int 评论所属文章 ID comment_post_ID
$comment_author string 评论者姓名 comment_author
$comment_author_email string 评论者邮箱 comment_author_email
$comment_author_url string 评论者网站 comment_author_url
$comment_content string 评论内容 comment_content
$comment_date string 评论时间 comment_date
$comment_approved string 评论是否审核通过 (0, 1, spam, trash) comment_approved
$comment_type string 评论类型 (comment, trackback, pingback) comment_type
$user_id int 评论者用户 ID(如果已登录) user_id

1.2 构造函数 __construct()

WP_Comment类的构造函数是用来初始化对象属性的。通常,它会接收一个评论数据(可以是数组或对象),然后把这些数据赋值给相应的属性。

    /**
     * WP_Comment constructor.
     *
     * @since 4.4.0
     *
     * @param WP_Comment|object|array $comment Comment to populate comment data from.
     */
    public function __construct( $comment = null ) {
        if ( is_object( $comment ) ) {
            foreach ( get_object_vars( $comment ) as $key => $value ) {
                $this->$key = $value;
            }
        } elseif ( is_array( $comment ) ) {
            foreach ( $comment as $key => $value ) {
                $this->$key = $value;
            }
        }

        if ( isset( $this->comment_ID ) ) {
            $this->comment_ID = (int) $this->comment_ID;
        }

        $this->fields = (array) $comment;
    }

这个构造函数会判断传入的$comment参数的类型:

  • 如果是对象: 遍历对象的所有属性,并赋值给WP_Comment对象的对应属性。
  • 如果是数组: 遍历数组的所有键值对,并赋值给WP_Comment对象的对应属性。
  • 都不是: 啥也不干,默默等待被赋值。

不管哪种情况,最后都会把原始的评论数据存储到$this->fields属性中,方便以后使用。

1.3 获取评论数据的方法

WP_Comment类还提供了一些方法来获取评论数据的不同形式。虽然大部分属性都是public的,可以直接访问,但使用方法可以进行一些格式化或者安全性处理。

例如:

  • get_comment_author(): 获取评论作者名。
  • get_comment_date( $format = '' ): 获取评论日期,可以指定日期格式。
  • get_comment_text(): 获取评论内容。
  • get_comment_link( $args = array() ): 获取评论的链接。

这些方法通常会对数据进行一些处理,比如转义特殊字符,或者格式化日期。

例子:

假设我们已经有了一个WP_Comment对象$comment,我们可以这样获取评论的信息:

$author = $comment->get_comment_author(); // 直接访问属性
$date = $comment->get_comment_date('Y-m-d'); // 使用方法,格式化日期
$content = $comment->comment_content; // 直接访问属性
echo "评论作者:{$author}, 评论日期:{$date}, 评论内容:{$content}";

二、WP_Comment_Query:评论的查询器

WP_Comment_Query类是用来从数据库中查询评论的。它允许我们设置各种查询参数,比如根据文章 ID、作者、日期等等条件来筛选评论。

2.1 类的定义和属性

WP_Comment_Query类也有很多属性,用来存储查询参数。

/**
 * Core class used for querying comments.
 *
 * @since 3.1.0
 */
class WP_Comment_Query {

    /**
     * SQL query string.
     *
     * @since 3.1.0
     * @access public
     * @var string
     */
    public $request;

    /**
     * List of comment IDs located by the query.
     *
     * @since 3.1.0
     * @access public
     * @var array
     */
    public $comments;

    /**
     * The amount of comments found for the current query.
     *
     * @since 3.1.0
     * @access public
     * @var int
     */
    public $comment_count = 0;

    /**
     * Query vars set by the user.
     *
     * @since 3.1.0
     * @access public
     * @var array
     */
    public $query_vars = array();

    /**
     * The parsed query variables.
     *
     * @since 3.1.0
     * @access public
     * @var array
     */
    public $query;

    /**
     * The default comment query variables.
     *
     * @since 3.1.0
     * @access public
     * @var array
     */
    public $default_query = array(
        'number'                  => '',
        'offset'                  => '',
        'orderby'                 => '',
        'order'                   => 'DESC',
        'fields'                  => 'all',
        'meta_key'                => '',
        'meta_value'              => '',
        'meta_query'              => '',
        'date_query'              => null, // See WP_Date_Query
        'search'                  => '',
        'comment__in'             => '',
        'comment__not_in'         => '',
        'comment_ID'              => '',
        'comment_author_email'    => '',
        'comment_author_url'      => '',
        'comment_author'          => '',
        'comment_author_IP'       => '',
        'comment_karma'           => '',
        'comment_approved'        => '',
        'comment_type'            => '',
        'comment_parent'          => '',
        'comment_post_ID'         => '',
        'comment_post_ID__in'     => '',
        'comment_post_ID__not_in' => '',
        'user_id'                 => '',
        'include_unapproved'      => '',
    );

    //... 其他方法
}

其中,比较重要的属性包括:

  • $request: 存储最终的 SQL 查询语句。
  • $comments: 存储查询结果,是一个WP_Comment对象数组。
  • $comment_count: 存储查询到的评论总数。
  • $query_vars: 存储用户设置的查询参数。
  • $query: 存储解析后的查询参数。
  • $default_query: 存储默认的查询参数。

2.2 构造函数 __construct()

WP_Comment_Query的构造函数接收一个参数,这个参数是一个数组,包含了各种查询参数。

    /**
     * WP_Comment_Query constructor.
     *
     * @since 3.1.0
     *
     * @param string|array $query Optional. Array or query string of comment query parameters.
     */
    public function __construct( $query = '' ) {
        $this->query_vars = wp_parse_args( $query );
        $this->parse_query( $this->query_vars );
        $this->query['fields'] = $this->get_fields();

        // Only query for ids when 'count' is the only query var.
        if ( array_keys( $this->query_vars ) === array( 'count' ) ) {
            $this->query['fields'] = 'ids';
        }

        $this->get_comments();
    }

这个构造函数主要做了以下几件事情:

  1. 使用wp_parse_args()函数,把传入的查询参数和默认参数合并。
  2. 调用parse_query()方法,解析查询参数。
  3. 调用get_fields()方法,获取需要返回的字段。
  4. 调用get_comments()方法,执行查询。

2.3 查询参数

WP_Comment_Query支持非常多的查询参数,可以通过数组的形式传递给构造函数。下面列出一些常用的查询参数:

参数名 类型 描述
number int 返回评论的数量
offset int 偏移量,从第几条评论开始返回
orderby string 排序字段,比如comment_datecomment_ID
order string 排序方式,ASCDESC
post_id (或comment_post_ID) int 只返回指定文章的评论
post_id__in (或comment_post_ID__in) array 只返回指定文章列表的评论
status (或comment_approved) string 返回指定状态的评论,比如approveholdspam
author_email (或comment_author_email) string 返回指定作者邮箱的评论

2.4 执行查询 get_comments()

get_comments()方法是WP_Comment_Query类的核心方法,它负责构建 SQL 查询语句,并执行查询。

    /**
     * Retrieve comments matching the current query vars.
     *
     * @since 3.1.0
     *
     * @global wpdb $wpdb WordPress database abstraction object.
     *
     * @return array|int List of WP_Comment objects, a list of comment IDs when 'fields' is 'ids',
     *                   or the number of comments when 'count' is passed as a query var.
     */
    public function get_comments() {
        global $wpdb;

        $comments = array();

        /**
         * Fires before the comment query is run.
         *
         * @since 3.1.0
         *
         * @param WP_Comment_Query &$this The WP_Comment_Query instance (passed by reference).
         */
        do_action_ref_array( 'pre_get_comments', array( &$this ) );

        /**
         * Filters the comments array before the query is run.
         *
         * Passing a non-null value will short-circuit the query, returning
         * the passed value instead.
         *
         * @since 3.7.0
         *
         * @param array|null      $value   An array of comments, or null to allow WP to run the query.
         * @param WP_Comment_Query &$this The WP_Comment_Query instance (passed by reference).
         */
        $comment_list = apply_filters_ref_array( 'comments_pre_query', array( null, &$this ) );

        if ( ! is_null( $comment_list ) ) {
            $this->comments = $comment_list;
            return $this->comments;
        }

        $this->request = $this->get_sql();

        if ( 'ids' === $this->query['fields'] ) {
            $ids = $wpdb->get_col( $this->request );

            return array_map( 'intval', $ids );
        }

        if ( 'count' === $this->query_vars['count'] ) {
            $this->comment_count = (int) $wpdb->get_var( $this->request );

            return $this->comment_count;
        }

        $comments = $wpdb->get_results( $this->request );

        // Prime comment caches.
        update_comment_cache( $comments );

        // Convert to WP_Comment objects.
        $this->comments = array_map( 'get_comment', $comments );

        /**
         * Fires after the comment query is run.
         *
         * @since 3.1.0
         *
         * @param array             &$comments The array of comment results.
         * @param WP_Comment_Query &$this     The WP_Comment_Query instance (passed by reference).
         */
        do_action_ref_array( 'comments_post_query', array( &$comments, &$this ) );

        return $this->comments;
    }

这个方法的主要步骤包括:

  1. 构建 SQL 查询语句 ( $this->get_sql() )。
  2. 执行 SQL 查询 ( $wpdb->get_results() )。
  3. 把查询结果转换成WP_Comment对象 ( array_map( 'get_comment', $comments ) )。

2.5 获取 SQL 语句 get_sql()

get_sql()方法负责根据查询参数构建 SQL 查询语句。这个方法比较复杂,因为它需要处理各种不同的查询参数,并把它们转换成 SQL 语句的 WHERE 子句。

例子:

假设我们要查询文章 ID 为 123 的所有已审核通过的评论,可以这样使用WP_Comment_Query

$args = array(
    'post_id' => 123,
    'status' => 'approve',
);

$comment_query = new WP_Comment_Query( $args );
$comments = $comment_query->get_comments();

if ( $comments ) {
    foreach ( $comments as $comment ) {
        echo "<p>{$comment->comment_author}: {$comment->comment_content}</p>";
    }
} else {
    echo "没有找到评论。";
}

三、WP_CommentWP_Comment_Query 的交互

现在我们来聊聊WP_CommentWP_Comment_Query是如何一起工作的。

  1. WP_Comment_Query 发起查询: 我们创建一个WP_Comment_Query对象,并传入查询参数。
  2. WP_Comment_Query 构建 SQL 语句: WP_Comment_Query对象根据查询参数构建 SQL 查询语句。
  3. 执行查询并获取数据: WP_Comment_Query对象执行 SQL 查询,从数据库中获取评论数据。
  4. 创建 WP_Comment 对象: WP_Comment_Query对象把每一条评论数据都封装到一个WP_Comment对象中。
  5. 返回 WP_Comment 对象数组: WP_Comment_Query对象返回一个WP_Comment对象数组,包含了所有查询到的评论。

简单来说,WP_Comment_Query负责找到符合条件的评论数据,然后WP_Comment负责把这些数据封装成对象,方便我们使用。

更详细的流程

  1. 调用 WP_Comment_Query: 你的代码(比如主题文件或插件)创建一个 WP_Comment_Query 实例,并传递一个包含查询参数的数组。

    $args = array(
        'post_id' => get_the_ID(), // 获取当前文章 ID
        'status'  => 'approve',    // 只获取已批准的评论
        'number'  => 10,           // 最多获取 10 条评论
    );
    $comments_query = new WP_Comment_Query( $args );
    $comments = $comments_query->get_comments();
  2. WP_Comment_Query 构造 SQL: WP_Comment_Query 的构造函数解析查询参数,并将它们存储在内部属性中。然后,get_comments() 方法被调用。这个方法会调用 get_sql() 方法来构建实际的 SQL 查询语句。 get_sql() 方法会根据你提供的参数构建复杂的 SQL 查询,包括 WHERE 子句、ORDER BY 子句和 LIMIT 子句。

  3. 数据库查询: get_comments() 方法使用 $wpdb 对象(WordPress 的数据库抽象层)来执行 SQL 查询。

    $this->request = $this->get_sql();  // 构建 SQL 查询
    $comments = $wpdb->get_results( $this->request ); // 执行查询
  4. 数据处理和 WP_Comment 对象创建: 数据库返回的结果通常是 PHP 对象或关联数组。 get_comments() 方法遍历这些结果,并使用 get_comment() 函数将每个结果转换成一个 WP_Comment 对象。

    $this->comments = array_map( 'get_comment', $comments );

    get_comment() 函数会检查缓存,如果评论已经存在于缓存中,则直接返回缓存的对象。否则,它会创建一个新的 WP_Comment 对象,并将数据库结果中的数据填充到该对象中。

  5. 返回结果: get_comments() 方法返回一个 WP_Comment 对象数组。你的代码现在可以遍历这个数组,并使用 WP_Comment 对象的方法来访问评论的各种属性。

    if ( $comments ) {
        foreach ( $comments as $comment ) {
            echo '<p>' . $comment->comment_author . ': ' . $comment->comment_content . '</p>';
        }
    }

为什么要使用 WP_Comment 对象?

直接使用数据库查询结果也可以,但使用 WP_Comment 对象有几个优点:

  • 封装性: WP_Comment 对象将评论的所有相关数据封装在一起,方便访问和管理。
  • 一致性: 无论评论来自哪个来源(数据库、缓存等),你都可以使用相同的方法来访问其属性。
  • 扩展性: WP_Comment 类可以被扩展,以添加自定义方法和属性。
  • 缓存: WordPress 使用对象缓存来存储 WP_Comment 对象,以提高性能。

总结

WP_CommentWP_Comment_Query是WordPress评论系统中两个非常重要的类。WP_Comment负责封装评论数据,WP_Comment_Query负责查询评论数据。它们一起工作,为我们提供了方便、高效的方式来管理评论。理解这两个类的原理,可以帮助我们更好地定制和扩展WordPress评论系统。

希望今天的讲解对你有所帮助!下次有机会再见!

发表回复

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