diff --git a/includes/core/classes/class-event-query.php b/includes/core/classes/class-event-query.php index f59092a5b..fe97aa0b9 100644 --- a/includes/core/classes/class-event-query.php +++ b/includes/core/classes/class-event-query.php @@ -53,7 +53,7 @@ protected function __construct() { */ protected function setup_hooks(): void { add_action( 'pre_get_posts', array( $this, 'prepare_event_query_before_execution' ) ); - add_filter( 'posts_clauses', array( $this, 'adjust_admin_event_sorting' ) ); + add_filter( 'posts_clauses', array( $this, 'adjust_admin_event_sorting' ), 10, 2 ); } /** @@ -231,10 +231,10 @@ static function () use ( $page_id ) { switch ( $events_query ) { case 'upcoming': remove_filter( 'posts_clauses', array( $this, 'adjust_sorting_for_past_events' ) ); - add_filter( 'posts_clauses', array( $this, 'adjust_sorting_for_upcoming_events' ) ); + add_filter( 'posts_clauses', array( $this, 'adjust_sorting_for_upcoming_events' ), 10, 2 ); break; case 'past': - add_filter( 'posts_clauses', array( $this, 'adjust_sorting_for_past_events' ) ); + add_filter( 'posts_clauses', array( $this, 'adjust_sorting_for_past_events' ), 10, 2 ); remove_filter( 'posts_clauses', array( $this, 'adjust_sorting_for_upcoming_events' ) ); break; default: @@ -249,13 +249,23 @@ static function () use ( $page_id ) { * This method modifies the SQL query pieces, including join, where, orderby, etc., to adjust the sorting criteria * for upcoming events in the query. It ensures that events are ordered by their start datetime in ascending order. * + * @see https://developer.wordpress.org/reference/hooks/posts_clauses/ + * * @since 1.0.0 * - * @param array $query_pieces An array containing pieces of the SQL query. + * @param array $query_pieces An array containing pieces of the SQL query. + * @param WP_Query $query The WP_Query instance (passed by reference). * @return array The modified SQL query pieces with adjusted sorting criteria for upcoming events. */ - public function adjust_sorting_for_upcoming_events( array $query_pieces ): array { - return $this->adjust_event_sql( $query_pieces, 'upcoming', 'ASC' ); + public function adjust_sorting_for_upcoming_events( array $query_pieces, WP_Query $query ): array { + + return $this->adjust_event_sql( + $query_pieces, + 'upcoming', + $query->get( 'order' ), + $query->get( 'orderby' ), + (bool) $query->get( 'include_unfinished' ) + ); } /** @@ -264,12 +274,18 @@ public function adjust_sorting_for_upcoming_events( array $query_pieces ): array * This method modifies the SQL query pieces, including join, where, orderby, etc., to adjust the sorting criteria * for past events in the query. It ensures that events are ordered by their start datetime in the desired order. * - * @param array $query_pieces An array containing pieces of the SQL query. - * + * @param array $query_pieces An array containing pieces of the SQL query. + * @param WP_Query $query The WP_Query instance (passed by reference). * @return array The modified SQL query pieces with adjusted sorting criteria for past events. */ - public function adjust_sorting_for_past_events( array $query_pieces ): array { - return $this->adjust_event_sql( $query_pieces, 'past' ); + public function adjust_sorting_for_past_events( array $query_pieces, WP_Query $query ): array { + return $this->adjust_event_sql( + $query_pieces, + 'past', + $query->get( 'order' ), + $query->get( 'orderby' ), + (bool) $query->get( 'include_unfinished' ) + ); } /** @@ -280,18 +296,17 @@ public function adjust_sorting_for_past_events( array $query_pieces ): array { * * @since 1.0.0 * - * @param array $query_pieces An array containing pieces of the SQL query. + * @param array $query_pieces An array containing pieces of the SQL query. + * @param WP_Query $query The WP_Query instance (passed by reference). * @return array The modified SQL query pieces with adjusted sorting criteria. */ - public function adjust_admin_event_sorting( array $query_pieces ): array { + public function adjust_admin_event_sorting( array $query_pieces, WP_Query $query ): array { if ( ! is_admin() ) { return $query_pieces; } - global $wp_query; - - if ( 'datetime' === $wp_query->get( 'orderby' ) ) { - $query_pieces = $this->adjust_event_sql( $query_pieces, 'all', $wp_query->get( 'order' ) ); + if ( 'datetime' === $query->get( 'orderby' ) ) { + $query_pieces = $this->adjust_event_sql( $query_pieces, 'all', $query->get( 'order' ) ); } return $query_pieces; @@ -304,14 +319,26 @@ public function adjust_admin_event_sorting( array $query_pieces ): array { * the `gatherpress_events` table in the database join. It allows querying events based on different * criteria such as upcoming or past events and specifying the event order (DESC or ASC). * + * @see https://developer.wordpress.org/reference/hooks/posts_join/ + * @see https://developer.wordpress.org/reference/hooks/posts_orderby/ + * @see https://developer.wordpress.org/reference/hooks/posts_where/ + * * @since 1.0.0 * - * @param array $pieces An array of query pieces, including join, where, orderby, and more. - * @param string $type The type of events to query (options: 'all', 'upcoming', 'past'). - * @param string $order The event order ('DESC' for descending or 'ASC' for ascending). + * @param array $pieces An array of query pieces, including join, where, orderby, and more. + * @param string $type The type of events to query (options: 'all', 'upcoming', 'past') (Default: 'all'). + * @param string $order The event order ('DESC' for descending or 'ASC' for ascending) (Default: 'DESC'). + * @param string[]|string $order_by List or singular string of ORDERBY statement(s) (Default: ['datetime']). + * @param bool $inclusive Whether to include currently running events in the query (Default: true). * @return array An array containing adjusted SQL clauses for the Event query. */ - public function adjust_event_sql( array $pieces, string $type = 'all', string $order = 'DESC' ): array { + public function adjust_event_sql( + array $pieces, + string $type = 'all', + string $order = 'DESC', + array|string $order_by = array( 'datetime' ), + bool $inclusive = true + ): array { global $wpdb; $defaults = array( @@ -324,13 +351,37 @@ public function adjust_event_sql( array $pieces, string $type = 'all', string $o 'limits' => '', ); $pieces = array_merge( $defaults, $pieces ); - $table = sprintf( Event::TABLE_FORMAT, $wpdb->prefix ); + $table = sprintf( Event::TABLE_FORMAT, $wpdb->prefix ); // Could also be (just) $wpdb->{gatherpress_events}. $pieces['join'] .= ' LEFT JOIN ' . esc_sql( $table ) . ' ON ' . esc_sql( $wpdb->posts ) . '.ID=' . esc_sql( $table ) . '.post_id'; $order = strtoupper( $order ); - if ( in_array( $order, array( 'DESC', 'ASC' ), true ) ) { - $pieces['orderby'] = sprintf( esc_sql( $table ) . '.datetime_start_gmt %s', esc_sql( $order ) ); + + // ORDERBY is an array, which allows to orderby multiple values. + // Currently it is only allowed to order events by ONE value. + $order_by = ( is_array( $order_by ) ) ? $order_by[0] : $order_by; + switch ( strtolower( $order_by ) ) { + case 'id': + $pieces['orderby'] = sprintf( esc_sql( $wpdb->posts ) . '.ID %s', esc_sql( $order ) ); + break; + + case 'title': + $pieces['orderby'] = sprintf( esc_sql( $wpdb->posts ) . '.post_name %s', esc_sql( $order ) ); + break; + + case 'modified': + $pieces['orderby'] = sprintf( esc_sql( $wpdb->posts ) . '.post_modified_gmt %s', esc_sql( $order ) ); + break; + + case 'rand': + $pieces['orderby'] = esc_sql( 'RAND()' ); + break; + + case 'datetime': + default: + $pieces['orderby'] = sprintf( esc_sql( $table ) . '.datetime_start_gmt %s', esc_sql( $order ) ); + break; + } } if ( 'all' === $type ) { @@ -339,10 +390,12 @@ public function adjust_event_sql( array $pieces, string $type = 'all', string $o $current = gmdate( Event::DATETIME_FORMAT, time() ); + $column = ( ( $inclusive && 'upcoming' === $type ) || ( ! $inclusive && 'past' === $type ) ) ? 'datetime_end_gmt' : 'datetime_start_gmt'; + if ( 'upcoming' === $type ) { - $pieces['where'] .= $wpdb->prepare( ' AND %i.datetime_end_gmt >= %s', $table, $current ); // phpcs:ignore WordPress.DB.PreparedSQLPlaceholders.UnsupportedIdentifierPlaceholder + $pieces['where'] .= $wpdb->prepare( ' AND %i.%i >= %s', $table, $column, $current ); // phpcs:ignore WordPress.DB.PreparedSQLPlaceholders.UnsupportedIdentifierPlaceholder } elseif ( 'past' === $type ) { - $pieces['where'] .= $wpdb->prepare( ' AND %i.datetime_end_gmt < %s', $table, $current ); // phpcs:ignore WordPress.DB.PreparedSQLPlaceholders.UnsupportedIdentifierPlaceholder + $pieces['where'] .= $wpdb->prepare( ' AND %i.%i < %s', $table, $column, $current ); // phpcs:ignore WordPress.DB.PreparedSQLPlaceholders.UnsupportedIdentifierPlaceholder } return $pieces;