Integrate MLS listings with property map and add smart sync

Property Map:
- Replace ACF-based property display with MLS database queries
- Use real lat/lng coordinates from MLS (100% coverage)
- Create property-card-mls.php template for MLS property cards
- Update AJAX handler to filter MLS properties

MLS Plugin Enhancements:
- Add 'wp mls run' smart sync command (auto-detects full/incremental/resume)
- Add database index migrations for lat/lng and composite search indexes
- Add comprehensive README.md documentation

Documentation:
- Update site README.md with sysadmin quick reference
- Add FEATURES_PENDING_12_15.md tracking client feature requests

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Hanson.xyz Dev
2025-12-15 22:32:41 -06:00
parent b9cddd2f64
commit fc018ca604
13 changed files with 2346 additions and 308 deletions
@@ -9,6 +9,12 @@ if (!defined('ABSPATH')) {
class MLS_DB {
/**
* Schema version for index migrations
* Increment this when adding new indexes
*/
const SCHEMA_VERSION = 2;
/**
* Get table name with prefix
*
@@ -275,6 +281,78 @@ class MLS_DB {
) {$charset_collate};";
dbDelta($sql_media_log);
// Run index migrations
self::run_index_migrations();
}
/**
* Run index migrations that dbDelta cannot handle
*
* dbDelta can create tables and add columns, but cannot add indexes
* to existing tables. This method handles incremental index additions.
*/
public static function run_index_migrations() {
global $wpdb;
$current_schema = (int) get_option('mls_schema_version', 1);
// Migration to schema version 2: Add search and geo indexes
if ($current_schema < 2) {
$table_properties = $wpdb->prefix . MLS_TABLE_PROPERTIES;
$table_media = $wpdb->prefix . MLS_TABLE_MEDIA;
// Check and add indexes only if they don't exist
$existing_indexes = self::get_existing_indexes($table_properties);
// Geospatial indexes for bounding box queries
if (!isset($existing_indexes['idx_latitude'])) {
$wpdb->query("ALTER TABLE {$table_properties} ADD INDEX idx_latitude (latitude)");
}
if (!isset($existing_indexes['idx_longitude'])) {
$wpdb->query("ALTER TABLE {$table_properties} ADD INDEX idx_longitude (longitude)");
}
// Composite index for common search pattern (status + city + price)
if (!isset($existing_indexes['idx_status_city_price'])) {
$wpdb->query("ALTER TABLE {$table_properties} ADD INDEX idx_status_city_price (standard_status, city, list_price)");
}
// Composite index for status + property_type searches
if (!isset($existing_indexes['idx_status_type'])) {
$wpdb->query("ALTER TABLE {$table_properties} ADD INDEX idx_status_type (standard_status, property_type)");
}
// Media table: composite index for listing + order (eliminates filesort)
$media_indexes = self::get_existing_indexes($table_media);
if (!isset($media_indexes['idx_listing_order'])) {
$wpdb->query("ALTER TABLE {$table_media} ADD INDEX idx_listing_order (listing_key, media_order)");
}
update_option('mls_schema_version', 2);
}
// Future migrations go here:
// if ($current_schema < 3) { ... }
}
/**
* Get existing indexes for a table
*
* @param string $table Full table name
* @return array Index names as keys
*/
private static function get_existing_indexes($table) {
global $wpdb;
$indexes = array();
$results = $wpdb->get_results("SHOW INDEX FROM {$table}");
foreach ($results as $row) {
$indexes[$row->Key_name] = true;
}
return $indexes;
}
/**