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:
@@ -106,54 +106,53 @@ $view_class = $show_map ? 'is-map-view' : 'is-grid-view';
|
||||
</main>
|
||||
|
||||
<?php
|
||||
// Always load map data for responsive switching
|
||||
$map_properties = new WP_Query(array(
|
||||
'post_type' => 'property',
|
||||
'posts_per_page' => -1,
|
||||
));
|
||||
|
||||
// Load MLS properties for map markers
|
||||
$markers = array();
|
||||
$city_coords = array(
|
||||
'Albert Lea' => array(43.6480, -93.3685),
|
||||
'Austin' => array(43.6666, -92.9746),
|
||||
'Glenville' => array(43.5733, -93.2779),
|
||||
'Emmons' => array(43.5013, -93.4896),
|
||||
'Clarks Grove' => array(43.7627, -93.3196),
|
||||
'Alden' => array(43.6719, -93.5768),
|
||||
'Hartland' => array(43.8030, -93.4846),
|
||||
'Geneva' => array(43.8255, -93.2682),
|
||||
'Owatonna' => array(44.0838, -93.2260),
|
||||
'Faribault' => array(44.2949, -93.2688),
|
||||
'Rochester' => array(44.0234, -92.4699),
|
||||
'Mankato' => array(44.1636, -93.9994),
|
||||
);
|
||||
|
||||
if ($map_properties->have_posts()) :
|
||||
while ($map_properties->have_posts()) :
|
||||
$map_properties->the_post();
|
||||
$city = get_field('city');
|
||||
$price = get_field('property_price');
|
||||
$address = get_field('street_address');
|
||||
if (function_exists('mls_get_properties')) {
|
||||
$mls_properties = mls_get_properties(array(
|
||||
'status' => 'Active',
|
||||
'limit' => 1000, // Reasonable limit for map performance
|
||||
'orderby' => 'modification_timestamp',
|
||||
'order' => 'DESC',
|
||||
));
|
||||
|
||||
// Get coords for city, default to Albert Lea
|
||||
$coords = isset($city_coords[$city]) ? $city_coords[$city] : $city_coords['Albert Lea'];
|
||||
// Add small random offset (seeded by post ID for consistency)
|
||||
srand(get_the_ID());
|
||||
$lat = $coords[0] + (rand(-50, 50) / 10000);
|
||||
$lng = $coords[1] + (rand(-50, 50) / 10000);
|
||||
foreach ($mls_properties as $property) {
|
||||
// Skip properties without coordinates
|
||||
if (empty($property->latitude) || empty($property->longitude)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Format address
|
||||
$address_parts = array();
|
||||
if ($property->street_number) {
|
||||
$address_parts[] = $property->street_number;
|
||||
}
|
||||
if ($property->street_name) {
|
||||
$address_parts[] = $property->street_name;
|
||||
}
|
||||
if ($property->street_suffix) {
|
||||
$address_parts[] = $property->street_suffix;
|
||||
}
|
||||
$street = implode(' ', $address_parts);
|
||||
$full_address = $street ? $street . ', ' . $property->city : $property->city;
|
||||
|
||||
$markers[] = array(
|
||||
'id' => get_the_ID(),
|
||||
'lat' => $lat,
|
||||
'lng' => $lng,
|
||||
'title' => get_the_title(),
|
||||
'price' => '$' . number_format($price),
|
||||
'address' => $address . ', ' . $city,
|
||||
'url' => get_permalink(),
|
||||
'id' => $property->listing_key,
|
||||
'lat' => (float) $property->latitude,
|
||||
'lng' => (float) $property->longitude,
|
||||
'title' => $full_address,
|
||||
'price' => '$' . number_format($property->list_price),
|
||||
'address' => $full_address,
|
||||
'url' => home_url('/properties/?listing=' . urlencode($property->listing_key)),
|
||||
'beds' => $property->bedrooms_total,
|
||||
'baths' => $property->bathrooms_total,
|
||||
'sqft' => $property->living_area,
|
||||
'status' => $property->standard_status,
|
||||
'photo' => null, // Placeholder - photos will be added later
|
||||
);
|
||||
endwhile;
|
||||
wp_reset_postdata();
|
||||
endif;
|
||||
}
|
||||
}
|
||||
?>
|
||||
<!-- Leaflet CSS -->
|
||||
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css" integrity="sha256-p4NxAoJBhIIN+hmNHrzRCf9tD/miZyoHS5obTRR9BMY=" crossorigin=""/>
|
||||
|
||||
@@ -11,7 +11,7 @@ if (!defined('ABSPATH')) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle AJAX property filter requests
|
||||
* Handle AJAX property filter requests (MLS-based)
|
||||
*/
|
||||
function homeproz_ajax_filter_properties() {
|
||||
// Verify nonce
|
||||
@@ -19,6 +19,11 @@ function homeproz_ajax_filter_properties() {
|
||||
wp_send_json_error('Invalid nonce');
|
||||
}
|
||||
|
||||
// Check if MLS plugin is available
|
||||
if (!function_exists('mls_get_properties')) {
|
||||
wp_send_json_error('MLS plugin not available');
|
||||
}
|
||||
|
||||
// Get filter values
|
||||
$property_type = isset($_POST['property_type']) ? sanitize_text_field($_POST['property_type']) : '';
|
||||
$property_status = isset($_POST['property_status']) ? sanitize_text_field($_POST['property_status']) : '';
|
||||
@@ -29,100 +34,40 @@ function homeproz_ajax_filter_properties() {
|
||||
$sort = isset($_POST['sort']) ? sanitize_text_field($_POST['sort']) : 'newest';
|
||||
$paged = isset($_POST['paged']) ? intval($_POST['paged']) : 1;
|
||||
|
||||
// Build query args
|
||||
$args = array(
|
||||
'post_type' => 'property',
|
||||
'posts_per_page' => 12,
|
||||
'paged' => $paged,
|
||||
// Build MLS query args
|
||||
$per_page = 12;
|
||||
$mls_args = array(
|
||||
'status' => $property_status ?: 'Active',
|
||||
'limit' => 1000, // Get all for counting, then paginate
|
||||
'orderby' => 'modification_timestamp',
|
||||
'order' => 'DESC',
|
||||
);
|
||||
|
||||
// Taxonomy filters
|
||||
$tax_query = array();
|
||||
|
||||
// Map filter values to MLS query args
|
||||
if ($property_type) {
|
||||
$tax_query[] = array(
|
||||
'taxonomy' => 'property_type',
|
||||
'field' => 'slug',
|
||||
'terms' => $property_type,
|
||||
);
|
||||
$mls_args['property_type'] = $property_type;
|
||||
}
|
||||
|
||||
if ($property_status) {
|
||||
$tax_query[] = array(
|
||||
'taxonomy' => 'property_status',
|
||||
'field' => 'slug',
|
||||
'terms' => $property_status,
|
||||
);
|
||||
}
|
||||
|
||||
if ($property_location) {
|
||||
$tax_query[] = array(
|
||||
'taxonomy' => 'property_location',
|
||||
'field' => 'slug',
|
||||
'terms' => $property_location,
|
||||
);
|
||||
$mls_args['city'] = $property_location;
|
||||
}
|
||||
|
||||
if (!empty($tax_query)) {
|
||||
$args['tax_query'] = $tax_query;
|
||||
if (count($tax_query) > 1) {
|
||||
$args['tax_query']['relation'] = 'AND';
|
||||
}
|
||||
}
|
||||
|
||||
// Meta query for price and bedrooms
|
||||
$meta_query = array();
|
||||
|
||||
if ($min_price) {
|
||||
$meta_query[] = array(
|
||||
'key' => 'property_price',
|
||||
'value' => $min_price,
|
||||
'type' => 'NUMERIC',
|
||||
'compare' => '>=',
|
||||
);
|
||||
$mls_args['min_price'] = $min_price;
|
||||
}
|
||||
|
||||
if ($max_price) {
|
||||
$meta_query[] = array(
|
||||
'key' => 'property_price',
|
||||
'value' => $max_price,
|
||||
'type' => 'NUMERIC',
|
||||
'compare' => '<=',
|
||||
);
|
||||
$mls_args['max_price'] = $max_price;
|
||||
}
|
||||
|
||||
if ($beds) {
|
||||
$meta_query[] = array(
|
||||
'key' => 'bedrooms',
|
||||
'value' => $beds,
|
||||
'type' => 'NUMERIC',
|
||||
'compare' => '>=',
|
||||
);
|
||||
$mls_args['min_beds'] = $beds;
|
||||
}
|
||||
|
||||
if (!empty($meta_query)) {
|
||||
$args['meta_query'] = $meta_query;
|
||||
if (count($meta_query) > 1) {
|
||||
$args['meta_query']['relation'] = 'AND';
|
||||
}
|
||||
}
|
||||
|
||||
// Fetch all matching properties for status-based sorting
|
||||
$args['posts_per_page'] = -1;
|
||||
$args['orderby'] = 'modified';
|
||||
$args['order'] = 'DESC';
|
||||
|
||||
$all_properties = get_posts($args);
|
||||
|
||||
// Sort by status (Active > Pending > Sold) then by modified date
|
||||
$sorted_properties = homeproz_sort_properties_by_status($all_properties);
|
||||
// Fetch all matching properties
|
||||
$all_properties = mls_get_properties($mls_args);
|
||||
|
||||
// Handle pagination manually
|
||||
$per_page = 12;
|
||||
$total = count($sorted_properties);
|
||||
$total = count($all_properties);
|
||||
$max_pages = ceil($total / $per_page);
|
||||
$offset = ($paged - 1) * $per_page;
|
||||
$paged_properties = array_slice($sorted_properties, $offset, $per_page);
|
||||
$paged_properties = array_slice($all_properties, $offset, $per_page);
|
||||
|
||||
ob_start();
|
||||
|
||||
@@ -142,13 +87,11 @@ function homeproz_ajax_filter_properties() {
|
||||
<?php if (!empty($paged_properties)) : ?>
|
||||
<div class="properties-grid">
|
||||
<?php
|
||||
global $post;
|
||||
foreach ($paged_properties as $property_post) :
|
||||
$post = $property_post;
|
||||
setup_postdata($post);
|
||||
get_template_part('template-parts/property/property-card');
|
||||
foreach ($paged_properties as $property) :
|
||||
// Pass MLS property to card template
|
||||
set_query_var('mls_property', $property);
|
||||
get_template_part('template-parts/property/property-card-mls');
|
||||
endforeach;
|
||||
wp_reset_postdata();
|
||||
?>
|
||||
</div>
|
||||
|
||||
@@ -197,43 +140,42 @@ function homeproz_ajax_filter_properties() {
|
||||
<?php
|
||||
$html = ob_get_clean();
|
||||
|
||||
// Build markers data for map view
|
||||
// Build markers data for map view from MLS properties
|
||||
$markers = array();
|
||||
$city_coords = array(
|
||||
'Albert Lea' => array(43.6480, -93.3685),
|
||||
'Austin' => array(43.6666, -92.9746),
|
||||
'Glenville' => array(43.5733, -93.2779),
|
||||
'Emmons' => array(43.5013, -93.4896),
|
||||
'Clarks Grove' => array(43.7627, -93.3196),
|
||||
'Alden' => array(43.6719, -93.5768),
|
||||
'Hartland' => array(43.8030, -93.4846),
|
||||
'Geneva' => array(43.8255, -93.2682),
|
||||
'Owatonna' => array(44.0838, -93.2260),
|
||||
'Faribault' => array(44.2949, -93.2688),
|
||||
'Rochester' => array(44.0234, -92.4699),
|
||||
'Mankato' => array(44.1636, -93.9994),
|
||||
);
|
||||
|
||||
foreach ($sorted_properties as $prop) {
|
||||
$city = get_field('city', $prop->ID);
|
||||
$price = get_field('property_price', $prop->ID);
|
||||
$address = get_field('street_address', $prop->ID);
|
||||
foreach ($all_properties as $property) {
|
||||
// Skip properties without coordinates
|
||||
if (empty($property->latitude) || empty($property->longitude)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Get coords for city, default to Albert Lea
|
||||
$coords = isset($city_coords[$city]) ? $city_coords[$city] : $city_coords['Albert Lea'];
|
||||
// Add small random offset to prevent overlapping markers (seeded by post ID for consistency)
|
||||
srand($prop->ID);
|
||||
$lat = $coords[0] + (rand(-50, 50) / 10000);
|
||||
$lng = $coords[1] + (rand(-50, 50) / 10000);
|
||||
// Format address
|
||||
$address_parts = array();
|
||||
if ($property->street_number) {
|
||||
$address_parts[] = $property->street_number;
|
||||
}
|
||||
if ($property->street_name) {
|
||||
$address_parts[] = $property->street_name;
|
||||
}
|
||||
if ($property->street_suffix) {
|
||||
$address_parts[] = $property->street_suffix;
|
||||
}
|
||||
$street = implode(' ', $address_parts);
|
||||
$full_address = $street ? $street . ', ' . $property->city : $property->city;
|
||||
|
||||
$markers[] = array(
|
||||
'id' => $prop->ID,
|
||||
'lat' => $lat,
|
||||
'lng' => $lng,
|
||||
'title' => $prop->post_title,
|
||||
'price' => '$' . number_format($price),
|
||||
'address' => $address . ', ' . $city,
|
||||
'url' => get_permalink($prop->ID),
|
||||
'id' => $property->listing_key,
|
||||
'lat' => (float) $property->latitude,
|
||||
'lng' => (float) $property->longitude,
|
||||
'title' => $full_address,
|
||||
'price' => '$' . number_format($property->list_price),
|
||||
'address' => $full_address,
|
||||
'url' => home_url('/properties/?listing=' . urlencode($property->listing_key)),
|
||||
'beds' => $property->bedrooms_total,
|
||||
'baths' => $property->bathrooms_total,
|
||||
'sqft' => $property->living_area,
|
||||
'status' => $property->standard_status,
|
||||
'photo' => null, // Placeholder - photos will be added later
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,136 @@
|
||||
<?php
|
||||
/**
|
||||
* MLS Property Card Template Part
|
||||
*
|
||||
* Displays an MLS property in card format for archive views
|
||||
*
|
||||
* @package HomeProz
|
||||
*/
|
||||
|
||||
// Prevent direct access
|
||||
if (!defined('ABSPATH')) {
|
||||
exit;
|
||||
}
|
||||
|
||||
// Get MLS property data from query var
|
||||
$property = get_query_var('mls_property');
|
||||
|
||||
if (!$property) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Extract property data
|
||||
$listing_key = $property->listing_key;
|
||||
$price = $property->list_price;
|
||||
$bedrooms = $property->bedrooms_total;
|
||||
$bathrooms = $property->bathrooms_total;
|
||||
$square_feet = $property->living_area;
|
||||
$status = $property->standard_status;
|
||||
$public_remarks = $property->public_remarks;
|
||||
|
||||
// Format address
|
||||
$address_parts = array();
|
||||
if ($property->street_number) {
|
||||
$address_parts[] = $property->street_number;
|
||||
}
|
||||
if ($property->street_name) {
|
||||
$address_parts[] = $property->street_name;
|
||||
}
|
||||
if ($property->street_suffix) {
|
||||
$address_parts[] = $property->street_suffix;
|
||||
}
|
||||
$street = implode(' ', $address_parts);
|
||||
|
||||
$full_address = $street;
|
||||
if ($property->city) {
|
||||
$full_address .= ', ' . $property->city;
|
||||
}
|
||||
if ($property->state_or_province) {
|
||||
$full_address .= ', ' . $property->state_or_province;
|
||||
}
|
||||
|
||||
// Property URL (will be updated when single property view is implemented)
|
||||
$property_url = home_url('/properties/?listing=' . urlencode($listing_key));
|
||||
|
||||
// Status class mapping
|
||||
$status_class = 'badge-active';
|
||||
if ($status === 'Pending') {
|
||||
$status_class = 'badge-pending';
|
||||
} elseif ($status === 'Closed' || $status === 'Sold') {
|
||||
$status_class = 'badge-sold';
|
||||
}
|
||||
?>
|
||||
|
||||
<article id="property-<?php echo esc_attr($listing_key); ?>" data-property-id="<?php echo esc_attr($listing_key); ?>" class="property-card card mls-property">
|
||||
<a href="<?php echo esc_url($property_url); ?>" class="property-card-link-overlay" aria-hidden="true" tabindex="-1"></a>
|
||||
<div class="property-card-image">
|
||||
<!-- Photo placeholder - will be implemented later -->
|
||||
<div class="property-card-placeholder">
|
||||
<svg width="48" height="48" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" aria-hidden="true">
|
||||
<path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/>
|
||||
<polyline points="9 22 9 12 15 12 15 22"/>
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
<?php if ($status) : ?>
|
||||
<span class="property-card-badge badge <?php echo esc_attr($status_class); ?>">
|
||||
<?php echo esc_html($status); ?>
|
||||
</span>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<div class="property-card-content">
|
||||
<div class="property-card-price">
|
||||
<?php echo esc_html('$' . number_format($price)); ?>
|
||||
</div>
|
||||
|
||||
<h3 class="property-card-title">
|
||||
<?php echo esc_html($full_address ?: 'Property ' . $listing_key); ?>
|
||||
</h3>
|
||||
|
||||
<?php if ($bedrooms || $bathrooms || $square_feet) : ?>
|
||||
<ul class="property-card-specs">
|
||||
<?php if ($bedrooms) : ?>
|
||||
<li class="spec-item">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true">
|
||||
<path d="M3 7v11h18V7M3 7V4a1 1 0 0 1 1-1h16a1 1 0 0 1 1 1v3M3 7h18M7 11h4v4H7zM14 11h3"/>
|
||||
</svg>
|
||||
<span><?php echo esc_html($bedrooms); ?> <?php echo $bedrooms == 1 ? 'Bed' : 'Beds'; ?></span>
|
||||
</li>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if ($bathrooms) : ?>
|
||||
<li class="spec-item">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true">
|
||||
<path d="M4 12h16M4 12v7a1 1 0 0 0 1 1h14a1 1 0 0 0 1-1v-7M4 12V6a2 2 0 0 1 2-2h3v2a1 1 0 0 0 1 1h1a1 1 0 0 0 1-1V4"/>
|
||||
</svg>
|
||||
<span><?php echo esc_html($bathrooms); ?> <?php echo $bathrooms == 1 ? 'Bath' : 'Baths'; ?></span>
|
||||
</li>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if ($square_feet) : ?>
|
||||
<li class="spec-item">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true">
|
||||
<rect x="3" y="3" width="18" height="18" rx="2"/>
|
||||
<path d="M3 9h18M9 3v18"/>
|
||||
</svg>
|
||||
<span><?php echo esc_html(number_format($square_feet)); ?> sqft</span>
|
||||
</li>
|
||||
<?php endif; ?>
|
||||
</ul>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if ($public_remarks) : ?>
|
||||
<p class="property-card-excerpt">
|
||||
<?php echo esc_html(wp_trim_words($public_remarks, 15, '...')); ?>
|
||||
</p>
|
||||
<?php endif; ?>
|
||||
|
||||
<span class="property-card-link">
|
||||
View Details
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true">
|
||||
<path d="M5 12h14M12 5l7 7-7 7"/>
|
||||
</svg>
|
||||
</span>
|
||||
</div>
|
||||
</article>
|
||||
@@ -2,7 +2,7 @@
|
||||
/**
|
||||
* Property Results Template Part
|
||||
*
|
||||
* Displays property results for archive/search
|
||||
* Displays MLS property results for archive/search
|
||||
*
|
||||
* @package HomeProz
|
||||
*/
|
||||
@@ -12,140 +12,70 @@ if (!defined('ABSPATH')) {
|
||||
exit;
|
||||
}
|
||||
|
||||
// Get filter values
|
||||
// Check if MLS plugin is available
|
||||
if (!function_exists('mls_get_properties')) {
|
||||
?>
|
||||
<div class="no-properties">
|
||||
<h3>Properties Unavailable</h3>
|
||||
<p>Property listings are temporarily unavailable. Please try again later.</p>
|
||||
</div>
|
||||
<?php
|
||||
return;
|
||||
}
|
||||
|
||||
// Get filter values from URL
|
||||
$current_type = isset($_GET['property_type']) ? sanitize_text_field($_GET['property_type']) : '';
|
||||
$current_status = isset($_GET['property_status']) ? sanitize_text_field($_GET['property_status']) : '';
|
||||
$current_status = isset($_GET['property_status']) ? sanitize_text_field($_GET['property_status']) : 'Active';
|
||||
$current_location = isset($_GET['property_location']) ? sanitize_text_field($_GET['property_location']) : '';
|
||||
$current_min_price = isset($_GET['min_price']) ? intval($_GET['min_price']) : '';
|
||||
$current_max_price = isset($_GET['max_price']) ? intval($_GET['max_price']) : '';
|
||||
$current_beds = isset($_GET['beds']) ? intval($_GET['beds']) : '';
|
||||
$current_sort = isset($_GET['sort']) ? sanitize_text_field($_GET['sort']) : 'newest';
|
||||
|
||||
// Build query args
|
||||
// Pagination
|
||||
$paged = get_query_var('paged') ? get_query_var('paged') : 1;
|
||||
$args = array(
|
||||
'post_type' => 'property',
|
||||
'posts_per_page' => 12,
|
||||
'paged' => $paged,
|
||||
$per_page = 12;
|
||||
|
||||
// Build MLS query args
|
||||
$mls_args = array(
|
||||
'status' => $current_status ?: 'Active',
|
||||
'limit' => 1000, // Get all for counting, then paginate
|
||||
'orderby' => 'modification_timestamp',
|
||||
'order' => 'DESC',
|
||||
);
|
||||
|
||||
// Taxonomy filters
|
||||
$tax_query = array();
|
||||
|
||||
// Map filter values to MLS query args
|
||||
if ($current_type) {
|
||||
$tax_query[] = array(
|
||||
'taxonomy' => 'property_type',
|
||||
'field' => 'slug',
|
||||
'terms' => $current_type,
|
||||
);
|
||||
$mls_args['property_type'] = $current_type;
|
||||
}
|
||||
|
||||
if ($current_status) {
|
||||
$tax_query[] = array(
|
||||
'taxonomy' => 'property_status',
|
||||
'field' => 'slug',
|
||||
'terms' => $current_status,
|
||||
);
|
||||
}
|
||||
|
||||
if ($current_location) {
|
||||
$tax_query[] = array(
|
||||
'taxonomy' => 'property_location',
|
||||
'field' => 'slug',
|
||||
'terms' => $current_location,
|
||||
);
|
||||
$mls_args['city'] = $current_location;
|
||||
}
|
||||
|
||||
if (!empty($tax_query)) {
|
||||
$args['tax_query'] = $tax_query;
|
||||
if (count($tax_query) > 1) {
|
||||
$args['tax_query']['relation'] = 'AND';
|
||||
}
|
||||
}
|
||||
|
||||
// Meta query for price and bedrooms
|
||||
$meta_query = array();
|
||||
|
||||
if ($current_min_price) {
|
||||
$meta_query[] = array(
|
||||
'key' => 'property_price',
|
||||
'value' => $current_min_price,
|
||||
'type' => 'NUMERIC',
|
||||
'compare' => '>=',
|
||||
);
|
||||
$mls_args['min_price'] = $current_min_price;
|
||||
}
|
||||
|
||||
if ($current_max_price) {
|
||||
$meta_query[] = array(
|
||||
'key' => 'property_price',
|
||||
'value' => $current_max_price,
|
||||
'type' => 'NUMERIC',
|
||||
'compare' => '<=',
|
||||
);
|
||||
$mls_args['max_price'] = $current_max_price;
|
||||
}
|
||||
|
||||
if ($current_beds) {
|
||||
$meta_query[] = array(
|
||||
'key' => 'bedrooms',
|
||||
'value' => $current_beds,
|
||||
'type' => 'NUMERIC',
|
||||
'compare' => '>=',
|
||||
);
|
||||
$mls_args['min_beds'] = $current_beds;
|
||||
}
|
||||
|
||||
if (!empty($meta_query)) {
|
||||
$args['meta_query'] = $meta_query;
|
||||
if (count($meta_query) > 1) {
|
||||
$args['meta_query']['relation'] = 'AND';
|
||||
}
|
||||
}
|
||||
|
||||
// For status-based sorting, we need to fetch all matching properties and sort in PHP
|
||||
// This is efficient for real estate sites with < 1000 properties
|
||||
$args['posts_per_page'] = -1;
|
||||
$args['orderby'] = 'modified';
|
||||
$args['order'] = 'DESC';
|
||||
|
||||
$all_properties = get_posts($args);
|
||||
|
||||
// Sort by status (Active > Pending > Sold) then by modified date
|
||||
$sorted_properties = homeproz_sort_properties_by_status($all_properties);
|
||||
// Fetch all matching properties
|
||||
$all_properties = mls_get_properties($mls_args);
|
||||
|
||||
// Handle pagination manually
|
||||
$per_page = 12;
|
||||
$total = count($sorted_properties);
|
||||
$total = count($all_properties);
|
||||
$max_pages = ceil($total / $per_page);
|
||||
$offset = ($paged - 1) * $per_page;
|
||||
$paged_properties = array_slice($sorted_properties, $offset, $per_page);
|
||||
|
||||
// Create a fake WP_Query-like object for compatibility
|
||||
$properties = (object) array(
|
||||
'posts' => $paged_properties,
|
||||
'found_posts' => $total,
|
||||
'max_num_pages' => $max_pages,
|
||||
);
|
||||
|
||||
// Helper function to check if we have posts
|
||||
$properties->have_posts = function() use (&$paged_properties) {
|
||||
static $index = 0;
|
||||
if ($index < count($paged_properties)) {
|
||||
return true;
|
||||
}
|
||||
$index = 0;
|
||||
return false;
|
||||
};
|
||||
|
||||
// Loop through properties manually
|
||||
global $post;
|
||||
$property_index = 0;
|
||||
$paged_properties = array_slice($all_properties, $offset, $per_page);
|
||||
?>
|
||||
|
||||
<!-- Results Meta -->
|
||||
<div class="properties-meta">
|
||||
<p class="properties-count">
|
||||
<?php if ($properties->found_posts > 0) : ?>
|
||||
Showing <strong><?php echo esc_html($properties->found_posts); ?></strong>
|
||||
<?php echo $properties->found_posts === 1 ? 'property' : 'properties'; ?>
|
||||
<?php if ($total > 0) : ?>
|
||||
Showing <strong><?php echo esc_html($total); ?></strong>
|
||||
<?php echo $total === 1 ? 'property' : 'properties'; ?>
|
||||
<?php else : ?>
|
||||
No properties found
|
||||
<?php endif; ?>
|
||||
@@ -155,12 +85,11 @@ $property_index = 0;
|
||||
<?php if (!empty($paged_properties)) : ?>
|
||||
<div class="properties-grid">
|
||||
<?php
|
||||
foreach ($paged_properties as $property_post) :
|
||||
$post = $property_post;
|
||||
setup_postdata($post);
|
||||
get_template_part('template-parts/property/property-card');
|
||||
foreach ($paged_properties as $property) :
|
||||
// Pass MLS property to card template
|
||||
set_query_var('mls_property', $property);
|
||||
get_template_part('template-parts/property/property-card-mls');
|
||||
endforeach;
|
||||
wp_reset_postdata();
|
||||
?>
|
||||
</div>
|
||||
|
||||
@@ -195,5 +124,3 @@ $property_index = 0;
|
||||
<a href="<?php echo esc_url(get_post_type_archive_link('property')); ?>" class="btn btn-primary">View All Properties</a>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php wp_reset_postdata(); ?>
|
||||
|
||||
Reference in New Issue
Block a user