Snapshot: MLS sync fixes, image refresh, plugin/theme updates
MLS plugin fixes from this session: - Fix silent insert failures: location column NOT NULL was rejecting wpdb->insert calls, causing ~18k new properties since Dec 2025 to be lost. Inserts now build raw SQL with ST_PointFromText so the spatial column is populated atomically. - Auto-refresh expired media URLs in MLS_Media_Handler::fetch_and_cache(), guarded by a property-level GET_LOCK so concurrent fetches share one API refresh. - Normalize WP_Error to null in mls_get_property_image() so callers can rely on the documented string|null contract. - Support comma-separated property_type filters in MLS_Query and MLS_Cluster so the homepage "View All Commercial" link (?property_type=Commercial+Sale,Land,Farm) actually filters correctly. - Incremental sync now looks back 10 minutes past the latest modification timestamp as a safety margin against missed records. - Smart sync exits silently (info-level, not warning) when a full sync is in progress. Operational: - New cron: weekly full sync Sundays at 3 AM (/usr/local/bin/mls-full-sync). - New cron: hourly 2GB cap on mls-thumbnails/ and cache/transformed-images/ (/usr/local/bin/mls-image-cache-cap). - Logrotate config for wp-content/debug.log (2-day retention, daily rotation, delaycompress). Repo policy: - CLAUDE.md updated with explicit "commit everything except build artifacts" policy. - .gitignore: untrack runtime image caches and debug.log rotations. Other modifications in this snapshot are pre-existing in-flight theme/plugin/db_content_updates work. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -41,16 +41,31 @@ class MLS_Cluster {
|
||||
|
||||
/**
|
||||
* Minimum properties before any grouping kicks in
|
||||
* Below this, always show individual markers
|
||||
* Below this, always show individual markers regardless of zoom
|
||||
*/
|
||||
const MIN_FOR_GROUPING = 30;
|
||||
|
||||
/**
|
||||
* Viewport-aware marker threshold
|
||||
* If viewport contains fewer than this many properties AND zoom >= 9,
|
||||
* show individual markers instead of clusters.
|
||||
* This helps mobile viewports which show smaller geographic areas.
|
||||
*/
|
||||
const VIEWPORT_MARKER_THRESHOLD = 120;
|
||||
|
||||
/**
|
||||
* Minimum zoom level for viewport-aware marker display
|
||||
* Below this zoom, always use density/cluster mode even with few properties
|
||||
* (prevents showing 100+ scattered markers across entire state)
|
||||
*/
|
||||
const MIN_ZOOM_FOR_VIEWPORT_MARKERS = 9;
|
||||
|
||||
/**
|
||||
* Zoom thresholds for visualization modes
|
||||
*/
|
||||
const ZOOM_DENSE_MAX = 5; // 1-5: density dots (40% more dense)
|
||||
const ZOOM_DENSITY_MAX = 8; // 6-8: density dots (normal)
|
||||
const ZOOM_CLUSTER_MAX = 15; // 9-15: numbered clusters
|
||||
const ZOOM_CLUSTER_MAX = 15; // 9-15: numbered clusters (unless viewport threshold met)
|
||||
// 16+: individual markers
|
||||
|
||||
/**
|
||||
@@ -269,8 +284,15 @@ class MLS_Cluster {
|
||||
}
|
||||
|
||||
if ($args['property_type']) {
|
||||
$where[] = 'property_type = %s';
|
||||
$values[] = $args['property_type'];
|
||||
$types = array_filter(array_map('trim', explode(',', $args['property_type'])));
|
||||
if (count($types) === 1) {
|
||||
$where[] = 'property_type = %s';
|
||||
$values[] = $types[0];
|
||||
} elseif (count($types) > 1) {
|
||||
$placeholders = implode(',', array_fill(0, count($types), '%s'));
|
||||
$where[] = "property_type IN ({$placeholders})";
|
||||
$values = array_merge($values, $types);
|
||||
}
|
||||
}
|
||||
|
||||
if ($args['city']) {
|
||||
@@ -314,7 +336,7 @@ class MLS_Cluster {
|
||||
$total = (int) $wpdb->get_var($count_sql);
|
||||
}
|
||||
|
||||
// If few properties, always show individual markers (no grouping)
|
||||
// If very few properties, always show individual markers (no grouping)
|
||||
if ($total <= self::MIN_FOR_GROUPING) {
|
||||
return $this->get_individual_markers($where_sql, $values, $total);
|
||||
}
|
||||
@@ -327,19 +349,34 @@ class MLS_Cluster {
|
||||
|
||||
$zoom = (int) $args['zoom'];
|
||||
|
||||
// Determine visualization mode based on zoom level
|
||||
// Zoom 1-5: Density dots (40% more dense)
|
||||
// Determine visualization mode based on zoom level AND viewport property count
|
||||
//
|
||||
// Priority order:
|
||||
// 1. Very zoomed out (zoom 1-5): Always density dots (dense)
|
||||
// 2. Zoomed out (zoom 6-8): Always density dots (normal)
|
||||
// 3. Medium zoom (9-15) with FEW properties in viewport: Individual markers
|
||||
// 4. Medium zoom (9-15) with MANY properties: Clusters
|
||||
// 5. Very zoomed in (16+): Always individual markers
|
||||
|
||||
// Zoom 1-5: Density dots (40% more dense) - always, regardless of count
|
||||
if ($zoom <= self::ZOOM_DENSE_MAX) {
|
||||
return $this->get_density_data($where_sql, $values, $zoom, $center_lat, $total, self::DENSITY_DOT_SPACING_DENSE);
|
||||
}
|
||||
|
||||
// Zoom 6-11: Density dots (normal spacing)
|
||||
// Zoom 6-8: Density dots (normal spacing) - always, regardless of count
|
||||
if ($zoom <= self::ZOOM_DENSITY_MAX) {
|
||||
return $this->get_density_data($where_sql, $values, $zoom, $center_lat, $total, self::DENSITY_DOT_SPACING);
|
||||
}
|
||||
|
||||
// Zoom 9-15: Always use server-side clusters (let server handle grouping)
|
||||
// Zoom 9-15: Use viewport-aware threshold
|
||||
// If viewport has relatively few properties, show individual markers
|
||||
// This helps mobile viewports which show smaller geographic areas at same zoom level
|
||||
if ($zoom <= self::ZOOM_CLUSTER_MAX) {
|
||||
if ($total <= self::VIEWPORT_MARKER_THRESHOLD) {
|
||||
// Few enough properties in viewport - show individual markers
|
||||
return $this->get_individual_markers($where_sql, $values, $total);
|
||||
}
|
||||
// Many properties - use clusters
|
||||
return $this->get_cluster_data($where_sql, $values, $zoom, $center_lat, $total);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user