Make WordPress Core


Ignore:
Timestamp:
09/25/2015 03:12:09 PM (9 years ago)
Author:
boonebgorges
Message:

Introduce hierarchical query support to WP_Comment_Query.

Comments can be threaded. Now your query can be threaded too! Bonus: it's
not totally insane.

  • The new $hierarchical parameter for WP_Comment_Query accepts three values:
    • false - Default value, and equivalent to current behavior. No descendants are fetched for matched comments.
    • 'flat' - WP_Comment_Query will fetch the descendant tree for each comment matched by the query paramaters, and append them to the flat array of comments returned. Use this when you have a separate routine for constructing the tree - for example, when passing a list of comments to a Walker object.
    • 'threaded' - WP_Comment_Query will fetch the descendant tree for each comment, and return it in a tree structure located in the children property of the WP_Comment objects.
  • WP_Comment now has a few utility methods for fetching the descendant tree (get_children()), fetching a single direct descendant comment (get_child()), and adding anothing WP_Comment object as a direct descendant (add_child()). Note that add_child() only modifies the comment object - it does not touch the database.

Props boonebgorges, wonderboymusic.
See #8071.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/wp-includes/class-wp-comment-query.php

    r34544 r34546  
    138138     * @since 4.2.0
    139139     * @since 4.4.0 `$parent__in` and `$parent__not_in` were added.
    140      * @since 4.4.0 Order by `comment__in` was added. `$update_comment_meta_cache` and `$no_found_rows` were added.
     140     * @since 4.4.0 Order by `comment__in` was added. `$update_comment_meta_cache`, `$no_found_rows`,
     141     *              and `$hierarchical` were added.
    141142     * @access public
    142143     *
     
    207208     *     @type array        $type__not_in        Exclude comments from a given array of comment types. Default empty.
    208209     *     @type int          $user_id             Include comments for a specific user ID. Default empty.
     210     *     @type bool|string  $hierarchical        Whether to include comment descendants in the results.
     211     *                                             'threaded' returns a tree, with each comment's children stored
     212     *                                             in a `children` property on the `WP_Comment` object. 'flat'
     213     *                                             returns a flat array of found comments plus their children.
     214     *                                             Pass `false` to leave out descendants. The parameter is ignored
     215     *                                             (forced to `false`) when `$fields` is 'ids' or 'counts'.
     216     *                                             Accepts 'threaded', 'flat', or false. Default: false.
    209217     *     @type bool         $update_comment_meta_cache Whether to prime the metadata cache for found comments.
    210218     *                                                   Default true.
     
    250258            'meta_query' => '',
    251259            'date_query' => null, // See WP_Date_Query
     260            'hierarchical' => false,
    252261            'update_comment_meta_cache' => true,
    253262        );
     
    397406        $comments = array_map( 'get_comment', $_comments );
    398407
     408        if ( $this->query_vars['hierarchical'] ) {
     409            $comments = $this->fill_descendants( $comments );
     410        }
     411
    399412        $this->comments = $comments;
    400413        return $this->comments;
     
    664677                $this->sql_clauses['where']['comment_type__' . strtolower( str_replace( ' ', '_', $operator ) ) ] = "comment_type $operator ($types_sql)";
    665678            }
     679        }
     680
     681        if ( $this->query_vars['hierarchical'] && ! $this->query_vars['parent'] ) {
     682            $this->query_vars['parent'] = 0;
    666683        }
    667684
     
    796813            return array_map( 'intval', $comment_ids );
    797814        }
     815    }
     816
     817    /**
     818     * Fetch descendants for located comments.
     819     *
     820     * Instead of calling `get_children()` separately on each child comment, we do a single set of queries to fetch
     821     * the descendant trees for all matched top-level comments.
     822     *
     823     * @since 4.4.0
     824     *
     825     * @param array $comments Array of top-level comments whose descendants should be filled in.
     826     * @return array
     827     */
     828    protected function fill_descendants( $comments ) {
     829        global $wpdb;
     830
     831        $levels = array(
     832            0 => wp_list_pluck( $comments, 'comment_ID' ),
     833        );
     834
     835        $where_clauses = $this->sql_clauses['where'];
     836        unset(
     837            $where_clauses['parent'],
     838            $where_clauses['parent__in'],
     839            $where_clauses['parent__not_in']
     840        );
     841
     842        // Fetch an entire level of the descendant tree at a time.
     843        $level = 0;
     844        do {
     845            $parent_ids = $levels[ $level ];
     846            $where = 'WHERE ' . implode( ' AND ', $where_clauses ) . ' AND comment_parent IN (' . implode( ',', array_map( 'intval', $parent_ids ) ) . ')';
     847            $comment_ids = $wpdb->get_col( "{$this->sql_clauses['select']} {$this->sql_clauses['from']} {$where} {$this->sql_clauses['groupby']}" );
     848
     849            $level++;
     850            $levels[ $level ] = $comment_ids;
     851        } while ( $comment_ids );
     852
     853        // Prime comment caches for non-top-level comments.
     854        $descendant_ids = array();
     855        for ( $i = 1; $i < count( $levels ); $i++ ) {
     856            $descendant_ids = array_merge( $descendant_ids, $levels[ $i ] );
     857        }
     858
     859        _prime_comment_caches( $descendant_ids, $this->query_vars['update_comment_meta_cache'] );
     860
     861        // Assemble a flat array of all comments + descendants.
     862        $all_comments = $comments;
     863        foreach ( $descendant_ids as $descendant_id ) {
     864            $all_comments[] = get_comment( $descendant_id );
     865        }
     866
     867        // If a threaded representation was requested, build the tree.
     868        if ( 'threaded' === $this->query_vars['hierarchical'] ) {
     869            $threaded_comments = $ref = array();
     870            foreach ( $all_comments as $k => $c ) {
     871                $_c = get_comment( $c->comment_ID );
     872
     873                // If the comment isn't in the reference array, it goes in the top level of the thread.
     874                if ( ! isset( $ref[ $c->comment_parent ] ) ) {
     875                    $threaded_comments[ $_c->comment_ID ] = $_c;
     876                    $ref[ $_c->comment_ID ] = $threaded_comments[ $_c->comment_ID ];
     877
     878                // Otherwise, set it as a child of its parent.
     879                } else {
     880
     881                    $ref[ $_c->comment_parent ]->add_child( $_c );
     882//                  $ref[ $c->comment_parent ]->children[ $c->comment_ID ] = $c;
     883                    $ref[ $_c->comment_ID ] = $ref[ $_c->comment_parent ]->get_child( $_c->comment_ID );
     884                }
     885            }
     886
     887            $comments = $threaded_comments;
     888        } else {
     889            $comments = $all_comments;
     890        }
     891
     892        return $comments;
    798893    }
    799894
Note: See TracChangeset for help on using the changeset viewer.