WordPress.org

Make WordPress Core

Ticket #18694: 18694.2.patch

File 18694.2.patch, 15.2 KB (added by Viper007Bond, 7 years ago)

Final patch? Adds date query support to posts and comments

  • wp-includes/comment.php

     
    222222                        'type' => '',
    223223                        'user_id' => '',
    224224                        'search' => '',
    225                         'count' => false
     225                        'count' => false,
     226                        'date_query' => null, // See WP_Date_Query
    226227                );
    227228
    228229                $this->query_vars = wp_parse_args( $query_vars, $defaults );
     
    324325                if ( '' !== $search )
    325326                        $where .= $this->get_search_sql( $search, array( 'comment_author', 'comment_author_email', 'comment_author_url', 'comment_author_IP', 'comment_content' ) );
    326327
     328                if ( ! empty( $date_query ) && is_array( $date_query ) ) {
     329                        $date_query_object = new WP_Date_Query( $date_query, 'comment_date' );
     330                        $where .= $date_query_object->get_sql();
     331                }
     332
    327333                $post_fields = array_filter( compact( array( 'post_author', 'post_name', 'post_parent', 'post_status', 'post_type', ) ) );
    328334                if ( ! empty( $post_fields ) ) {
    329335                        $join = "JOIN $wpdb->posts ON $wpdb->posts.ID = $wpdb->comments.comment_post_ID";
  • wp-includes/query.php

     
    869869        var $meta_query = false;
    870870
    871871        /**
     872         * Date query container
     873         *
     874         * @since 3.5.0
     875         * @access public
     876         * @var object WP_Date_Query
     877         */
     878        var $date_query = false;
     879
     880        /**
    872881         * Holds the data for a single object that is queried.
    873882         *
    874883         * Holds the contents of a post, page, category, attachment.
     
    20562065                                $where .= " AND SECOND($wpdb->posts.post_date)=" . substr($q['m'], 12, 2);
    20572066                }
    20582067
     2068                // Handle individual date parameters
     2069                $date_parameters = array();
     2070
    20592071                if ( '' !== $q['hour'] )
    2060                         $where .= " AND HOUR($wpdb->posts.post_date)='" . $q['hour'] . "'";
     2072                        $date_parameters['hour'] = $q['hour'];
    20612073
    20622074                if ( '' !== $q['minute'] )
    2063                         $where .= " AND MINUTE($wpdb->posts.post_date)='" . $q['minute'] . "'";
     2075                        $date_parameters['minute'] = $q['minute'];
    20642076
    20652077                if ( '' !== $q['second'] )
    2066                         $where .= " AND SECOND($wpdb->posts.post_date)='" . $q['second'] . "'";
     2078                        $date_parameters['second'] = $q['second'];
    20672079
    20682080                if ( $q['year'] )
    2069                         $where .= " AND YEAR($wpdb->posts.post_date)='" . $q['year'] . "'";
     2081                        $date_parameters['year'] = $q['year'];
    20702082
    20712083                if ( $q['monthnum'] )
    2072                         $where .= " AND MONTH($wpdb->posts.post_date)='" . $q['monthnum'] . "'";
     2084                        $date_parameters['month'] = $q['monthnum'];
    20732085
    20742086                if ( $q['day'] )
    2075                         $where .= " AND DAYOFMONTH($wpdb->posts.post_date)='" . $q['day'] . "'";
     2087                        $date_parameters['day'] = $q['day'];
    20762088
     2089                if ( $q['w'] )
     2090                        $date_parameters['week'] = $q['w'];
     2091
     2092                if ( ! empty( $date_parameters ) ) {
     2093                        $date_query = new WP_Date_Query( array( $date_parameters ) );
     2094                        $where .= $date_query->get_sql();
     2095                }
     2096                unset( $date_parameters );
     2097
     2098                // Handle complex date queries
     2099                if ( ! empty( $q['date_query'] ) ) {
     2100                        $this->date_query = new WP_Date_Query( $q['date_query'] );
     2101                        $where .= $this->date_query->get_sql();
     2102                }
     2103
    20772104                // If we've got a post_type AND its not "any" post_type.
    20782105                if ( !empty($q['post_type']) && 'any' != $q['post_type'] ) {
    20792106                        foreach ( (array)$q['post_type'] as $_post_type ) {
     
    21422169                        $where .= " AND $wpdb->posts.post_name = '" . $q['attachment'] . "'";
    21432170                }
    21442171
    2145                 if ( $q['w'] )
    2146                         $where .= ' AND ' . _wp_mysql_week( "`$wpdb->posts`.`post_date`" ) . " = '" . $q['w'] . "'";
    21472172
    21482173                if ( intval($q['comments_popup']) )
    21492174                        $q['p'] = absint($q['comments_popup']);
     
    35203545}
    35213546
    35223547/**
     3548 * WP_Date_Query will generate a MySQL WHERE clause for the specified date-based parameters.
     3549 *
     3550 * Initialize the class by passing an array of arrays of parameters. Example:
     3551 *
     3552 * $date_query = new WP_Date_Query( array(
     3553 *              'column' => 'optional, column to query against, default is post_date',
     3554 *              'compare' => 'optional, see WP_Date_Query::get_compare()',
     3555 *              'relation' => 'optional, OR or AND, how the sub-arrays should be compared, default is AND',
     3556 *              array(
     3557 *                      'column' => 'see above',
     3558 *                      'compare' => 'see above',
     3559 *                      'after' => 'string or array, see WP_Date_Query::build_mysql_datetime()',
     3560 *                      'before' => 'string or array, see WP_Date_Query::build_mysql_datetime()',
     3561 *                      'inclusive' => 'boolean, for after/before, whether exact value should be matched or not',
     3562 *                      'year' => '4 digit int',
     3563 *                      'month' => 'int, 1-12',
     3564 *                      'week' => 'int, 0-53',
     3565 *                      'day' => 'int, 1-31',
     3566 *                      'hour' => 'int, 0-23',
     3567 *                      'minute' => 'int, 0-60',
     3568 *                      'second' => 'int, 0-60',
     3569 *              ),
     3570 * ) );
     3571 *
     3572 * Then call the get_sql() method to get the MySQL WHERE string:
     3573 *
     3574 * $where .= $date_query->get_sql();
     3575 *
     3576 * @since 3.5.0
     3577 */
     3578class WP_Date_Query {
     3579        /**
     3580         * List of date queries.
     3581         *
     3582         * @since 3.5.0
     3583         * @access public
     3584         * @var array
     3585         */
     3586        public $queries = array();
     3587
     3588        /**
     3589         * The relation between the queries. Can be either 'AND' or 'OR' and can be changed via the query arguments.
     3590         *
     3591         * @since 3.5.0
     3592         * @access public
     3593         * @var string
     3594         */
     3595        public $relation = 'AND';
     3596
     3597        /**
     3598         * The column to query against. Can be changed via the query arguments.
     3599         *
     3600         * @since 3.5.0
     3601         * @access public
     3602         * @var string
     3603         */
     3604        public $column = 'post_date';
     3605
     3606        /**
     3607         * The value comparison operator. Can be changed via the query arguments.
     3608         *
     3609         * @since 3.5.0
     3610         * @access public
     3611         * @var array
     3612         */
     3613        public $compare = '=';
     3614
     3615        /**
     3616         * Constructor
     3617         *
     3618         * @param array $date_query A date query parameter array, see class descriptor for further details.
     3619         * @param array (optional) $default_column What column name to query against. Defaults to "post_date".
     3620         */
     3621        function __construct( $date_query, $default_column = 'post_date' ) {
     3622                if ( empty( $date_query ) || ! is_array( $date_query ) )
     3623                        return;
     3624
     3625                if ( isset( $date_query['relation'] ) && strtoupper( $date_query['relation'] ) == 'OR' ) {
     3626                        $this->relation = 'OR';
     3627                } else {
     3628                        $this->relation = 'AND';
     3629                }
     3630
     3631                if ( ! empty( $date_query['column'] ) )
     3632                        $this->column = esc_sql( $date_query['column'] );
     3633                else
     3634                        $this->column = esc_sql( $default_column );
     3635
     3636                $this->compare = $this->get_compare( $date_query );
     3637
     3638                // If an array of arrays wasn't passed, fix it
     3639                if ( ! isset( $date_query[0] ) )
     3640                        $date_query = array( $date_query );
     3641
     3642                $this->queries = array();
     3643                foreach ( $date_query as $key => $query ) {
     3644                        if ( ! is_array( $query ) )
     3645                                continue;
     3646
     3647                        $this->queries[$key] = $query;
     3648                }
     3649        }
     3650
     3651        /**
     3652         * Determines and validates what comparison operator to use.
     3653         *
     3654         * @since 3.5.0
     3655         * @access public
     3656         *
     3657         * @param array $query A date query or a date subquery
     3658         * @return string The comparison operator
     3659         */
     3660        public function get_compare( $query ) {
     3661                if ( ! empty( $query['compare'] ) && in_array( $query['compare'], array( '=', '!=', '>', '>=', '<', '<=', 'IN', 'NOT IN', 'BETWEEN', 'NOT BETWEEN' ) ) )
     3662                        return strtoupper( $query['compare'] );
     3663
     3664                return $this->compare;
     3665        }
     3666
     3667        /**
     3668         * Turns an array of date query parameters into a MySQL string.
     3669         *
     3670         * @since 3.5.0
     3671         * @access public
     3672         *
     3673         * @return string MySQL WHERE parameters
     3674         */
     3675        public function get_sql() {
     3676                global $wpdb;
     3677
     3678                // The parts of the final query
     3679                $where = array();
     3680
     3681                foreach ( $this->queries as $key => $query ) {
     3682
     3683                        // The sub-parts of a $where part
     3684                        $where_parts = array();
     3685
     3686                        $column = ( ! empty( $query['column'] ) ) ? esc_sql( $query['column'] ) : $this->column;
     3687
     3688                        $compare = $this->get_compare( $query );
     3689
     3690                        $lt = '<';
     3691                        $gt = '>';
     3692                        if ( ! empty( $query['inclusive'] ) ) {
     3693                                $lt .= '=';
     3694                                $gt .= '=';
     3695                        }
     3696
     3697                        // Range queries
     3698                        if ( ! empty( $query['after'] ) )
     3699                                $where_parts[] = $wpdb->prepare( "$column $gt %s", $this->build_mysql_datetime( $query['after'] ) );
     3700
     3701                        if ( ! empty( $query['before'] ) )
     3702                                $where_parts[] = $wpdb->prepare( "$column $lt %s", $this->build_mysql_datetime( $query['before'] ) );
     3703
     3704                        // Specific value queries
     3705
     3706                        if ( isset( $query['year'] ) && $value = $this->build_value( $compare, $query['year'] ) )
     3707                                $where_parts[] = "YEAR( $column ) $compare $value";
     3708
     3709                        if ( isset( $query['month'] ) && $value = $this->build_value( $compare, $query['month'] ) )
     3710                                $where_parts[] = "MONTH( $column ) $compare $value";
     3711
     3712                        // Legacy
     3713                        if ( isset( $query['monthnum'] ) && $value = $this->build_value( $compare, $query['monthnum'] ) )
     3714                                $where_parts[] = "MONTH( $column ) $compare $value";
     3715
     3716                        if ( isset( $query['week'] ) && false !== ( $value = $this->build_value( $compare, $query['week'] ) ) )
     3717                                $where_parts[] = _wp_mysql_week( $column ) . " $compare $value";
     3718
     3719                        // Legacy
     3720                        if ( isset( $query['w'] ) && false !== ( $value = $this->build_value( $compare, $query['w'] ) ) )
     3721                                $where_parts[] = _wp_mysql_week( $column ) . " $compare $value";
     3722
     3723                        if ( isset( $query['dayofyear'] ) && $value = $this->build_value( $compare, $query['dayofyear'] ) )
     3724                                $where_parts[] = "DAYOFYEAR( $column ) $compare $value";
     3725
     3726                        if ( isset( $query['day'] ) && $value = $this->build_value( $compare, $query['day'] ) )
     3727                                $where_parts[] = "DAYOFMONTH( $column ) $compare $value";
     3728
     3729                        if ( isset( $query['dayofweek'] ) && $value = $this->build_value( $compare, $query['dayofweek'] ) )
     3730                                $where_parts[] = "DAYOFWEEK( $column ) $compare $value";
     3731
     3732                        if ( isset( $query['hour'] ) || isset( $query['minute'] ) || isset( $query['second'] ) ) {
     3733                                // Avoid notices
     3734                                foreach ( array( 'hour', 'minute', 'second' ) as $unit ) {
     3735                                        if ( ! isset( $query[$unit] ) ) {
     3736                                                $query[$unit] = null;
     3737                                        }
     3738                                }
     3739
     3740                                if ( $time_query = $this->build_time_query( $column, $compare, $query['hour'], $query['minute'], $query['second'] ) ) {
     3741                                        $where_parts[] = $time_query;
     3742                                }
     3743                        }
     3744
     3745                        // Combine the parts of this subquery into a single string
     3746                        if ( ! empty( $where_parts ) )
     3747                                $where[$key] = '( ' . implode( ' AND ', $where_parts ) . ' )';
     3748                }
     3749
     3750                // Combine the subquery strings into a single string
     3751                if ( ! empty( $where ) )
     3752                        $where = ' AND ( ' . implode( " {$this->relation} ", $where ) . ' )';
     3753                else
     3754                        $where = '';
     3755
     3756                return apply_filters( 'get_date_sql', $where, $this );
     3757        }
     3758
     3759        /**
     3760         * Builds and validates a value string based on the comparison operator.
     3761         *
     3762         * @since 3.5.0
     3763         * @access public
     3764         *
     3765         * @param string $compare The compare operator to use
     3766         * @param string|array $value The value
     3767         * @return string|int|false The value to be used in SQL or false on error.
     3768         */
     3769        public function build_value( $compare, $value ) {
     3770                if ( ! isset( $value ) )
     3771                        return false;
     3772
     3773                switch ( $compare ) {
     3774                        case 'IN':
     3775                        case 'NOT IN':
     3776                                return '(' . implode( ',', array_map( 'intval', (array) $value ) ) . ')';
     3777
     3778                        case 'BETWEEN':
     3779                        case 'NOT BETWEEN':
     3780                                if ( ! is_array( $value ) || 2 != count( $value ) || ! isset( $value[0] ) || ! isset( $value[1] ) )
     3781                                        $value = array( $value, $value );
     3782
     3783                                $value = array_map( 'intval', $value );
     3784
     3785                                return $value[0] . ' AND ' . $value[1];
     3786
     3787                        default;
     3788                                return (int) $value;
     3789                }
     3790        }
     3791
     3792        /**
     3793         * Builds a MySQL format date/time based on some query parameters.
     3794         *
     3795         * You can pass an array of values (year, month, etc.) with missing parameter values being defaulted to 0
     3796         * or you can pass a string that will be run through strtotime().
     3797         *
     3798         * @since 3.5.0
     3799         * @access public
     3800         *
     3801         * @param string|array $datetime An array of parameters or a strotime() string
     3802         * @return string|false A MySQL format date/time or false on failure
     3803         */
     3804        public function build_mysql_datetime( $datetime ) {
     3805                $now = current_time( 'timestamp' );
     3806
     3807                if ( is_array( $datetime ) ) {
     3808                        if ( ! isset( $datetime['year'] ) )
     3809                                $datetime['year'] = gmdate( 'Y', $now );
     3810
     3811                        if ( ! isset( $datetime['month'] ) )
     3812                                $datetime['month'] = 1;
     3813
     3814                        if ( ! isset( $datetime['day'] ) )
     3815                                $datetime['day'] = 1;
     3816
     3817                        if ( ! isset( $datetime['hour'] ) )
     3818                                $datetime['hour'] = 0;
     3819
     3820                        if ( ! isset( $datetime['minute'] ) )
     3821                                $datetime['minute'] = 0;
     3822
     3823                        if ( ! isset( $datetime['second'] ) )
     3824                                $datetime['second'] = 0;
     3825
     3826                        $datetime = array_map( 'absint', $datetime );
     3827
     3828                        return sprintf( '%04d-%02d-%02d %02d:%02d:%02d', $datetime['year'], $datetime['month'], $datetime['day'], $datetime['hour'], $datetime['minute'], $datetime['second'] );
     3829                } else {
     3830                        // Timezone issues here possibly
     3831                        return gmdate( 'Y-m-d H:i:s', strtotime( $datetime, $now ) );
     3832                }
     3833
     3834                return false;
     3835        }
     3836
     3837        /**
     3838         * Builds a query string for comparing time values (hour, minute, second).
     3839         *
     3840         * If just hour, minute, or second is set than a normal comparison will be done.
     3841         * However if multiple values are passed, a pseudo-decimal time will be created
     3842         * in order to be able to accurately compare against.
     3843         *
     3844         * @since 3.5.0
     3845         * @access public
     3846         *
     3847         * @param string $column The column to query against. Needs to be pre-validated!
     3848         * @param string $compare The comparison operator. Needs to be pre-validated!
     3849         * @param int|null $hour Optional. An hour value (0-23).
     3850         * @param int|null $minute Optional. A minute value (0-59).
     3851         * @param int|null $second Optional. A second value (0-59).
     3852         * @return string|false A query part or false on failure.
     3853         */
     3854        public function build_time_query( $column, $compare, $hour = null, $minute = null, $second = null ) {
     3855                global $wpdb;
     3856
     3857                // Have to have at least one
     3858                if ( ! isset( $hour ) && ! isset( $minute ) && ! isset( $second ) )
     3859                        return false;
     3860
     3861                // Complex combined queries aren't supported for multi-value queries
     3862                if ( in_array( $compare, array( 'IN', 'NOT IN', 'BETWEEN', 'NOT BETWEEN' ) ) ) {
     3863                        $return = array();
     3864
     3865                        if ( isset( $hour ) && false !== ( $value = $this->build_value( $compare, $hour ) ) )
     3866                                $return[] = "HOUR( $column ) $compare $value";
     3867
     3868                        if ( isset( $minute ) && false !== ( $value = $this->build_value( $compare, $minute ) ) )
     3869                                $return[] = "MINUTE( $column ) $compare $value";
     3870
     3871                        if ( isset( $second ) && false !== ( $value = $this->build_value( $compare, $second ) ) )
     3872                                $return[] = "SECOND( $column ) $compare $value";
     3873
     3874                        return implode( ' AND ', $return );
     3875                }
     3876
     3877                // Cases where just one unit is set
     3878                if ( isset( $hour ) && ! isset( $minute ) && ! isset( $second ) && false !== ( $value = $this->build_value( $compare, $hour ) ) ) {
     3879                        return "HOUR( $column ) $compare $value";
     3880                } elseif ( ! isset( $hour ) && isset( $minute ) && ! isset( $second ) && false !== ( $value = $this->build_value( $compare, $minute ) ) ) {
     3881                        return "MINUTE( $column ) $compare $value";
     3882                } elseif ( ! isset( $hour ) && ! isset( $minute ) && isset( $second ) && false !== ( $value = $this->build_value( $compare, $second ) ) ) {
     3883                        return "SECOND( $column ) $compare $value";
     3884                }
     3885
     3886                // Single units were already handled. Since hour & second isn't allowed, minute must to be set.
     3887                if ( ! isset( $minute ) )
     3888                        return false;
     3889
     3890                $format = $time = '';
     3891
     3892                // Hour
     3893                if ( $hour ) {
     3894                        $format .= '%H.';
     3895                        $time   .= sprintf( '%02d', $hour ) . '.';
     3896                } else {
     3897                        $format .= '0.';
     3898                        $time   .= '0.';
     3899                }
     3900
     3901                // Minute
     3902                $format .= '%i';
     3903                $time   .= sprintf( '%02d', $minute );
     3904
     3905                if ( isset( $second ) ) {
     3906                        $format .= '%s';
     3907                        $time   .= sprintf( '%02d', $second );
     3908                }
     3909
     3910                return $wpdb->prepare( "DATE_FORMAT( $column, %s ) $compare %f", $format, $time );
     3911        }
     3912}
     3913
     3914/**
    35233915 * Redirect old slugs to the correct permalink.
    35243916 *
    35253917 * Attempts to find the current slug from the past slugs.