prefix . 'abprf_orders'; $property_table = $wpdb->prefix . 'abprf_property'; // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery $total_order = (int) $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM %i", $order_table ) ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery $total_property = (int) $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM %i", $property_table ) ); $abprf_info = array(); $post_ids = self::get_post_id( [ 'status' => [ 'publish', 'draft', 'private', 'trash' ] ] ); $post_counts = wp_count_posts( ABPRF_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; $abprf_info['post_ids'] = $post_ids; $abprf_info['total_post'] = sizeof( $post_ids ); $abprf_info['total_publish'] = $total_publish; $abprf_info['total_draft'] = $total_draft; $abprf_info['total_private'] = $total_private; $abprf_info['total_trash'] = $total_trash; $abprf_info['total_property'] = $total_property; $abprf_info['total_order'] = $total_order; wp_cache_set( $cache_key, $abprf_info, 'abprf_info', 30 ); return $abprf_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 get_post_id( $filters = [] ): array { $post_type = ( $filters['cpt'] ?? null ) ?: ABPRF_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 ) ?: null; $loc_id = ( $filters['loc_id'] ?? null ) ?: null; $brand_id = ( $filters['brand_id'] ?? null ) ?: null; $lat = ( $filters['lat'] ?? null ) ?: null; $lng = ( $filters['lng'] ?? null ) ?: null; $radius = ( $filters['radius'] ?? null ) ?: null; $rent_rule = ( $filters['rent_rule'] ?? null ) ?: null; // Resolve geo coordinates into matching location ids first. if ( $lat !== null && $lng !== null && is_numeric( $lat ) && is_numeric( $lng ) ) { $geo_locations = self::get_geo_locations( (float) $lat, (float) $lng, $radius ); if ( empty( $geo_locations ) ) { return []; } if ( $loc_id !== null ) { $loc_id = array_values( array_intersect( is_array( $loc_id ) ? $loc_id : explode( ',', $loc_id ), $geo_locations ) ); } else { $loc_id = $geo_locations; } } $loc_query = ''; if ( ! empty( $loc_id ) ) { $loc_ids = is_array( $loc_id ) ? $loc_id : explode( ',', $loc_id ); if ( count( $loc_ids ) === 1 ) { $loc_query = [ 'key' => 'abprf_location', 'value' => $loc_ids[0], 'compare' => 'LIKE' ]; } else { $or_clauses = []; foreach ( $loc_ids as $loc_id_item ) { if ( $loc_id_item !== '' ) { $or_clauses[] = [ 'key' => 'abprf_location', 'value' => $loc_id_item, 'compare' => 'LIKE' ]; } } if ( ! empty( $or_clauses ) ) { $loc_query = $or_clauses; $loc_query['relation'] = 'OR'; } } } $cat_query = ! empty( $cat_id ) ? [ 'key' => 'abprf_category', 'value' => $cat_id, 'compare' => '=' ] : ''; $rent_query = ! empty( $rent_rule ) ? [ 'key' => 'rent_rule', 'value' => $rent_rule, 'compare' => '=', 'type' => 'CHAR' ] : ''; $brand_query = ''; if ( ! empty( $brand_id ) ) { $brand_ids = is_array( $brand_id ) ? $brand_id : explode( ',', $brand_id ); if ( count( $brand_ids ) === 1 ) { $brand_query = [ 'key' => 'abprf_brand', 'value' => $brand_ids[0], 'compare' => 'LIKE' ]; } else { $or_clauses = []; foreach ( $brand_ids as $brand_id_item ) { if ( $brand_id_item !== '' ) { $or_clauses[] = [ 'key' => 'abprf_brand', 'value' => $brand_id_item, 'compare' => 'LIKE' ]; } } if ( ! empty( $or_clauses ) ) { $brand_query = $or_clauses; $brand_query['relation'] = 'OR'; } } } $args = array( 'fields' => 'ids', 'post_type' => $post_type, 'posts_per_page' => $show, 'paged' => $page, 'post_status' => $status, ); $meta_query = array_values( array_filter( [ $cat_query, $loc_query, $rent_query, $brand_query ] ) ); if ( ! empty( $meta_query ) ) { $args['meta_query'] = $meta_query; if ( count( $meta_query ) > 1 ) { $args['meta_query']['relation'] = 'AND'; } } $all_data = get_posts( $args ); return array_unique( $all_data ); } public static function get_geo_locations( float $lat, float $lng, $radius = null ): array { $all_locations = ABPRF_Function::get_option( 'abprf_location' ); $all_locations = is_array( $all_locations ) ? $all_locations : []; $radius = ( $radius !== null && is_numeric( $radius ) && $radius > 0 ) ? (float) $radius : null; $matches = []; foreach ( $all_locations as $term_id => $location ) { $loc_lat = isset( $location['lat'] ) ? (float) $location['lat'] : null; $loc_lng = isset( $location['lng'] ) ? (float) $location['lng'] : null; $polygon = isset( $location['polygon'] ) && is_array( $location['polygon'] ) ? $location['polygon'] : []; if ( $loc_lat !== null && $loc_lng !== null ) { $distance = self::geo_distance( $lat, $lng, $loc_lat, $loc_lng ); $match_radius = $radius; if ( $match_radius === null && isset( $location['radius'] ) && is_numeric( $location['radius'] ) && $location['radius'] > 0 ) { $match_radius = (float) $location['radius']; } if ( $match_radius !== null && $distance <= $match_radius ) { $matches[] = (string) $term_id; continue; } } if ( count( $polygon ) >= 3 && self::point_in_polygon( $lat, $lng, $polygon ) ) { $matches[] = (string) $term_id; } } return array_unique( $matches ); } public static function geo_distance( float $lat1, float $lng1, float $lat2, float $lng2 ): float { $earth_radius = 6371.0088; // kilometers $d_lat = deg2rad( $lat2 - $lat1 ); $d_lng = deg2rad( $lng2 - $lng1 ); $a = sin( $d_lat / 2 ) ** 2 + cos( deg2rad( $lat1 ) ) * cos( deg2rad( $lat2 ) ) * sin( $d_lng / 2 ) ** 2; return round( $earth_radius * 2 * atan2( sqrt( $a ), sqrt( 1 - $a ) ), 3 ); } public static function point_in_polygon( float $lat, float $lng, array $polygon ): bool { $inside = false; $count = count( $polygon ); for ( $i = 0, $j = $count - 1; $i < $count; $j = $i++ ) { $p1_lat = (float) ( $polygon[ $i ]['lat'] ?? $polygon[ $i ][0] ?? 0 ); $p1_lng = (float) ( $polygon[ $i ]['lng'] ?? $polygon[ $i ][1] ?? 0 ); $p2_lat = (float) ( $polygon[ $j ]['lat'] ?? $polygon[ $j ][0] ?? 0 ); $p2_lng = (float) ( $polygon[ $j ]['lng'] ?? $polygon[ $j ][1] ?? 0 ); $intersect = ( ( $p1_lat > $lat ) !== ( $p2_lat > $lat ) ) && ( $lng < ( $p2_lng - $p1_lng ) * ( $lat - $p1_lat ) / ( max( ( $p2_lat - $p1_lat ), PHP_FLOAT_EPSILON ) ) + $p1_lng ); if ( $intersect ) { $inside = ! $inside; } } return $inside; } public static function get_property( $filters = array(), $count = false ) { global $wpdb; $table_name = $wpdb->prefix . 'abprf_property'; $cache_key = 'abprf_prop_' . md5( wp_json_encode( $filters ) . $count ); $cache_group = 'abprf_property'; $results = wp_cache_get( $cache_key, $cache_group ); if ( false !== $results ) { return $results; } $conditions = []; $params = []; /***************/ $post_id = $filters['post_id'] ?? null; if ( ! empty( $post_id ) && $post_id !== 'all' ) { if ( in_array( $post_id, [ 'on', 'off' ], true ) ) { $conditions[] = "rent_continue = %s"; $params[] = $post_id; } else { $conditions[] = "post_id = %d"; $params[] = (int) $post_id; } } if ( ! empty( $filters['property_id'] ) ) { $conditions[] = "id = %d"; $params[] = (int) $filters['property_id']; } if ( ! empty( $filters['rent_continue'] ) ) { $conditions[] = "rent_continue = %s"; $params[] = $filters['rent_continue']; } if ( ! empty( $filters['rent_rule'] ) ) { $conditions[] = "rent_rule = %s"; $params[] = $filters['rent_rule']; } if ( ! empty( $filters['brand_id'] ) ) { $conditions[] = "brand = %s"; $params[] = $filters['brand_id']; } $category = ( $filters['cat_id'] ?? null ) ?: ( $filters['category'] ?? null ); if ( ! empty( $category ) ) { $conditions[] = "category = %s"; $params[] = $category; } $location = ( $filters['loc_id'] ?? null ) ?: ( $filters['location'] ?? null ); $lat = ( $filters['lat'] ?? null ) ?: null; $lng = ( $filters['lng'] ?? null ) ?: null; if ( $lat !== null && $lng !== null && is_numeric( $lat ) && is_numeric( $lng ) ) { $geo_locations = self::get_geo_locations( (float) $lat, (float) $lng, $filters['radius'] ?? null ); if ( empty( $geo_locations ) ) { return $count ? 0 : []; } if ( ! empty( $location ) ) { $geo_locations = array_values( array_intersect( is_array( $location ) ? $location : explode( ',', $location ), $geo_locations ) ); } $location = $geo_locations; } if ( ! empty( $location ) ) { $loc_ids = is_array( $location ) ? $location : array_map( 'trim', explode( ',', $location ) ); $loc_ids = array_values( array_filter( $loc_ids, static fn( $id ) => $id !== '' ) ); if ( count( $loc_ids ) === 1 ) { $conditions[] = "FIND_IN_SET(%s, location)"; $params[] = $loc_ids[0]; } elseif ( count( $loc_ids ) > 1 ) { $placeholders = implode( ' OR ', array_fill( 0, count( $loc_ids ), 'FIND_IN_SET(%s, location)' ) ); $conditions[] = "( " . $placeholders . " )"; $params = array_merge( $params, $loc_ids ); } } if ( ! empty( $filters['status'] ) ) { $conditions[] = "status = %s"; $params[] = $filters['status']; } /***************/ $select = $count ? "SELECT COUNT(*)" : "SELECT *"; $sql = "$select FROM %i"; $query_args = [ $table_name ]; if ( ! empty( $conditions ) ) { $sql .= " WHERE " . implode( " AND ", $conditions ); $query_args = array_merge( $query_args, $params ); } $allowed_columns = [ 'id', 'post_id', 'property_id', 'created_at' ]; $order_by = sanitize_sql_orderby( $filters['order_by'] ?? 'created_at' ); $order_by = in_array( $order_by, $allowed_columns, true ) ? $order_by : 'created_at'; $order_dir = strtoupper( $filters['order_dir'] ?? 'ASC' ) === 'ASC' ? 'ASC' : 'DESC'; // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared $sql .= " ORDER BY $order_by $order_dir"; $limit = (int) ( $filters['limit'] ?? 0 ); $offset = (int) ( $filters['offset'] ?? 0 ); if ( $limit > 0 ) { $sql .= " LIMIT %d OFFSET %d"; $query_args[] = $limit; $query_args[] = $offset; } if ( $count ) { // 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, ...$query_args ) ); } else { // 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, ...$query_args ), ARRAY_A ); } wp_cache_set( $cache_key, $results, $cache_group, 30 ); return $results; } public static function get_property_counts( array $post_ids ): array { global $wpdb; $ids = array_values( array_unique( array_map( 'absint', $post_ids ) ) ); if ( empty( $ids ) ) { return []; } $cache_key = 'abprf_prop_count_' . md5( wp_json_encode( $ids ) ); $cache_group = 'abprf_property'; $counts = wp_cache_get( $cache_key, $cache_group ); if ( false !== $counts ) { return $counts; } $placeholders = implode( ',', array_fill( 0, count( $ids ), '%d' ) ); $table_name = $wpdb->prefix . 'abprf_property'; // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching // phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- $placeholders is a generated %d list for absint IDs, passed via $wpdb->prepare(). $rows = $wpdb->get_results( $wpdb->prepare( "SELECT post_id, COUNT(*) AS c FROM %i WHERE post_id IN ($placeholders) GROUP BY post_id", array_merge( [ $table_name ], $ids ) ), ARRAY_A ); // phpcs:enable WordPress.DB.PreparedSQL.InterpolatedNotPrepared $counts = []; foreach ( $rows as $row ) { $counts[ (int) $row['post_id'] ] = (int) $row['c']; } wp_cache_set( $cache_key, $counts, $cache_group, 30 ); return $counts; } public static function get_booking_query( $filters = array(), $limit = 0, $offset = 0, $count = false ) { global $wpdb; $table_name = $wpdb->prefix . 'abprf_orders'; $cache_key = 'abprf_bk_' . md5( wp_json_encode( $filters ) . $limit . $offset . $count ); $cache_group = 'abprf_orders'; $cached = wp_cache_get( $cache_key, $cache_group ); if ( false !== $cached ) { return $cached; } $conditions = []; $params = []; $status = ! empty( $filters['status'] ) ? sanitize_text_field( $filters['status'] ) : null; $booked_status = $status ?: ABPRF_Function::booking_status(); $booked_status = $booked_status ? explode( ',', $booked_status ) : []; $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 ); } if ( ! empty( $filters['post_id'] ) ) { $conditions[] = "post_id = %d"; $params[] = intval( $filters['post_id'] ); } if ( ! empty( $filters['user_id'] ) ) { $conditions[] = "user_id = %d"; $params[] = intval( $filters['user_id'] ); } if ( ! empty( $filters['item_id'] ) ) { $conditions[] = "item_id = %d"; $params[] = intval( $filters['item_id'] ); } if ( ! empty( $filters['order_id'] ) ) { $conditions[] = "order_id = %d"; $params[] = intval( $filters['order_id'] ); } if ( ! empty( $filters['property_id'] ) ) { $conditions[] = "JSON_SEARCH(property_id, 'one', %s, NULL, '$') IS NOT NULL"; $params[] = (string) intval( $filters['property_id'] ); } if ( ! empty( $filters['ex_id'] ) ) { $conditions[] = "JSON_SEARCH(ex_id, 'one', %s, NULL, '$') IS NOT NULL"; $params[] = (string) sanitize_text_field( $filters['ex_id'] ); } if ( ! empty( $filters['location'] ) ) { $conditions[] = "FIND_IN_SET(%s, location)"; $params[] = (string) intval( $filters['location'] ); } $start_time = ! empty( $filters['start_time'] ) ? gmdate( 'Y-m-d H:i:s', strtotime( $filters['start_time'] ) ) : null; $end_time = ! empty( $filters['end_time'] ) ? gmdate( 'Y-m-d H:i:s', strtotime( $filters['end_time'] ) ) : null; $order_date = ! empty( $filters['order_date'] ) ? gmdate( 'Y-m-d', strtotime( $filters['order_date'] ) ) : null; $booking_time_from = ! empty( $filters['booking_time_from'] ) ? gmdate( 'Y-m-d', strtotime( $filters['booking_time_from'] ) ) : null; $booking_time_to = ! empty( $filters['booking_time_to'] ) ? gmdate( 'Y-m-d', strtotime( $filters['booking_time_to'] ) ) : null; $order_time_from = ! empty( $filters['order_date_from'] ) ? gmdate( 'Y-m-d', strtotime( $filters['order_date_from'] ) ) : null; $order_time_to = ! empty( $filters['order_date_to'] ) ? gmdate( 'Y-m-d', strtotime( $filters['order_date_to'] ) ) : null; $billing_name = ! empty( $filters['billing_name'] ) ? '%' . sanitize_text_field( $filters['billing_name'] ) . '%' : null; $billing_email = ! empty( $filters['billing_email'] ) ? '%' . sanitize_text_field( $filters['billing_email'] ) . '%' : null; $billing_phone = ! empty( $filters['billing_phone'] ) ? '%' . sanitize_text_field( $filters['billing_phone'] ) . '%' : null; if ( ! empty( $start_time ) && ! empty( $end_time ) ) { $conditions[] = "(book_from < %s AND book_to > %s)"; $params[] = $end_time; $params[] = $start_time; } else { if ( ! empty( $start_time ) ) { $conditions[] = "DATE(start_time) = %s "; $params[] = $start_time; } } if ( ! empty( $order_date ) ) { $conditions[] = "DATE(created_at) = %s "; $params[] = $order_date; } if ( ! empty( $booking_time_from ) && ! empty( $booking_time_to ) ) { $conditions[] = "DATE(start_time) BETWEEN %s AND %s"; $params[] = $booking_time_from; $params[] = $booking_time_to; } if ( ! empty( $order_time_from ) && ! empty( $order_time_to ) ) { $conditions[] = "DATE(created_at) BETWEEN %s AND %s"; $params[] = $order_time_from; $params[] = $order_time_to; } if ( ! empty( $billing_name ) ) { $conditions[] = "billing_name LIKE %s"; $params[] = $billing_name; } if ( ! empty( $billing_email ) ) { $conditions[] = "billing_email LIKE %s"; $params[] = $billing_email; } if ( ! empty( $billing_phone ) ) { $conditions[] = "billing_phone LIKE %s"; $params[] = $billing_phone; } $select = $count ? "SELECT COUNT(*)" : "SELECT *"; $sql = "$select FROM %i"; $query_args = [ $table_name ]; if ( ! empty( $conditions ) ) { $sql .= " WHERE " . implode( " AND ", $conditions ); $query_args = array_merge( $query_args, $params ); } $allowed_columns = [ 'id', 'post_id', 'order_id', 'status', 'created_at' ]; $order_by = sanitize_sql_orderby( $filters['order_by'] ?? 'order_id' ); $order_by = in_array( $order_by, $allowed_columns, true ) ? $order_by : 'order_id'; $order_dir = strtoupper( $filters['order_dir'] ?? 'ASC' ) === 'ASC' ? 'ASC' : 'DESC'; // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared $sql .= " ORDER BY $order_by $order_dir"; if ( $limit > 0 ) { $sql .= " LIMIT %d OFFSET %d"; $query_args[] = (int) $limit; $query_args[] = (int) $offset; } if ( $count ) { // 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, ...$query_args ) ); } else { // 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, ...$query_args ), ARRAY_A ); } $results = $results ?: ( $count ? 0 : [] ); wp_cache_set( $cache_key, $results, $cache_group, 30 ); return $results; } public static function get_sold_qty( $filters = [] ) { $sold_qty = 0; $booking_lists = self::get_booking_query( $filters ); if ( empty( $booking_lists ) ) { return $sold_qty; } $id = $filters['property_id'] ?? ''; foreach ( $booking_lists as $booking_list ) { $property_ids = json_decode( $booking_list['property_id'] ?? '', true ) ?: []; $ticket_infos = json_decode( $booking_list['property_info'] ?? '', true ) ?: []; if ( ! empty( $id ) ) { if ( in_array( $id, $property_ids, true ) && isset( $ticket_infos[ $id ] ) ) { $sold_qty += $ticket_infos[ $id ]['qty'] ?? 1; } } else { foreach ( $ticket_infos as $ticket_info ) { $sold_qty += $ticket_info['qty'] ?? 1; } } } return $sold_qty; } public static function get_sold_qty_ex( $filters = [] ) { $sold_qty = 0; $booking_lists = self::get_booking_query( $filters ); if ( empty( $booking_lists ) ) { return $sold_qty; } $id = $filters['ex_id'] ?? ''; foreach ( $booking_lists as $booking_list ) { $ex_ids = json_decode( $booking_list['ex_id'] ?? '', true ) ?: []; $additional_infos = json_decode( $booking_list['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; } } new ABPRF_Query(); }