| @@ -1,7 +1,7 @@ | ||
| 1 | 1 | <?php |
| 2 | 2 | /** |
| 3 | - * Live mode: normalize a provider response body into { records, total }. | |
| 3 | + * Direct MLS access: normalize a provider response into { records, total }. | |
| 4 | 4 | * |
| 5 | 5 | * Pure functions — no WordPress, no network. Family 1 providers all answer |
| 6 | 6 | * with the standard OData envelope: @odata.count + value[]. Provider-family |
| 7 | 7 | * variants (Rapattoni, Realtor.ca) extend this file when they are ported. |
| @@ -16,12 +16,10 @@ | ||
| 16 | 16 | /** |
| 17 | 17 | * Parse a raw response body into records + total. |
| 18 | 18 | * |
| 19 | 19 | * @param string $body Raw HTTP response body. |
| 20 | - * @param array $config Per-MLS config (type, …) — kept as the family-dispatch | |
| 21 | - * seam; every current provider answers one of the two | |
| 22 | - * envelopes below (Rapattoni's PropertyPictures quirk is | |
| 23 | - * normalized later, in live-map-reso). | |
| 20 | + * @param array $config Per-MLS config retained for call compatibility. Provider | |
| 21 | + * adapters normalize any family-specific record fields. | |
| 24 | 22 | * @return array{records:array,total:int}|null Null when the body is not a |
| 25 | 23 | * usable listing payload (bad JSON or a provider error envelope) — |
| 26 | 24 | * callers treat null as "keep the warm cache". |
| 27 | 25 | */ |
| @@ -52,10 +50,14 @@ | ||
| 52 | 50 | 'total' => $total, |
| 53 | 51 | ); |
| 54 | 52 | } |
| 55 | 53 | |
| 56 | - // Generic RESO OData envelope: @odata.count + value[]. | |
| 57 | - $records = isset( $data['value'] ) && is_array( $data['value'] ) ? array_values( $data['value'] ) : array(); | |
| 54 | + // Generic RESO OData requires a real value[] member. An unrelated JSON | |
| 55 | + // object is an invalid response, not a successful zero-listing result. | |
| 56 | + if ( ! array_key_exists( 'value', $data ) || ! is_array( $data['value'] ) ) { | |
| 57 | + return null; | |
| 58 | + } | |
| 59 | + $records = array_values( $data['value'] ); | |
| 58 | 60 | // @odata.count when present, otherwise the page's own record count. |
| 59 | 61 | $total = isset( $data['@odata.count'] ) && is_numeric( $data['@odata.count'] ) |
| 60 | 62 | ? (int) $data['@odata.count'] |
| 61 | 63 | : count( $records ); |
| @@ -63,41 +65,8 @@ | ||
| 63 | 65 | return array( |
| 64 | 66 | 'records' => $records, |
| 65 | 67 | 'total' => $total, |
| 66 | 68 | ); |
| 67 | -} | |
| 68 | - | |
| 69 | -/** | |
| 70 | - * Group BrightMedia rows into standard RESO Media arrays, keyed by their | |
| 71 | - * listing's ResourceRecordKey — the shape mlsimport_live_media_urls() reads. | |
| 72 | - * HiRes URL preferred, plain MediaURL the fallback (as the AWS media fetch | |
| 73 | - * normalizes). | |
| 74 | - * | |
| 75 | - * @param array $rows BrightMedia value[] rows. | |
| 76 | - * @return array<string,array> ListingKey => Media[]. | |
| 77 | - */ | |
| 78 | -function mlsimport_live_brightmls_media_map( array $rows ): array { | |
| 79 | - $map = array(); | |
| 80 | - // Group every media row under its listing's ResourceRecordKey. | |
| 81 | - foreach ( $rows as $row ) { | |
| 82 | - // Skip rows we can't attribute to a listing. | |
| 83 | - if ( ! is_array( $row ) || ! isset( $row['ResourceRecordKey'] ) ) { | |
| 84 | - continue; | |
| 85 | - } | |
| 86 | - // Prefer the hi-res URL; fall back to the plain one; skip if neither. | |
| 87 | - $url = ! empty( $row['MediaURLHiRes'] ) ? $row['MediaURLHiRes'] : ( $row['MediaURL'] ?? '' ); | |
| 88 | - if ( '' === (string) $url ) { | |
| 89 | - continue; | |
| 90 | - } | |
| 91 | - // Append a standard RESO-Media-shaped entry under this listing's key. | |
| 92 | - $map[ (string) $row['ResourceRecordKey'] ][] = array( | |
| 93 | - 'MediaURL' => (string) $url, | |
| 94 | - 'Order' => $row['MediaDisplayOrder'] ?? null, | |
| 95 | - 'MediaCategory' => $row['MediaCategory'] ?? null, | |
| 96 | - 'MediaType' => $row['MediaType'] ?? null, | |
| 97 | - ); | |
| 98 | - } | |
| 99 | - return $map; | |
| 100 | 69 | } |
| 101 | 70 | |
| 102 | 71 | /** |
| 103 | 72 | * The SaaS /clusters answer as the map's { type: clusters } payload — the |