@@ -34,7 +34,8 @@ class EventDateQueryAbilities {
3434 private const DEFAULT_PUBLIC_RESULTS = 50 ;
3535 private const TAXONOMY_CANDIDATE_BATCH = 100 ;
3636
37- private static bool $ registered = false ;
37+ private static bool $ registered = false ;
38+ private ?array $ prefilteredQueryArgs = null ;
3839
3940 public function __construct () {
4041 if ( ! self ::$ registered ) {
@@ -458,7 +459,10 @@ public function executeQueryEvents( array $input ): array {
458459 * @param array $input The full ability input array.
459460 */
460461 $ base_query_args = $ query_args ;
461- $ query_args = (array ) apply_filters ( 'data_machine_events_calendar_query_args ' , $ query_args , $ input );
462+ $ prefiltered = $ this ->consumePrefilteredQueryArgs ();
463+ $ query_args = is_array ( $ prefiltered )
464+ ? $ prefiltered
465+ : (array ) apply_filters ( 'data_machine_events_calendar_query_args ' , $ query_args , $ input );
462466
463467 if ( $ query_args === $ base_query_args && $ this ->canUseBoundedTaxonomyCandidates ( $ input , $ tax_filters , $ scope , $ status , $ per_page , $ page ) ) {
464468 remove_filter ( 'posts_clauses ' , $ clauses_filter );
@@ -709,19 +713,21 @@ public function buildMatchingPostIdsSql( array $input ): string {
709713 * @return string SQL selecting start_date, end_date, and bucket_count.
710714 */
711715 public function buildMatchingEventDateAggregateSql ( array $ input , string $ start_expression , string $ end_expression ): string {
712- $ selective = $ this ->buildSelectiveTaxonomyAggregateSql ( $ input , $ start_expression , $ end_expression );
713- if ( '' !== $ selective ) {
714- return $ selective ;
715- }
716-
717- $ request = '' ;
718716 $ input ['fields ' ] = 'ids ' ;
719717 $ input ['per_page ' ] = -1 ;
720718 $ input [ self ::CAPTURE_AGGREGATE_QUERY_VAR ] = array (
721719 'start_expression ' => $ start_expression ,
722720 'end_expression ' => $ end_expression ,
723721 );
724722
723+ $ this ->prefilteredQueryArgs = null ;
724+ $ selective = $ this ->buildSelectiveTaxonomyAggregateSql ( $ input , $ start_expression , $ end_expression );
725+ if ( '' !== $ selective ) {
726+ return $ selective ;
727+ }
728+
729+ $ request = '' ;
730+
725731 $ capture = static function ( $ posts , $ query ) use ( &$ request ) {
726732 if ( ! $ query ->get ( self ::CAPTURE_AGGREGATE_QUERY_VAR ) ) {
727733 return $ posts ;
@@ -737,6 +743,7 @@ public function buildMatchingEventDateAggregateSql( array $input, string $start_
737743 $ this ->executeQueryEvents ( $ input );
738744 } finally {
739745 remove_filter ( 'posts_pre_query ' , $ capture , PHP_INT_MAX );
746+ $ this ->prefilteredQueryArgs = null ;
740747 }
741748
742749 return $ request ;
@@ -755,20 +762,20 @@ private function buildSelectiveTaxonomyAggregateSql( array $input, string $start
755762
756763 $ db_engine = defined ( 'DB_ENGINE ' ) ? strtolower ( (string ) constant ( 'DB_ENGINE ' ) ) : '' ;
757764 $ database_type = defined ( 'DATABASE_TYPE ' ) ? strtolower ( (string ) constant ( 'DATABASE_TYPE ' ) ) : '' ;
758- $ tax_filters = is_array ( $ input ['tax_filters ' ] ?? null ) ? array_filter ( $ input ['tax_filters ' ] ) : array ();
765+ $ tax_filters = is_array ( $ input ['tax_filters ' ] ?? null ) ? $ input ['tax_filters ' ] : array ();
759766 if (
760767 'sqlite ' === $ db_engine
761768 || 'sqlite ' === $ database_type
762769 || true !== $ wpdb ->is_mysql
763770 || 1 !== count ( $ tax_filters )
771+ || empty ( reset ( $ tax_filters ) )
764772 || 'publish ' !== ( $ input ['status ' ] ?? 'publish ' )
765773 || ! empty ( $ input ['search ' ] )
766774 || ! empty ( $ input ['geo ' ] )
767775 || ! empty ( $ input ['exclude ' ] )
768776 || ! empty ( $ input ['meta_query ' ] )
769777 || ! empty ( $ input ['scope_token ' ] )
770778 || ! empty ( $ input ['time_scope ' ] )
771- || false !== has_filter ( 'data_machine_events_calendar_query_args ' )
772779 ) {
773780 return '' ;
774781 }
@@ -814,6 +821,9 @@ private function buildSelectiveTaxonomyAggregateSql( array $input, string $start
814821 } elseif ( 'past ' === $ scope ) {
815822 $ where [] = UpcomingFilter::past_where ( $ now );
816823 }
824+ if ( $ this ->hasCustomizedAggregateQueryArgs ( $ input , $ tax_filters ) ) {
825+ return '' ;
826+ }
817827
818828 $ placeholders = implode ( ', ' , array_fill ( 0 , count ( $ taxonomy_ids ), '%d ' ) );
819829 $ table = EventDatesTable::table_name ();
@@ -832,6 +842,49 @@ private function buildSelectiveTaxonomyAggregateSql( array $input, string $start
832842 return $ wpdb ->prepare ( $ sql , ...$ values ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared -- Trusted table/expression fragments; all values use placeholders.
833843 }
834844
845+ /** Determine whether registered query callbacks actually constrain this aggregate. */
846+ private function hasCustomizedAggregateQueryArgs ( array $ input , array $ tax_filters ): bool {
847+ if ( false === has_filter ( 'data_machine_events_calendar_query_args ' ) ) {
848+ return false ;
849+ }
850+
851+ $ query_args = array (
852+ 'post_type ' => Event_Post_Type::POST_TYPE ,
853+ 'post_status ' => 'publish ' ,
854+ 'posts_per_page ' => -1 ,
855+ 'paged ' => max ( 1 , (int ) ( $ input ['page ' ] ?? 1 ) ),
856+ 'no_found_rows ' => true ,
857+ 'orderby ' => 'none ' ,
858+ 'fields ' => 'ids ' ,
859+ 'tax_query ' => array ( 'relation ' => 'AND ' ),
860+ );
861+ $ query_args [ self ::CAPTURE_AGGREGATE_QUERY_VAR ] = $ input [ self ::CAPTURE_AGGREGATE_QUERY_VAR ];
862+
863+ foreach ( $ tax_filters as $ taxonomy => $ term_ids ) {
864+ $ query_args ['tax_query ' ][] = array (
865+ 'taxonomy ' => sanitize_key ( $ taxonomy ),
866+ 'field ' => 'term_id ' ,
867+ 'terms ' => array_values ( array_unique ( array_filter ( array_map ( 'absint ' , (array ) $ term_ids ) ) ) ),
868+ 'operator ' => 'IN ' ,
869+ );
870+ }
871+
872+ $ filtered = (array ) apply_filters ( 'data_machine_events_calendar_query_args ' , $ query_args , $ input );
873+ if ( $ filtered === $ query_args ) {
874+ return false ;
875+ }
876+
877+ $ this ->prefilteredQueryArgs = $ filtered ;
878+ return true ;
879+ }
880+
881+ /** Consume the one-shot aggregate fallback handoff before WP_Query can re-enter. */
882+ private function consumePrefilteredQueryArgs (): ?array {
883+ $ prefiltered = $ this ->prefilteredQueryArgs ;
884+ $ this ->prefilteredQueryArgs = null ;
885+ return $ prefiltered ;
886+ }
887+
835888 /**
836889 * Execute an exact count through the canonical WP_Query constraint path.
837890 *
0 commit comments