user_id = $user_id; } /** * Get all dashboard stats in a single cached object * * @return array */ public function get_all_stats() : array { $cache_key = 'stats_' . $this->user_id; $stats = wp_cache_get( $cache_key, $this->cache_group ); if ( false === $stats ) { $stats = array( 'total_events' => $this->calculate_total_events_count(), 'upcoming_events' => $this->calculate_upcoming_events_count(), 'past_events' => $this->calculate_past_events_count(), 'total_tickets' => $this->calculate_total_tickets_sold(), 'total_revenue' => $this->calculate_total_revenue(), 'revenue_target' => $this->get_annual_revenue_target(), ); wp_cache_set( $cache_key, $stats, $this->cache_group, $this->cache_expiration ); HVAC_Logger::info( 'Dashboard stats calculated and cached', 'Dashboard', array( 'user_id' => $this->user_id ) ); } return $stats; } /** * Clear cache for a specific user * * @return void */ public function clear_cache() { $cache_key = 'stats_' . $this->user_id; wp_cache_delete( $cache_key, $this->cache_group ); HVAC_Logger::info( 'Dashboard cache cleared', 'Dashboard', array( 'user_id' => $this->user_id ) ); } /** * Calculate total events count (optimized) * * @return int */ private function calculate_total_events_count() : int { global $wpdb; // Direct query is more efficient for simple counts $count = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(ID) FROM {$wpdb->posts} WHERE post_type = %s AND post_author = %d AND post_status IN ('publish', 'future', 'draft', 'pending', 'private')", Tribe__Events__Main::POSTTYPE, $this->user_id ) ); return (int) $count; } /** * Calculate upcoming events count * * @return int */ private function calculate_upcoming_events_count() : int { global $wpdb; $today = current_time( 'mysql' ); // Query using post_author and meta data $count = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(DISTINCT p.ID) FROM {$wpdb->posts} p INNER JOIN {$wpdb->postmeta} pm ON p.ID = pm.post_id WHERE p.post_type = %s AND p.post_author = %d AND p.post_status IN ('publish', 'future') AND pm.meta_key = '_EventStartDate' AND pm.meta_value >= %s", Tribe__Events__Main::POSTTYPE, $this->user_id, $today ) ); return (int) $count; } /** * Calculate past events count * * @return int */ private function calculate_past_events_count() : int { global $wpdb; $today = current_time( 'mysql' ); $count = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(DISTINCT p.ID) FROM {$wpdb->posts} p INNER JOIN {$wpdb->postmeta} pm ON p.ID = pm.post_id WHERE p.post_type = %s AND p.post_author = %d AND p.post_status IN ('publish', 'private') AND pm.meta_key = '_EventEndDate' AND pm.meta_value < %s", Tribe__Events__Main::POSTTYPE, $this->user_id, $today ) ); return (int) $count; } /** * Calculate total tickets sold (optimized with single query) * * @return int */ private function calculate_total_tickets_sold() : int { global $wpdb; // Get all event IDs in one query $event_ids = $wpdb->get_col( $wpdb->prepare( "SELECT ID FROM {$wpdb->posts} WHERE post_type = %s AND post_author = %d AND post_status IN ('publish', 'future', 'draft', 'pending', 'private')", Tribe__Events__Main::POSTTYPE, $this->user_id ) ); if ( empty( $event_ids ) ) { return 0; } // Get sum of tickets sold in one query $placeholders = array_fill( 0, count( $event_ids ), '%d' ); $sql = $wpdb->prepare( "SELECT SUM(meta_value) FROM {$wpdb->postmeta} WHERE meta_key = '_tribe_tickets_sold' AND post_id IN (" . implode( ',', $placeholders ) . ")", $event_ids ); $total = $wpdb->get_var( $sql ); return (int) $total; } /** * Calculate total revenue (optimized) * * @return float */ private function calculate_total_revenue() : float { global $wpdb; // Get all event IDs in one query $event_ids = $wpdb->get_col( $wpdb->prepare( "SELECT ID FROM {$wpdb->posts} WHERE post_type = %s AND post_author = %d AND post_status IN ('publish', 'future', 'draft', 'pending', 'private')", Tribe__Events__Main::POSTTYPE, $this->user_id ) ); if ( empty( $event_ids ) ) { return 0.0; } // Get sum of revenue in one query $placeholders = array_fill( 0, count( $event_ids ), '%d' ); $sql = $wpdb->prepare( "SELECT SUM(meta_value) FROM {$wpdb->postmeta} WHERE meta_key = '_tribe_revenue_total' AND post_id IN (" . implode( ',', $placeholders ) . ")", $event_ids ); $total = $wpdb->get_var( $sql ); return (float) $total; } /** * Get annual revenue target * * @return float|null */ private function get_annual_revenue_target() : ?float { $target = get_user_meta( $this->user_id, 'annual_revenue_target', true ); return ! empty( $target ) && is_numeric( $target ) ? (float) $target : null; } /** * Get events table data (optimized) * * @param string $filter_status Status filter * @return array */ public function get_events_table_data( string $filter_status = 'all' ) : array { global $wpdb; $valid_statuses = array( 'publish', 'future', 'draft', 'pending', 'private' ); $post_status = ( 'all' === $filter_status || ! in_array( $filter_status, $valid_statuses, true ) ) ? $valid_statuses : array( $filter_status ); // Convert to SQL-safe string $status_placeholders = array_fill( 0, count( $post_status ), '%s' ); $status_sql = implode( ',', $status_placeholders ); // Get all events with their metadata in fewer queries $sql = $wpdb->prepare( "SELECT p.ID, p.post_title, p.post_status, p.guid, MAX(CASE WHEN pm.meta_key = '_EventStartDate' THEN pm.meta_value END) as start_date, MAX(CASE WHEN pm.meta_key = '_EventOrganizerID' THEN pm.meta_value END) as organizer_id, MAX(CASE WHEN pm.meta_key = '_tribe_tickets_sold' THEN pm.meta_value END) as tickets_sold, MAX(CASE WHEN pm.meta_key = '_tribe_revenue_total' THEN pm.meta_value END) as revenue FROM {$wpdb->posts} p LEFT JOIN {$wpdb->postmeta} pm ON p.ID = pm.post_id WHERE p.post_type = %s AND p.post_author = %d AND p.post_status IN ($status_sql) GROUP BY p.ID ORDER BY start_date DESC", array_merge( array( Tribe__Events__Main::POSTTYPE, $this->user_id ), $post_status ) ); $events = $wpdb->get_results( $sql ); $events_data = array(); foreach ( $events as $event ) { // Get ticket capacity $capacity = $this->get_event_capacity( $event->ID ); $events_data[] = array( 'id' => $event->ID, 'status' => $event->post_status, 'name' => $event->post_title, 'link' => get_permalink( $event->ID ), 'start_date_ts' => strtotime( $event->start_date ), 'organizer_id' => (int) $event->organizer_id, 'capacity' => $capacity, 'sold' => (int) $event->tickets_sold, 'revenue' => (float) $event->revenue, ); } return $events_data; } /** * Get event capacity * * @param int $event_id Event ID * @return string|int */ private function get_event_capacity( $event_id ) { if ( ! function_exists( 'tribe_get_tickets' ) ) { return 0; } $tickets = tribe_get_tickets( $event_id ); $total_capacity = 0; foreach ( $tickets as $ticket ) { $capacity = $ticket->capacity(); if ( $capacity === -1 ) { return 'Unlimited'; } if ( is_numeric( $capacity ) ) { $total_capacity += $capacity; } } return $total_capacity; } }