Add venue taxonomies and filter /find-training to show only approved labs: - Create venue_type, venue_equipment, venue_amenities taxonomies - Filter venue markers by mq-approved-lab taxonomy term - Add equipment and amenities badges to venue modal - Add venue contact form with AJAX handler and email notification - Include POC (Point of Contact) meta for each training lab 9 approved training labs configured: - Fast Track Learning Lab, Progressive Training Lab, NAVAC Technical Training Center - Stevens Equipment Phoenix/Johnstown, San Jacinto College, Johnstone Supply - TruTech Tools Training Center (new), Auer Steel & Heating Supply (new) Note: Venues not displaying on map - to be debugged next session. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
805 lines
25 KiB
PHP
805 lines
25 KiB
PHP
<?php
|
|
/**
|
|
* Training Map Data Provider
|
|
*
|
|
* Provides marker data for trainers and venues on the Find Training map.
|
|
*
|
|
* @package HVAC_Community_Events
|
|
* @since 2.2.0
|
|
*/
|
|
|
|
if (!defined('ABSPATH')) {
|
|
exit;
|
|
}
|
|
|
|
/**
|
|
* Class HVAC_Training_Map_Data
|
|
*
|
|
* Data provider for the Find Training Google Maps integration.
|
|
* Queries trainers and venues with coordinates for map display.
|
|
*/
|
|
class HVAC_Training_Map_Data {
|
|
|
|
/**
|
|
* Singleton instance
|
|
*
|
|
* @var HVAC_Training_Map_Data|null
|
|
*/
|
|
private static ?self $instance = null;
|
|
|
|
/**
|
|
* Cache group for queries
|
|
*
|
|
* @var string
|
|
*/
|
|
private string $cache_group = 'hvac_training_map';
|
|
|
|
/**
|
|
* Cache expiration (1 hour)
|
|
*
|
|
* @var int
|
|
*/
|
|
private int $cache_expiration = 3600;
|
|
|
|
/**
|
|
* Get singleton instance
|
|
*
|
|
* @return HVAC_Training_Map_Data
|
|
*/
|
|
public static function get_instance(): self {
|
|
if (self::$instance === null) {
|
|
self::$instance = new self();
|
|
}
|
|
return self::$instance;
|
|
}
|
|
|
|
/**
|
|
* Constructor
|
|
*/
|
|
private function __construct() {
|
|
// Clear cache when profiles or venues are updated
|
|
add_action('save_post_trainer_profile', [$this, 'clear_trainer_cache']);
|
|
add_action('save_post_tribe_venue', [$this, 'clear_venue_cache']);
|
|
}
|
|
|
|
/**
|
|
* Get trainer markers for map
|
|
*
|
|
* @param array $filters Optional filters
|
|
* @return array Trainer markers data
|
|
*/
|
|
public function get_trainer_markers(array $filters = []): array {
|
|
// Generate cache key based on filters
|
|
$cache_key = 'trainers_' . md5(serialize($filters));
|
|
$cached = wp_cache_get($cache_key, $this->cache_group);
|
|
|
|
if ($cached !== false && empty($filters)) {
|
|
return $cached;
|
|
}
|
|
|
|
// Get approved user IDs
|
|
$approved_user_ids = $this->get_approved_user_ids();
|
|
|
|
if (empty($approved_user_ids)) {
|
|
return [];
|
|
}
|
|
|
|
// Build query args
|
|
$query_args = [
|
|
'post_type' => 'trainer_profile',
|
|
'posts_per_page' => -1,
|
|
'post_status' => 'publish',
|
|
'meta_query' => [
|
|
'relation' => 'AND',
|
|
[
|
|
'key' => 'is_public_profile',
|
|
'value' => '1',
|
|
'compare' => '='
|
|
],
|
|
[
|
|
'key' => 'user_id',
|
|
'value' => $approved_user_ids,
|
|
'compare' => 'IN'
|
|
],
|
|
[
|
|
'key' => 'latitude',
|
|
'compare' => 'EXISTS'
|
|
],
|
|
[
|
|
'key' => 'longitude',
|
|
'compare' => 'EXISTS'
|
|
],
|
|
[
|
|
'key' => 'latitude',
|
|
'value' => '',
|
|
'compare' => '!='
|
|
],
|
|
[
|
|
'key' => 'longitude',
|
|
'value' => '',
|
|
'compare' => '!='
|
|
]
|
|
]
|
|
];
|
|
|
|
// Add state filter
|
|
if (!empty($filters['state'])) {
|
|
$query_args['meta_query'][] = [
|
|
'key' => 'trainer_state',
|
|
'value' => sanitize_text_field($filters['state']),
|
|
'compare' => '='
|
|
];
|
|
}
|
|
|
|
// Add search filter
|
|
if (!empty($filters['search'])) {
|
|
$search = sanitize_text_field($filters['search']);
|
|
$query_args['meta_query'][] = [
|
|
'relation' => 'OR',
|
|
[
|
|
'key' => 'trainer_display_name',
|
|
'value' => $search,
|
|
'compare' => 'LIKE'
|
|
],
|
|
[
|
|
'key' => 'trainer_city',
|
|
'value' => $search,
|
|
'compare' => 'LIKE'
|
|
],
|
|
[
|
|
'key' => 'company_name',
|
|
'value' => $search,
|
|
'compare' => 'LIKE'
|
|
]
|
|
];
|
|
}
|
|
|
|
$query = new WP_Query($query_args);
|
|
$markers = [];
|
|
|
|
if ($query->have_posts()) {
|
|
while ($query->have_posts()) {
|
|
$query->the_post();
|
|
$profile_id = get_the_ID();
|
|
$marker = $this->format_trainer_marker($profile_id);
|
|
|
|
// Apply certification filter
|
|
if (!empty($filters['certification'])) {
|
|
$cert_match = false;
|
|
foreach ($marker['certifications'] as $cert) {
|
|
if (stripos($cert, $filters['certification']) !== false) {
|
|
$cert_match = true;
|
|
break;
|
|
}
|
|
}
|
|
if (!$cert_match) {
|
|
continue;
|
|
}
|
|
}
|
|
|
|
// Apply proximity filter
|
|
if (!empty($filters['lat']) && !empty($filters['lng']) && !empty($filters['radius'])) {
|
|
$distance = $this->calculate_distance(
|
|
$filters['lat'],
|
|
$filters['lng'],
|
|
$marker['lat'],
|
|
$marker['lng']
|
|
);
|
|
if ($distance > $filters['radius']) {
|
|
continue;
|
|
}
|
|
$marker['distance'] = round($distance, 1);
|
|
}
|
|
|
|
$markers[] = $marker;
|
|
}
|
|
}
|
|
|
|
wp_reset_postdata();
|
|
|
|
// Sort by distance if proximity search
|
|
if (!empty($filters['lat']) && !empty($filters['lng'])) {
|
|
usort($markers, function($a, $b) {
|
|
return ($a['distance'] ?? 0) <=> ($b['distance'] ?? 0);
|
|
});
|
|
}
|
|
|
|
// Cache if no filters
|
|
if (empty($filters)) {
|
|
wp_cache_set($cache_key, $markers, $this->cache_group, $this->cache_expiration);
|
|
}
|
|
|
|
return $markers;
|
|
}
|
|
|
|
/**
|
|
* Get venue markers for map
|
|
*
|
|
* Filters to show only measureQuick Approved Training Labs.
|
|
*
|
|
* @param array $filters Optional filters
|
|
* @return array Venue markers data
|
|
*/
|
|
public function get_venue_markers(array $filters = []): array {
|
|
// Check if TEC is active
|
|
if (!function_exists('tribe_get_venue')) {
|
|
return [];
|
|
}
|
|
|
|
// Generate cache key
|
|
$cache_key = 'venues_approved_labs_' . md5(serialize($filters));
|
|
$cached = wp_cache_get($cache_key, $this->cache_group);
|
|
|
|
if ($cached !== false && empty($filters)) {
|
|
return $cached;
|
|
}
|
|
|
|
// Build query args - filter for approved training labs only
|
|
$query_args = [
|
|
'post_type' => 'tribe_venue',
|
|
'posts_per_page' => -1,
|
|
'post_status' => 'publish',
|
|
// Only show venues tagged as measureQuick Approved Training Labs
|
|
'tax_query' => [
|
|
[
|
|
'taxonomy' => 'venue_type',
|
|
'field' => 'slug',
|
|
'terms' => 'mq-approved-lab',
|
|
],
|
|
],
|
|
'meta_query' => [
|
|
'relation' => 'AND',
|
|
[
|
|
'relation' => 'OR',
|
|
// Check for our custom venue coordinates
|
|
[
|
|
'key' => 'venue_latitude',
|
|
'compare' => 'EXISTS'
|
|
],
|
|
// Also check TEC's built-in coordinates
|
|
[
|
|
'key' => '_VenueLat',
|
|
'compare' => 'EXISTS'
|
|
]
|
|
]
|
|
]
|
|
];
|
|
|
|
// Add state filter
|
|
if (!empty($filters['state'])) {
|
|
$query_args['meta_query'][] = [
|
|
'relation' => 'OR',
|
|
[
|
|
'key' => '_VenueStateProvince',
|
|
'value' => sanitize_text_field($filters['state']),
|
|
'compare' => '='
|
|
],
|
|
[
|
|
'key' => '_VenueState',
|
|
'value' => sanitize_text_field($filters['state']),
|
|
'compare' => '='
|
|
]
|
|
];
|
|
}
|
|
|
|
// Add search filter
|
|
if (!empty($filters['search'])) {
|
|
$query_args['s'] = sanitize_text_field($filters['search']);
|
|
}
|
|
|
|
$query = new WP_Query($query_args);
|
|
$markers = [];
|
|
|
|
if ($query->have_posts()) {
|
|
while ($query->have_posts()) {
|
|
$query->the_post();
|
|
$venue_id = get_the_ID();
|
|
$marker = $this->format_venue_marker($venue_id);
|
|
|
|
// Skip venues without valid coordinates
|
|
if (empty($marker['lat']) || empty($marker['lng'])) {
|
|
continue;
|
|
}
|
|
|
|
// Apply proximity filter
|
|
if (!empty($filters['lat']) && !empty($filters['lng']) && !empty($filters['radius'])) {
|
|
$distance = $this->calculate_distance(
|
|
$filters['lat'],
|
|
$filters['lng'],
|
|
$marker['lat'],
|
|
$marker['lng']
|
|
);
|
|
if ($distance > $filters['radius']) {
|
|
continue;
|
|
}
|
|
$marker['distance'] = round($distance, 1);
|
|
}
|
|
|
|
$markers[] = $marker;
|
|
}
|
|
}
|
|
|
|
wp_reset_postdata();
|
|
|
|
// Sort by distance if proximity search
|
|
if (!empty($filters['lat']) && !empty($filters['lng'])) {
|
|
usort($markers, function($a, $b) {
|
|
return ($a['distance'] ?? 0) <=> ($b['distance'] ?? 0);
|
|
});
|
|
}
|
|
|
|
// Cache if no filters
|
|
if (empty($filters)) {
|
|
wp_cache_set($cache_key, $markers, $this->cache_group, $this->cache_expiration);
|
|
}
|
|
|
|
return $markers;
|
|
}
|
|
|
|
/**
|
|
* Format trainer data for map marker
|
|
*
|
|
* @param int $profile_id Trainer profile post ID
|
|
* @return array Formatted marker data
|
|
*/
|
|
private function format_trainer_marker(int $profile_id): array {
|
|
$user_id = get_post_meta($profile_id, 'user_id', true);
|
|
$lat = get_post_meta($profile_id, 'latitude', true);
|
|
$lng = get_post_meta($profile_id, 'longitude', true);
|
|
|
|
// Get certifications
|
|
$certifications = $this->get_trainer_certifications($profile_id, $user_id);
|
|
|
|
// Get event count (cached)
|
|
$event_count = get_post_meta($profile_id, 'cached_event_count', true);
|
|
if (empty($event_count)) {
|
|
$event_count = 0;
|
|
}
|
|
|
|
return [
|
|
'id' => $profile_id,
|
|
'type' => 'trainer',
|
|
'lat' => floatval($lat),
|
|
'lng' => floatval($lng),
|
|
'name' => get_post_meta($profile_id, 'trainer_display_name', true),
|
|
'city' => get_post_meta($profile_id, 'trainer_city', true),
|
|
'state' => get_post_meta($profile_id, 'trainer_state', true),
|
|
'certifications' => $certifications,
|
|
'certification' => !empty($certifications) ? $certifications[0] : '',
|
|
'image' => get_post_meta($profile_id, 'profile_image_url', true),
|
|
'profile_id' => $profile_id,
|
|
'user_id' => intval($user_id),
|
|
'event_count' => intval($event_count)
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Format venue data for map marker
|
|
*
|
|
* @param int $venue_id Venue post ID
|
|
* @return array Formatted marker data
|
|
*/
|
|
private function format_venue_marker(int $venue_id): array {
|
|
// Try our custom coordinates first, then TEC's
|
|
$lat = get_post_meta($venue_id, 'venue_latitude', true);
|
|
$lng = get_post_meta($venue_id, 'venue_longitude', true);
|
|
|
|
if (empty($lat) || empty($lng)) {
|
|
$lat = get_post_meta($venue_id, '_VenueLat', true);
|
|
$lng = get_post_meta($venue_id, '_VenueLng', true);
|
|
}
|
|
|
|
// Get venue details from TEC
|
|
$city = get_post_meta($venue_id, '_VenueCity', true);
|
|
$state = get_post_meta($venue_id, '_VenueStateProvince', true) ?: get_post_meta($venue_id, '_VenueState', true);
|
|
$address = get_post_meta($venue_id, '_VenueAddress', true);
|
|
|
|
// Count upcoming events at this venue
|
|
$upcoming_events_count = $this->count_venue_upcoming_events($venue_id);
|
|
|
|
return [
|
|
'id' => $venue_id,
|
|
'type' => 'venue',
|
|
'lat' => floatval($lat),
|
|
'lng' => floatval($lng),
|
|
'name' => get_the_title($venue_id),
|
|
'address' => $address,
|
|
'city' => $city,
|
|
'state' => $state,
|
|
'upcoming_events' => $upcoming_events_count
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Get trainer certifications
|
|
*
|
|
* @param int $profile_id Profile post ID
|
|
* @param int $user_id User ID
|
|
* @return array List of certification names
|
|
*/
|
|
private function get_trainer_certifications(int $profile_id, int $user_id): array {
|
|
$certifications = [];
|
|
|
|
// Try new certification system
|
|
if (class_exists('HVAC_Trainer_Certification_Manager')) {
|
|
$cert_manager = HVAC_Trainer_Certification_Manager::instance();
|
|
$trainer_certs = $cert_manager->get_trainer_certifications($user_id);
|
|
|
|
foreach ($trainer_certs as $cert) {
|
|
$cert_type = get_post_meta($cert->ID, 'certification_type', true);
|
|
$status = get_post_meta($cert->ID, 'status', true) ?: 'active';
|
|
$expiration = get_post_meta($cert->ID, 'expiration_date', true);
|
|
|
|
// Only include active, non-expired certifications
|
|
$is_expired = $expiration && strtotime($expiration) < time();
|
|
if ($status === 'active' && !$is_expired && !empty($cert_type)) {
|
|
$certifications[] = $cert_type;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Fallback to legacy certification
|
|
if (empty($certifications)) {
|
|
$legacy = get_post_meta($profile_id, 'certification_type', true);
|
|
if (!empty($legacy)) {
|
|
$certifications[] = $legacy;
|
|
}
|
|
}
|
|
|
|
return array_unique($certifications);
|
|
}
|
|
|
|
/**
|
|
* Count upcoming events at a venue
|
|
*
|
|
* @param int $venue_id Venue post ID
|
|
* @return int Number of upcoming events
|
|
*/
|
|
private function count_venue_upcoming_events(int $venue_id): int {
|
|
if (!function_exists('tribe_get_events')) {
|
|
return 0;
|
|
}
|
|
|
|
$events = tribe_get_events([
|
|
'eventDisplay' => 'upcoming',
|
|
'posts_per_page' => -1,
|
|
'venue' => $venue_id,
|
|
'fields' => 'ids'
|
|
]);
|
|
|
|
return is_array($events) ? count($events) : 0;
|
|
}
|
|
|
|
/**
|
|
* Get full trainer profile for modal
|
|
*
|
|
* @param int $profile_id Profile post ID
|
|
* @return array|null Trainer data or null if not found
|
|
*/
|
|
public function get_trainer_full_profile(int $profile_id): ?array {
|
|
$profile = get_post($profile_id);
|
|
if (!$profile || $profile->post_type !== 'trainer_profile') {
|
|
return null;
|
|
}
|
|
|
|
$user_id = get_post_meta($profile_id, 'user_id', true);
|
|
|
|
// Get basic marker data
|
|
$data = $this->format_trainer_marker($profile_id);
|
|
|
|
// Add additional details for modal
|
|
$data['company'] = get_post_meta($profile_id, 'company_name', true);
|
|
$data['bio'] = get_post_meta($profile_id, 'trainer_bio', true);
|
|
$data['training_formats'] = $this->get_meta_array($profile_id, 'training_formats');
|
|
$data['training_locations'] = $this->get_meta_array($profile_id, 'training_locations');
|
|
|
|
// Get upcoming events
|
|
$data['upcoming_events'] = $this->get_trainer_upcoming_events($user_id);
|
|
|
|
return $data;
|
|
}
|
|
|
|
/**
|
|
* Get full venue info
|
|
*
|
|
* @param int $venue_id Venue post ID
|
|
* @return array|null Venue data or null if not found
|
|
*/
|
|
public function get_venue_full_info(int $venue_id): ?array {
|
|
$venue = get_post($venue_id);
|
|
if (!$venue || $venue->post_type !== 'tribe_venue') {
|
|
return null;
|
|
}
|
|
|
|
$data = $this->format_venue_marker($venue_id);
|
|
|
|
// Add additional details
|
|
$data['zip'] = get_post_meta($venue_id, '_VenueZip', true);
|
|
$data['country'] = get_post_meta($venue_id, '_VenueCountry', true);
|
|
$data['phone'] = get_post_meta($venue_id, '_VenuePhone', true);
|
|
$data['website'] = get_post_meta($venue_id, '_VenueURL', true);
|
|
$data['description'] = $venue->post_content;
|
|
$data['capacity'] = get_post_meta($venue_id, '_VenueCapacity', true);
|
|
|
|
// Get equipment and amenities taxonomies
|
|
$equipment = wp_get_post_terms($venue_id, 'venue_equipment', ['fields' => 'names']);
|
|
$data['equipment'] = is_wp_error($equipment) ? [] : $equipment;
|
|
|
|
$amenities = wp_get_post_terms($venue_id, 'venue_amenities', ['fields' => 'names']);
|
|
$data['amenities'] = is_wp_error($amenities) ? [] : $amenities;
|
|
|
|
// Get POC (Point of Contact) information
|
|
$data['poc_user_id'] = get_post_meta($venue_id, '_venue_poc_user_id', true);
|
|
$data['poc_name'] = get_post_meta($venue_id, '_venue_poc_name', true);
|
|
$data['poc_email'] = get_post_meta($venue_id, '_venue_poc_email', true);
|
|
|
|
// If no POC meta, use post author as fallback
|
|
if (empty($data['poc_user_id'])) {
|
|
$data['poc_user_id'] = $venue->post_author;
|
|
$author = get_userdata($venue->post_author);
|
|
if ($author) {
|
|
$data['poc_name'] = $author->display_name;
|
|
$data['poc_email'] = $author->user_email;
|
|
}
|
|
}
|
|
|
|
// Get upcoming events list
|
|
if (function_exists('tribe_get_events')) {
|
|
$events = tribe_get_events([
|
|
'eventDisplay' => 'upcoming',
|
|
'posts_per_page' => 5,
|
|
'venue' => $venue_id
|
|
]);
|
|
|
|
$data['events'] = [];
|
|
foreach ($events as $event) {
|
|
$data['events'][] = [
|
|
'id' => $event->ID,
|
|
'title' => $event->post_title,
|
|
'date' => tribe_get_start_date($event->ID, false, 'M j, Y'),
|
|
'url' => get_permalink($event->ID)
|
|
];
|
|
}
|
|
}
|
|
|
|
return $data;
|
|
}
|
|
|
|
/**
|
|
* Get trainer's upcoming events
|
|
*
|
|
* @param int $user_id User ID
|
|
* @param int $limit Maximum events to return
|
|
* @return array Upcoming events
|
|
*/
|
|
private function get_trainer_upcoming_events(int $user_id, int $limit = 5): array {
|
|
if (!function_exists('tribe_get_events')) {
|
|
return [];
|
|
}
|
|
|
|
$events = tribe_get_events([
|
|
'author' => $user_id,
|
|
'eventDisplay' => 'upcoming',
|
|
'posts_per_page' => $limit
|
|
]);
|
|
|
|
$formatted = [];
|
|
foreach ($events as $event) {
|
|
$formatted[] = [
|
|
'id' => $event->ID,
|
|
'title' => $event->post_title,
|
|
'date' => tribe_get_start_date($event->ID, false, 'M j, Y'),
|
|
'url' => get_permalink($event->ID)
|
|
];
|
|
}
|
|
|
|
return $formatted;
|
|
}
|
|
|
|
/**
|
|
* Get approved user IDs for filtering trainer profiles
|
|
*
|
|
* @return array User IDs
|
|
*/
|
|
private function get_approved_user_ids(): array {
|
|
$user_query = new WP_User_Query([
|
|
'meta_query' => [
|
|
[
|
|
'key' => 'account_status',
|
|
'value' => ['approved', 'active', 'inactive'],
|
|
'compare' => 'IN'
|
|
]
|
|
],
|
|
'fields' => 'ID'
|
|
]);
|
|
|
|
return $user_query->get_results();
|
|
}
|
|
|
|
/**
|
|
* Get meta value as array (handles comma-separated or serialized)
|
|
*
|
|
* @param int $post_id Post ID
|
|
* @param string $meta_key Meta key
|
|
* @return array Values
|
|
*/
|
|
private function get_meta_array(int $post_id, string $meta_key): array {
|
|
$value = get_post_meta($post_id, $meta_key, true);
|
|
|
|
if (empty($value)) {
|
|
return [];
|
|
}
|
|
|
|
if (is_array($value)) {
|
|
return $value;
|
|
}
|
|
|
|
// Handle comma-separated
|
|
if (strpos($value, ',') !== false) {
|
|
return array_map('trim', explode(',', $value));
|
|
}
|
|
|
|
return [$value];
|
|
}
|
|
|
|
/**
|
|
* Calculate distance between two coordinates using Haversine formula
|
|
*
|
|
* @param float $lat1 First latitude
|
|
* @param float $lng1 First longitude
|
|
* @param float $lat2 Second latitude
|
|
* @param float $lng2 Second longitude
|
|
* @return float Distance in kilometers
|
|
*/
|
|
private function calculate_distance(float $lat1, float $lng1, float $lat2, float $lng2): float {
|
|
$earth_radius = 6371; // km
|
|
|
|
$lat1_rad = deg2rad($lat1);
|
|
$lat2_rad = deg2rad($lat2);
|
|
$delta_lat = deg2rad($lat2 - $lat1);
|
|
$delta_lng = deg2rad($lng2 - $lng1);
|
|
|
|
$a = sin($delta_lat / 2) * sin($delta_lat / 2) +
|
|
cos($lat1_rad) * cos($lat2_rad) *
|
|
sin($delta_lng / 2) * sin($delta_lng / 2);
|
|
|
|
$c = 2 * atan2(sqrt($a), sqrt(1 - $a));
|
|
|
|
return $earth_radius * $c;
|
|
}
|
|
|
|
/**
|
|
* Get unique state options from trainers
|
|
*
|
|
* @return array State options
|
|
*/
|
|
public function get_state_options(): array {
|
|
// Check cache first
|
|
$cache_key = 'filter_state_options';
|
|
$cached = wp_cache_get($cache_key, $this->cache_group);
|
|
|
|
if ($cached !== false) {
|
|
return $cached;
|
|
}
|
|
|
|
global $wpdb;
|
|
|
|
$states = $wpdb->get_col("
|
|
SELECT DISTINCT pm.meta_value
|
|
FROM {$wpdb->postmeta} pm
|
|
INNER JOIN {$wpdb->posts} p ON p.ID = pm.post_id
|
|
WHERE pm.meta_key = 'trainer_state'
|
|
AND p.post_type = 'trainer_profile'
|
|
AND p.post_status = 'publish'
|
|
AND pm.meta_value != ''
|
|
ORDER BY pm.meta_value ASC
|
|
");
|
|
|
|
$states = array_filter($states);
|
|
|
|
// Cache for 1 hour
|
|
wp_cache_set($cache_key, $states, $this->cache_group, $this->cache_expiration);
|
|
|
|
return $states;
|
|
}
|
|
|
|
/**
|
|
* Get certification type options
|
|
*
|
|
* @return array Certification options
|
|
*/
|
|
public function get_certification_options(): array {
|
|
// Check cache first
|
|
$cache_key = 'filter_certification_options';
|
|
$cached = wp_cache_get($cache_key, $this->cache_group);
|
|
|
|
if ($cached !== false) {
|
|
return $cached;
|
|
}
|
|
|
|
global $wpdb;
|
|
|
|
$certs = $wpdb->get_col("
|
|
SELECT DISTINCT pm.meta_value
|
|
FROM {$wpdb->postmeta} pm
|
|
INNER JOIN {$wpdb->posts} p ON p.ID = pm.post_id
|
|
WHERE pm.meta_key = 'certification_type'
|
|
AND p.post_type = 'trainer_profile'
|
|
AND p.post_status = 'publish'
|
|
AND pm.meta_value != ''
|
|
ORDER BY pm.meta_value ASC
|
|
");
|
|
|
|
$certs = array_filter($certs);
|
|
|
|
// Cache for 1 hour
|
|
wp_cache_set($cache_key, $certs, $this->cache_group, $this->cache_expiration);
|
|
|
|
return $certs;
|
|
}
|
|
|
|
/**
|
|
* Get training format options
|
|
*
|
|
* @return array Training format options
|
|
*/
|
|
public function get_training_format_options(): array {
|
|
// Check cache first
|
|
$cache_key = 'filter_format_options';
|
|
$cached = wp_cache_get($cache_key, $this->cache_group);
|
|
|
|
if ($cached !== false) {
|
|
return $cached;
|
|
}
|
|
|
|
global $wpdb;
|
|
|
|
$formats_raw = $wpdb->get_col("
|
|
SELECT DISTINCT pm.meta_value
|
|
FROM {$wpdb->postmeta} pm
|
|
INNER JOIN {$wpdb->posts} p ON p.ID = pm.post_id
|
|
WHERE pm.meta_key = 'training_formats'
|
|
AND p.post_type = 'trainer_profile'
|
|
AND p.post_status = 'publish'
|
|
AND pm.meta_value != ''
|
|
");
|
|
|
|
// Process comma-separated values
|
|
$formats = [];
|
|
foreach ($formats_raw as $format_string) {
|
|
if (empty($format_string)) continue;
|
|
$individual = array_map('trim', explode(',', $format_string));
|
|
$formats = array_merge($formats, $individual);
|
|
}
|
|
|
|
$formats = array_unique(array_filter($formats));
|
|
sort($formats);
|
|
|
|
// Cache for 1 hour
|
|
wp_cache_set($cache_key, $formats, $this->cache_group, $this->cache_expiration);
|
|
|
|
return $formats;
|
|
}
|
|
|
|
/**
|
|
* Clear trainer cache
|
|
*/
|
|
public function clear_trainer_cache(): void {
|
|
if (function_exists('wp_cache_delete_group')) {
|
|
wp_cache_delete_group($this->cache_group);
|
|
} else {
|
|
wp_cache_flush();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Clear venue cache
|
|
*/
|
|
public function clear_venue_cache(): void {
|
|
if (function_exists('wp_cache_delete_group')) {
|
|
wp_cache_delete_group($this->cache_group);
|
|
} else {
|
|
wp_cache_flush();
|
|
}
|
|
}
|
|
}
|