prefix . 'abptb_orders'; // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery $total_order = (int)$wpdb->get_var( $wpdb->prepare("SELECT COUNT(*) FROM %i", $order_table) ); $abptb_info = array(); $post_ids = self::get_post_id(['status' => ['publish', 'draft', 'private', 'trash']]); $post_counts = wp_count_posts(ABPTB_Function::get_cpt()); $total_publish = $post_counts->publish ?? 0; $total_draft = $post_counts->draft ?? 0; $total_private = $post_counts->private ?? 0; $total_trash = $post_counts->trash ?? 0; $abptb_info['post_ids'] = $post_ids; $abptb_info['total_post'] = sizeof($post_ids); $abptb_info['total_publish'] = $total_publish; $abptb_info['total_draft'] = $total_draft; $abptb_info['total_private'] = $total_private; $abptb_info['total_trash'] = $total_trash; $abptb_info['total_order'] = $total_order; wp_cache_set($cache_key, $abptb_info); return $abptb_info; } public static function query_post_type($post_type, $show = -1, $page = 1): WP_Query { $args = array( 'post_type' => $post_type, 'posts_per_page' => $show, 'paged' => $page, 'post_status' => 'publish' ); return new WP_Query($args); } public static function dummy_ids(): array { $args = array( 'post_type' => ABPTB_Function::get_cpt(), 'post_status' => 'any', 'posts_per_page' => -1, 'fields' => 'ids', 'meta_key' => 'dummy', 'meta_value' => 'on', ); // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query $posts = get_posts($args); return is_array($posts) ? $posts : array(); } public static function get_post_id($filters = []): array { $post_type = ($filters['cpt'] ?? null) ?: ABPTB_Function::get_cpt(); $show = ($filters['posts_per_page'] ?? null) ?: -1; $page = ($filters['paged'] ?? null) ?: 1; $status = ($filters['status'] ?? null) ?: 'publish'; $cat_id = $filters['cat_id'] ?? null; $loc_id = $filters['loc_id'] ?? null; $brand_id = $filters['brand_id'] ?? null; $org_id = $filters['org_id'] ?? null; $bp_dp = $filters['bp_dp'] ?? null; $bp = $filters['bp'] ?? null; $dp = $filters['dp'] ?? null; $title = $filters['post_title'] ?? ($filters['title'] ?? null); if (!empty($bp) && !empty($dp)) { $bp_dp = $bp . '_' . $dp; } $meta_query = ['relation' => 'AND']; // Category query if (!empty($cat_id)) { $meta_query[] = ['key' => 'abptb_category', 'value' => $cat_id, 'compare' => '=']; } if (!empty($brand_id)) { $meta_query[] = ['key' => 'abptb_brand', 'value' => $brand_id, 'compare' => '=']; } if (!empty($org_id)) { $meta_query[] = ['key' => 'abptb_organizer', 'value' => $org_id, 'compare' => '=']; } // route query if (!empty($bp_dp)) { $meta_query[] = ['key' => 'route_data', 'value' => '"' . $bp_dp . '"', 'compare' => 'LIKE']; } if (!empty($loc_id)) { $meta_query[] = ['key' => 'route_direction', 'value' => '"' . $loc_id . '"', 'compare' => 'LIKE']; } $args = array( 'fields' => 'ids', 'post_type' => $post_type, 'posts_per_page' => $show, 'paged' => $page, 'post_status' => $status, // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query 'meta_query' => $meta_query ); $title_filter = null; if (!empty($title)) { $args['s'] = $title; $args['suppress_filters'] = false; $title_filter = function ($search) use ($title) { global $wpdb; return $wpdb->prepare(" AND {$wpdb->posts}.post_title LIKE %s ", '%' . $wpdb->esc_like($title) . '%'); }; add_filter('posts_search', $title_filter, 10, 1); } $all_data = get_posts($args); if ($title_filter !== null) { remove_filter('posts_search', $title_filter, 10); } return array_unique($all_data); } public static function get_booking_query($filters = array(), $limit = 0, $offset = 0, $count = false) { global $wpdb; $table_name = $wpdb->prefix . 'abptb_orders'; $use_cache = !isset($filters['cache']) || false !== $filters['cache']; $cache_key = 'abptb_bk_' . md5(wp_json_encode($filters) . $limit . $offset . (int)$count); $cache_group = 'abptb_orders'; if ($use_cache) { $cached = wp_cache_get($cache_key, $cache_group); if (false !== $cached) { return $cached; } } $conditions = array(); $params = array(); // Order Status Filter $status = !empty($filters['status']) ? sanitize_text_field($filters['status']) : null; $booked_status = $status ?: ABPTB_Function::booking_status(); $booked_status = $booked_status ? explode(',', $booked_status) : array(); $is_all_status = (!empty($booked_status) && current($booked_status) === 'all'); if (!empty($booked_status) && !$is_all_status) { $placeholders = implode(',', array_fill(0, count($booked_status), '%s')); $conditions[] = "order_status IN ($placeholders)"; $params = array_merge($params, $booked_status); } // Integer ID Filters $int_keys = array('id', 'post_id', 'user_id', 'item_id', 'order_id', 'start_point', 'sp_id'); foreach ($int_keys as $key) { if (!empty($filters[$key])) { $conditions[] = "{$key} = %d"; $params[] = absint($filters[$key]); } } // Start Time Filter (Fixed Y-m-d format for DATE() comparison) if (!empty($filters['start_time'])) { $timestamp = strtotime($filters['start_time']); if (gmdate('H:i', $timestamp) !== '00:00') { $params[] = gmdate('Y-m-d H:i', $timestamp); $conditions[] = "DATE_FORMAT(start_time, '%%Y-%%m-%%d %%H:%%i') = %s"; } else { $params[] = gmdate('Y-m-d', $timestamp); $conditions[] = 'DATE(start_time) = %s'; } } if (!empty($filters['_start_time'])) { $timestamp = strtotime($filters['_start_time']); $params[] = gmdate('Y-m-d', $timestamp); $conditions[] = 'DATE(start_time) = %s'; } // Route Directions (BP & DP dynamically handled) if (!empty($filters['bp_dp'])) { $post_id = !empty($filters['post_id']) ? absint($filters['post_id']) : 0; $start_time = !empty($filters['start_time']) ? sanitize_text_field($filters['start_time']) : ''; $bp_dp_parts = explode('_', sanitize_text_field($filters['bp_dp'])); if (count($bp_dp_parts) >= 2 && $post_id && $start_time) { $bp = intval($bp_dp_parts[0]); $dp = intval($bp_dp_parts[1]); $routes = ABPTB_Function::get_post_info($post_id, 'return_route_direction', array()); if (!empty($routes) && is_array($routes)) { $sp = array_search($bp, $routes, false); $ep = array_search($dp, $routes, false); if (false !== $sp && false !== $ep) { $valid_bps = array_slice($routes, 0, $ep); $valid_dps = array_slice($routes, $sp + 1); if (!empty($valid_bps) && !empty($valid_dps)) { $bp_placeholders = implode(',', array_fill(0, count($valid_bps), '%s')); $dp_placeholders = implode(',', array_fill(0, count($valid_dps), '%s')); $conditions[] = "bp IN ({$bp_placeholders})"; $params = array_merge($params, $valid_bps); // FIXED ARRAY MERGE $conditions[] = "dp IN ({$dp_placeholders})"; $params = array_merge($params, $valid_dps); // FIXED ARRAY MERGE } } } } } else { // Single BP / DP filters fallback (if bp_dp is not present) if (!empty($filters['bp']) || !empty($filters['_bp'])) { $bp_val = !empty($filters['bp']) ? $filters['bp'] : $filters['_bp']; $conditions[] = 'bp = %d'; $params[] = absint($bp_val); } if (!empty($filters['dp']) || !empty($filters['_dp'])) { $dp_val = !empty($filters['dp']) ? $filters['dp'] : $filters['_dp']; $conditions[] = 'dp = %d'; $params[] = absint($dp_val); } if (!empty($filters['_bp_dp'])) { $conditions[] = "bp_dp LIKE %s"; $params[] = '%' . $wpdb->esc_like(sanitize_text_field($filters['_bp_dp'])) . '%'; } } // JSON Fields if (!empty($filters['ticket_id'])) { $conditions[] = 'JSON_CONTAINS(ticket_id, %s)'; $params[] = wp_json_encode(sanitize_text_field($filters['ticket_id'])); } if (!empty($filters['ex_id'])) { $conditions[] = 'JSON_CONTAINS(ex_id, %s)'; $params[] = wp_json_encode(sanitize_text_field($filters['ex_id'])); } // Date Range Filters if (!empty($filters['order_date'])) { $conditions[] = 'DATE(created_at) = %s'; $params[] = gmdate('Y-m-d', strtotime($filters['order_date'])); } if (!empty($filters['start_time_from']) && !empty($filters['start_time_to'])) { $conditions[] = 'DATE(start_time) BETWEEN %s AND %s'; $params[] = gmdate('Y-m-d', strtotime($filters['start_time_from'])); $params[] = gmdate('Y-m-d', strtotime($filters['start_time_to'])); } if (!empty($filters['order_date_from']) && !empty($filters['order_date_to'])) { $conditions[] = 'DATE(created_at) BETWEEN %s AND %s'; $params[] = gmdate('Y-m-d', strtotime($filters['order_date_from'])); $params[] = gmdate('Y-m-d', strtotime($filters['order_date_to'])); } // Billing Info (LIKE search) $like_keys = array('billing_name', 'billing_email', 'billing_phone', 'others'); foreach ($like_keys as $like_key) { if (!empty($filters[$like_key])) { $conditions[] = "{$like_key} LIKE %s"; $params[] = '%' . $wpdb->esc_like(sanitize_text_field($filters[$like_key])) . '%'; } } // Aggregate Mode (SUM/MIN/MAX/AVG over numeric columns, e.g. filters['aggregate'] => array('total' => 'SUM')) $aggregate_fields = array(); if (!empty($filters['aggregate']) && is_array($filters['aggregate']) && !$count) { $agg_select = array( 'total' => array( 'SUM' => 'COALESCE(SUM(total), 0) AS total', 'MIN' => 'COALESCE(MIN(total), 0) AS total', 'MAX' => 'COALESCE(MAX(total), 0) AS total', 'AVG' => 'COALESCE(AVG(total), 0) AS total', ), 'qty' => array( 'SUM' => 'COALESCE(SUM(qty), 0) AS qty', 'MIN' => 'COALESCE(MIN(qty), 0) AS qty', 'MAX' => 'COALESCE(MAX(qty), 0) AS qty', 'AVG' => 'COALESCE(AVG(qty), 0) AS qty', ), 'price' => array( 'SUM' => 'COALESCE(SUM(price), 0) AS price', 'MIN' => 'COALESCE(MIN(price), 0) AS price', 'MAX' => 'COALESCE(MAX(price), 0) AS price', 'AVG' => 'COALESCE(AVG(price), 0) AS price', ), ); foreach ($filters['aggregate'] as $agg_col => $agg_fn) { $agg_col = sanitize_key($agg_col); $agg_fn = strtoupper(sanitize_key($agg_fn)); if (isset($agg_select[$agg_col][$agg_fn])) { $aggregate_fields[$agg_col] = $agg_select[$agg_col][$agg_fn]; } } } // SQL Query Assembly $where_sql = !empty($conditions) ? ' WHERE ' . implode(' AND ', $conditions) : ''; $order_sql = ''; if (!$count && empty($aggregate_fields)) { $allowed_columns = array('id', 'post_id', 'order_id', 'status', 'created_at', 'updated_at'); $raw_order_by = !empty($filters['order_by']) ? sanitize_key($filters['order_by']) : 'order_id'; $order_by = in_array($raw_order_by, $allowed_columns, true) ? $raw_order_by : 'order_id'; $order_dir = (!empty($filters['order_dir']) && strtoupper($filters['order_dir']) === 'ASC') ? 'ASC' : 'DESC'; $order_sql = ' ORDER BY %i ' . ('ASC' === $order_dir ? 'ASC' : 'DESC'); $params[] = $order_by; if ($limit > 0) { $order_sql .= ' LIMIT %d OFFSET %d'; $params[] = absint($limit); $params[] = absint($offset); } } $select = $count ? 'SELECT COUNT(*)' : ($aggregate_fields ? 'SELECT ' . implode(', ', $aggregate_fields) : 'SELECT *'); $sql = "{$select} FROM %i{$where_sql}{$order_sql}"; if ($count) { if (!empty($params)) { // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, PluginCheck.Security.DirectDB.UnescapedDBParameter $results = $wpdb->get_var( // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared $wpdb->prepare($sql, array_merge(array($table_name), $params)) ); } else { // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter $results = $wpdb->get_var($wpdb->prepare($sql, $table_name)); } } elseif (!empty($aggregate_fields)) { if (!empty($params)) { // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, PluginCheck.Security.DirectDB.UnescapedDBParameter $results = $wpdb->get_row( // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared $wpdb->prepare($sql, array_merge(array($table_name), $params)), ARRAY_A ); } else { // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter $results = $wpdb->get_row($wpdb->prepare($sql, $table_name), ARRAY_A); } } else { if (!empty($params)) { // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, PluginCheck.Security.DirectDB.UnescapedDBParameter $results = $wpdb->get_results( // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared $wpdb->prepare($sql, array_merge(array($table_name), $params)), ARRAY_A ); } else { // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter $results = $wpdb->get_results($wpdb->prepare($sql, $table_name), ARRAY_A); } } $results = $results ?: ($count ? 0 : array()); if ($use_cache) { wp_cache_set($cache_key, $results, $cache_group, 30); } return $results; } public static function get_sold_qty_ex($filters = [], $bypass_cache = false) { $sold_qty = 0; $booking_items = self::get_booking_query(self::sold_filters($filters, $bypass_cache)); if (empty($booking_items)) { return $sold_qty; } $id = $filters['ex_id'] ?? ''; foreach ($booking_items as $booking_item) { $ex_ids = json_decode($booking_item['ex_id'] ?? '', true) ?: []; $additional_infos = json_decode($booking_item['ex_info'] ?? '', true) ?: []; if (!empty($id)) { if (in_array($id, $ex_ids, true) && isset($additional_infos[$id])) { $sold_qty += $additional_infos[$id]['qty'] ?? 1; } } else { foreach ($additional_infos as $additional_info) { $sold_qty += $additional_info['qty'] ?? 1; } } } return $sold_qty; } /** * Batch aggregate sold quantity + order count for many journeys in one query. * * Returns a map keyed by "{post_id}|{Y-m-d H:i}" holding the same values that * get_sold_ticket()['total'] and get_booking_query() would return for that single * post_id + start_time pair, so callers can render long journey lists without * issuing two queries per row. * * @param array $post_ids Transport post IDs to cover. * @param string $date Day to aggregate (Y-m-d). * @return array */ public static function get_slot_aggregates($post_ids = array(), $date = ''): array { global $wpdb; $ids = array_values(array_unique(array_filter(array_map('absint', (array) $post_ids)))); if (empty($ids) || empty($date)) { return array(); } $table_name = $wpdb->prefix . 'abptb_orders'; $statuses = array_values(array_filter(array_map('trim', explode(',', ABPTB_Function::booking_status_sold())))); if (empty($statuses)) { return array(); } $status_placeholders = implode(',', array_fill(0, count($statuses), '%s')); $id_placeholders = implode(',', array_fill(0, count($ids), '%d')); // %% escapes a literal percent for $wpdb->prepare(). $sql = "SELECT post_id, DATE_FORMAT(start_time, '%%Y-%%m-%%d %%H:%%i') AS slot, ticket_info FROM %i WHERE order_status IN ($status_placeholders) AND post_id IN ($id_placeholders) AND DATE(start_time) = %s"; $params = array_merge(array($table_name), $statuses, $ids, array($date)); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared $rows = $wpdb->get_results($wpdb->prepare($sql, $params), ARRAY_A); $aggregates = array(); foreach ((array) $rows as $row) { $key = (int) $row['post_id'] . '|' . $row['slot']; if (!isset($aggregates[$key])) { $aggregates[$key] = array('sold' => 0, 'orders' => 0); } $aggregates[$key]['orders']++; $ticket_infos = json_decode($row['ticket_info'] ?? '', true) ?: array(); if (empty($ticket_infos) || !is_array($ticket_infos)) { continue; } foreach ($ticket_infos as $ticket_info) { if (empty($ticket_info)) { continue; } $aggregates[$key]['sold'] += (int) ($ticket_info['qty'] ?? 1); } } return $aggregates; } public static function get_sp($id = '', $count = false) { global $wpdb; $cache_key = 'abptb_sp_' . md5($id . ($count ? '_count' : '_all')); $abptb_sp = wp_cache_get($cache_key); if (false !== $abptb_sp) { return $abptb_sp; } $table_name = $wpdb->prefix . 'abptb_sp'; if ($count) { if (!empty($id)) { // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching $results = $wpdb->get_var($wpdb->prepare("SELECT COUNT(*) FROM %i WHERE id = %d", $table_name, (int)$id)); } else { // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching $results = $wpdb->get_var($wpdb->prepare("SELECT COUNT(*) FROM %i", $table_name)); } } else { if (!empty($id)) { // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching $results = $wpdb->get_results($wpdb->prepare("SELECT * FROM %i WHERE id = %d ORDER BY id ASC", $table_name, (int)$id), ARRAY_A); } else { // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching $results = $wpdb->get_results($wpdb->prepare("SELECT * FROM %i ORDER BY id ASC", $table_name), ARRAY_A); } } wp_cache_set($cache_key, $results); return $results; } public static function get_sold_ticket($filters = [], $bypass_cache = false): array { $sold_qty = []; $booking_items = self::get_booking_query(self::sold_filters($filters, $bypass_cache)); if (empty($booking_items)) { return $sold_qty; } foreach ($booking_items as $booking_item) { $ticket_infos = json_decode($booking_item['ticket_info'] ?? '', true) ?: []; if (!empty($ticket_infos)) { foreach ($ticket_infos as $ticket_info) { if (!empty($ticket_info)) { $qty = $ticket_info ['qty'] ?? 1; $id = $ticket_info ['id'] ?? 'price'; $sold_qty [$id] = ($sold_qty [$id] ?? 0) + $qty; $sold_qty ['total'] = ($sold_qty ['total'] ?? 0) + $qty; } } } } return $sold_qty; } public static function get_sold_seat($filters = [], $bypass_cache = false): array { $sold_seats = []; $booking_items = self::get_booking_query(self::sold_filters($filters, $bypass_cache)); if (empty($booking_items)) { return $sold_seats; } foreach ($booking_items as $booking_item) { $ticket_infos = json_decode($booking_item['ticket_info'] ?? '', true) ?: []; if (!empty($ticket_infos)) { foreach ($ticket_infos as $ticket_info) { if (!empty($ticket_info)) { $sold_seats [] = $ticket_info ['name'] ?? ''; } } } } return array_values(array_unique($sold_seats)); } public static function sold_filters(array $filters = [], bool $bypass_cache = false): array { if (empty($filters['status'])) { $filters['status'] = ABPTB_Function::booking_status_sold(); } if ($bypass_cache) { $filters['cache'] = false; } return $filters; } public static function holds_table(): string { global $wpdb; return $wpdb->prefix . 'abptb_seat_holds'; } public static function hold_minutes(): int { return (int)apply_filters('abptb_seat_hold_minutes', 20); } public static function prune_seat_holds(): void { global $wpdb; $table_name = self::holds_table(); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching $wpdb->query($wpdb->prepare("DELETE FROM %i WHERE expires_at IS NOT NULL AND expires_at < %s", $table_name, current_time('mysql'))); } public static function seat_hold_exists($post_id, $start_time, $bp_dp, $sp_id, $seat_name, $session_id): bool { if (empty($post_id) || empty($start_time) || empty($bp_dp) || empty($seat_name) || empty($session_id)) { return false; } global $wpdb; $table_name = self::holds_table(); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching $count = (int)$wpdb->get_var($wpdb->prepare( "SELECT COUNT(*) FROM %i WHERE post_id = %d AND start_time = %s AND bp_dp = %s AND sp_id = %d AND seat_name = %s AND session_id = %s", $table_name, absint($post_id), sanitize_text_field($start_time), sanitize_text_field($bp_dp), absint($sp_id), sanitize_text_field($seat_name), sanitize_text_field($session_id) )); return $count > 0; } public static function create_seat_hold($post_id, $start_time, $bp_dp, $sp_id, $seat_name, $qty = 1, $session_id = '', $hold_ref = ''): bool { if (empty($post_id) || empty($start_time) || empty($bp_dp) || empty($seat_name) || empty($session_id)) { return false; } self::prune_seat_holds(); global $wpdb; $table_name = self::holds_table(); $expires_at = gmdate('Y-m-d H:i:s', strtotime('+' . self::hold_minutes() . ' minutes')); if (self::seat_hold_exists($post_id, $start_time, $bp_dp, $sp_id, $seat_name, $session_id)) { // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching $wpdb->update($table_name, ['expires_at' => $expires_at, 'hold_ref' => sanitize_text_field($hold_ref), 'qty' => absint($qty)], [ 'post_id' => absint($post_id), 'start_time' => sanitize_text_field($start_time), 'bp_dp' => sanitize_text_field($bp_dp), 'sp_id' => absint($sp_id), 'seat_name' => sanitize_text_field($seat_name), 'session_id' => sanitize_text_field($session_id), ], ['%s', '%s', '%d'], ['%d', '%s', '%s', '%d', '%s', '%s']); return true; } // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching $wpdb->hide_errors(); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching $inserted = $wpdb->insert($table_name, [ 'post_id' => absint($post_id), 'start_time' => sanitize_text_field($start_time), 'bp_dp' => sanitize_text_field($bp_dp), 'sp_id' => absint($sp_id), 'seat_name' => sanitize_text_field($seat_name), 'qty' => absint($qty), 'session_id' => sanitize_text_field($session_id), 'hold_ref' => sanitize_text_field($hold_ref), 'user_id' => get_current_user_id(), 'expires_at' => $expires_at, ]); $wpdb->show_errors(); return (bool)$inserted; } public static function delete_seat_holds($session_id = '', $hold_ref = ''): void { global $wpdb; $table_name = self::holds_table(); if (!empty($session_id) && !empty($hold_ref)) { // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching $wpdb->delete($table_name, ['session_id' => sanitize_text_field($session_id), 'hold_ref' => sanitize_text_field($hold_ref)], ['%s', '%s']); } elseif (!empty($session_id)) { // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching $wpdb->delete($table_name, ['session_id' => sanitize_text_field($session_id)], ['%s']); } } public static function get_held_seat($filters = [], $exclude_session = ''): array { $held_seats = []; $holds = self::get_seat_holds($filters, $exclude_session); if (!empty($holds)) { foreach ($holds as $hold) { if (!empty($hold['seat_name']) && strpos($hold['seat_name'], 'ticket:') !== 0) { $held_seats[] = $hold['seat_name']; } } } return array_values(array_unique($held_seats)); } public static function get_held_ticket($filters = [], $exclude_session = ''): array { $held_qty = []; $holds = self::get_seat_holds($filters, $exclude_session); if (!empty($holds)) { foreach ($holds as $hold) { if (!empty($hold['seat_name']) && strpos($hold['seat_name'], 'ticket:') === 0) { $ticket_id = substr($hold['seat_name'], 7); $held_qty[$ticket_id] = ($held_qty[$ticket_id] ?? 0) + intval($hold['qty'] ?? 1); } } } return $held_qty; } public static function get_seat_holds($filters = [], $exclude_session = ''): array { self::prune_seat_holds(); global $wpdb; $table_name = self::holds_table(); $conditions = []; $params = []; if (!empty($filters['post_id'])) { $conditions[] = 'post_id = %d'; $params[] = absint($filters['post_id']); } if (!empty($filters['start_time'])) { $conditions[] = 'start_time = %s'; $params[] = sanitize_text_field($filters['start_time']); } if (!empty($filters['bp_dp'])) { $conditions[] = 'bp_dp = %s'; $params[] = sanitize_text_field($filters['bp_dp']); } if (!empty($filters['sp_id'])) { $conditions[] = 'sp_id = %d'; $params[] = absint($filters['sp_id']); } if (!empty($filters['sp_id'])) { $conditions[] = 'sp_id = %d'; $params[] = absint($filters['sp_id']); } if (!empty($exclude_session)) { $conditions[] = 'session_id != %s'; $params[] = sanitize_text_field($exclude_session); } $where_sql = !empty($conditions) ? ' WHERE ' . implode(' AND ', $conditions) : ''; $sql = "SELECT * FROM %i{$where_sql}"; // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, PluginCheck.Security.DirectDB.UnescapedDBParameter $results = !empty($params) // phpcs:ignore PluginCheck.Security.DirectDB.UnescapedDBParameter ? $wpdb->get_results( // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared $wpdb->prepare($sql, array_merge(array($table_name), $params)), ARRAY_A) // phpcs:ignore PluginCheck.Security.DirectDB.UnescapedDBParameter : $wpdb->get_results( // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared $wpdb->prepare($sql, $table_name), ARRAY_A); return $results ?: []; } } new ABPTB_Query(); }