| 1 |
<?php |
| 2 |
/** |
| 3 |
* Live mode: swap the prepared-listings payload for a live MLS search. |
| 4 |
* |
| 5 |
* The render stack (grid, slider, AJAX repaint, archive) all flow through |
| 6 |
* Mlsimport_Standalone_Render::prepare(), whose payload passes the |
| 7 |
* mlsimport_prepared_listings filter — this one callback is the whole grid |
| 8 |
* read path. The render args already speak the live param vocabulary |
| 9 |
* (city/status/price_min/orderby/limit/page), so they pass straight to |
| 10 |
* mlsimport_live_search(). |
| 11 |
* |
| 12 |
* @package Mlsimport |
| 13 |
*/ |
| 14 |
|
| 15 |
if ( ! defined( 'ABSPATH' ) ) { |
| 16 |
exit; |
| 17 |
} |
| 18 |
|
| 19 |
/** |
| 20 |
* Replace the stored payload with live MLS results when live mode is on. |
| 21 |
* |
| 22 |
* Posts are synthetic WP_Post objects with NEGATIVE IDs: card templates run |
| 23 |
* get_post_meta()/has_post_thumbnail() on $post->ID before the card-view |
| 24 |
* filter fires, and a negative ID guarantees those hit nothing real. The row |
| 25 |
* keyed to each ID carries the flat-table scalars plus the raw record in |
| 26 |
* _live for the card rebuild. |
| 27 |
* |
| 28 |
* @param array $payload {posts, rows, total} from the stored query. |
| 29 |
* @param array $args Render args (the standalone filter params). |
| 30 |
* @return array |
| 31 |
*/ |
| 32 |
function mlsimport_live_prepared_listings( $payload, $args ): array { |
| 33 |
// Gate off: hand the stored query's payload straight through. |
| 34 |
if ( ! mlsimport_live_mode_active() ) { |
| 35 |
return (array) $payload; |
| 36 |
} |
| 37 |
|
| 38 |
// Live read: translate the render args into an MLS search. |
| 39 |
$result = mlsimport_live_search( (array) $args ); |
| 40 |
if ( ! is_array( $result ) ) { |
| 41 |
// MLS unreachable and no warm cache: an empty grid, never the stored rows. |
| 42 |
return mlsimport_live_payload( array(), 0 ); |
| 43 |
} |
| 44 |
|
| 45 |
// Build synthetic posts/rows from the live records + real total. |
| 46 |
return mlsimport_live_payload( $result['records'], (int) $result['total'] ); |
| 47 |
} |
| 48 |
add_filter( 'mlsimport_prepared_listings', 'mlsimport_live_prepared_listings', 10, 2 ); |
| 49 |
|
| 50 |
/** |
| 51 |
* Build the {posts, rows, total} payload the render stack consumes from raw |
| 52 |
* RESO records — shared by the grid swap and the list-by-key selection. |
| 53 |
* |
| 54 |
* @param array $records Raw RESO records. |
| 55 |
* @param int $total The set's real total (may exceed count($records)). |
| 56 |
* @return array |
| 57 |
*/ |
| 58 |
function mlsimport_live_payload( array $records, int $total ): array { |
| 59 |
$posts = array(); |
| 60 |
$rows = array(); |
| 61 |
// One synthetic post + row per raw record, indexed off a reset counter. |
| 62 |
foreach ( array_values( $records ) as $i => $record ) { |
| 63 |
// Skip anything that isn't a record array. |
| 64 |
if ( ! is_array( $record ) ) { |
| 65 |
continue; |
| 66 |
} |
| 67 |
// Negative, sequential ID so get_post_meta()/has_post_thumbnail() on it |
| 68 |
// can never collide with a real post. |
| 69 |
$id = -( $i + 1 ); |
| 70 |
// Reso record -> flat-table-shaped row (carries _live for later rebuilds). |
| 71 |
$row = mlsimport_live_row_from_reso( $record ); |
| 72 |
|
| 73 |
// Back-reference the synthetic ID onto the row and key the row by it. |
| 74 |
$row->post_id = $id; |
| 75 |
$rows[ $id ] = $row; |
| 76 |
// A minimal WP_Post the render stack can treat like a real listing. |
| 77 |
$posts[] = new WP_Post( |
| 78 |
(object) array( |
| 79 |
'ID' => $id, |
| 80 |
'post_type' => 'mlsimport_property', |
| 81 |
'post_status' => 'publish', |
| 82 |
'post_title' => isset( $record['UnparsedAddress'] ) ? (string) $record['UnparsedAddress'] : $row->listing_key, |
| 83 |
) |
| 84 |
); |
| 85 |
} |
| 86 |
|
| 87 |
// The shape prepare()'s consumers expect: posts + rows keyed by ID + total. |
| 88 |
return array( |
| 89 |
'posts' => $posts, |
| 90 |
'rows' => $rows, |
| 91 |
'total' => $total, |
| 92 |
); |
| 93 |
} |
| 94 |
|