PluginProbe
MLSImport: IDX Plugin & MLS Plugin for Real Estate Listings / 7.2.1
MLSImport: IDX Plugin & MLS Plugin for Real Estate Listings v7.2.1
7.2.1 7.2 7.1.2 7.1.1 7.1 7.0.4 7.0.6 7.0.7 6.3.8 6.3.7 6.3.6 6.3.5 6.3.4 6.3.3 6.3.1 trunk 5.7.3 5.7.5 5.8.1 5.8.2 5.8.3 5.8.4 5.8.6 6.0.4 6.0.5 All 36 releases
← All changes | includes/live/live-source.php +28 -89 7.0.47.2.1 View file →
@@ -1,7 +1,7 @@
1 1 <?php
2 2 /**
3 - * Live mode: the read functions everything else calls.
3 + * Direct MLS access: the read functions everything else calls.
4 4 *
5 5 * Only mlsimport_live_search() and mlsimport_live_get() talk HTTP to the
6 6 * MLS; both go through the cache. Query building and response parsing stay
7 7 * in the pure modules.
@@ -64,21 +64,11 @@
64 64 if ( array() === $config ) {
65 65 return null;
66 66 }
67 67
68 - if ( 'bridge' === ( $config['type'] ?? '' ) ) {
69 - $query = '?ListingKey=' . rawurlencode( $listing_key );
70 - } else {
71 - $query = '?';
72 - if ( ! empty( $config['expand'] ) ) {
73 - $query .= '&$expand=' . $config['expand'];
74 - }
75 - $query .= '&$top=1&$filter=' . mlsimport_live_filter_list_segment(
76 - mlsimport_live_field_alias( 'ListingKey', $config ),
77 - array( $listing_key )
78 - );
79 - $query = preg_replace( '/ and $/', '', $query );
80 - }
68 + $type = isset( $config['type'] ) ? (string) $config['type'] : '';
69 + $provider = Mlsimport_Provider_Family::adapter( $type, $config['mls_id'] ?? 0 );
70 + $query = $provider->build_direct_get_query( $listing_key, $config );
81 71
82 72 // 'q' keys the config shape too (dialect, $expand): a provider-type or
83 73 // expand change refetches instead of serving the stale-shaped record.
84 74 $key = mlsimport_live_cache_key(
@@ -151,16 +141,11 @@
151 141 * @param array $config Per-MLS config.
152 142 * @return string
153 143 */
154 144 function mlsimport_live_endpoint( array $config ): string {
155 - $base = isset( $config['api_import_url'] ) ? (string) $config['api_import_url'] : '';
156 - if ( 'bridge' === ( $config['type'] ?? '' ) ) {
157 - $native = preg_replace( '#/OData/([^/?]+)/Property/?#i', '/$1/listings', $base );
158 - if ( is_string( $native ) && '' !== $native ) {
159 - return $native;
160 - }
161 - }
162 - return $base;
145 + $type = isset( $config['type'] ) ? (string) $config['type'] : '';
146 + $provider = Mlsimport_Provider_Family::adapter( $type, $config['mls_id'] ?? 0 );
147 + return $provider->direct_endpoint( $config );
163 148 }
164 149
165 150 /**
166 151 * One direct HTTP GET against the MLS import URL.
@@ -169,14 +154,16 @@
169 154 * @return array{records:array,total:int}|null Null on transport/auth/parse failure.
170 155 */
171 156 function mlsimport_live_request( string $query ) {
172 157 $config = mlsimport_live_config();
158 + $type = isset( $config['type'] ) ? (string) $config['type'] : '';
159 + $provider = Mlsimport_Provider_Family::adapter( $type, $config['mls_id'] ?? 0 );
173 160 $headers = mlsimport_live_auth_headers();
174 - if ( array() === $config || array() === $headers ) {
161 + if ( array() === $config || ! $provider->supports_direct_access() || array() === $headers ) {
175 162 return null;
176 163 }
177 164
178 - $base = mlsimport_live_endpoint( $config );
165 + $base = $provider->direct_endpoint( $config );
179 166 // The base URL may already carry a query part; keep exactly one '?'.
180 167 $url = false === strpos( $base, '?' )
181 168 ? $base . $query
182 169 : $base . '&' . ltrim( $query, '?&' );
@@ -191,77 +178,29 @@
191 178 'headers' => $headers,
192 179 )
193 180 );
194 181
195 - if ( is_wp_error( $response ) || 200 !== (int) wp_remote_retrieve_response_code( $response ) ) {
182 + if ( is_wp_error( $response ) ) {
183 + error_log( 'MLSImport Direct MLS request failed: request_failed' );
196 184 return null;
197 185 }
198 186
199 - $result = mlsimport_live_parse_response( (string) wp_remote_retrieve_body( $response ), $config );
200 -
201 - // BrightMLS media lives on a separate endpoint; attach it here, inside the
202 - // cached request path, so the cache stores records WITH their media.
203 - if ( is_array( $result ) && 'brightmls' === ( $config['type'] ?? '' ) && array() !== $result['records'] ) {
204 - $result['records'] = mlsimport_live_attach_brightmls_media( $result['records'], $config, $headers );
187 + $outcome = $provider->read_direct_response(
188 + (int) wp_remote_retrieve_response_code( $response ),
189 + (string) wp_remote_retrieve_body( $response )
190 + );
191 + if ( empty( $outcome['success'] ) ) {
192 + $code = isset( $outcome['error']['code'] ) ? $outcome['error']['code'] : 'request_failed';
193 + error_log( 'MLSImport Direct MLS request failed: ' . $code );
194 + return null;
205 195 }
206 196
207 - return $result;
208 -}
209 -
210 -/**
211 - * Attach BrightMedia rows as standard Media[] to records that lack media,
212 - * fetched from the separate media endpoint in chunks of 100 keys (the same
213 - * request the AWS media fetch runs). A failed media chunk degrades to
214 - * photo-less cards, never a failed search.
215 - *
216 - * @param array $records Raw RESO records from the listings response.
217 - * @param array $config Per-MLS config (api_media_url).
218 - * @param array $headers Auth headers of the listings request.
219 - * @return array The records, media attached where the endpoint had it.
220 - */
221 -function mlsimport_live_attach_brightmls_media( array $records, array $config, array $headers ): array {
222 - $keys = array();
223 - foreach ( $records as $record ) {
224 - if ( is_array( $record ) && empty( $record['Media'] ) && isset( $record['ListingKey'] ) ) {
225 - $keys[] = (string) $record['ListingKey'];
226 - }
197 + $outcome = $provider->enrich_direct_media( $outcome, $config, $headers );
198 + if ( ! empty( $outcome['warning']['code'] ) ) {
199 + error_log( 'MLSImport Direct MLS warning: ' . $outcome['warning']['code'] );
227 200 }
228 - if ( array() === $keys ) {
229 - return $records;
230 - }
231 201
232 - $base = isset( $config['api_media_url'] ) ? trim( (string) $config['api_media_url'] ) : '';
233 - if ( '' === $base ) {
234 - // The fixed BrightMedia endpoint — the AWS media fetch's default.
235 - $base = 'https://bright-reso.brightmls.com/RESO/OData/bright/BrightMedia';
236 - }
237 - // BrightMLS's OData server rejects a '/' immediately before '?'.
238 - $base = rtrim( $base, '/' );
239 -
240 - $map = array();
241 - foreach ( array_chunk( $keys, 100 ) as $chunk ) {
242 - $url = $base . mlsimport_live_brightmls_media_query( $chunk );
243 - $url = str_replace( array( ' ', "'" ), array( '%20', '%27' ), $url );
244 - $response = wp_remote_get(
245 - $url,
246 - array(
247 - 'timeout' => 20,
248 - 'headers' => $headers,
249 - )
250 - );
251 - if ( is_wp_error( $response ) || 200 !== (int) wp_remote_retrieve_response_code( $response ) ) {
252 - continue;
253 - }
254 - $data = json_decode( (string) wp_remote_retrieve_body( $response ), true );
255 - if ( is_array( $data ) && ! empty( $data['value'] ) && is_array( $data['value'] ) ) {
256 - $map += mlsimport_live_brightmls_media_map( $data['value'] );
257 - }
258 - }
259 -
260 - foreach ( $records as $i => $record ) {
261 - $key = is_array( $record ) && isset( $record['ListingKey'] ) ? (string) $record['ListingKey'] : '';
262 - if ( '' !== $key && isset( $map[ $key ] ) ) {
263 - $records[ $i ]['Media'] = $map[ $key ];
264 - }
265 - }
266 - return $records;
202 + return array(
203 + 'records' => $outcome['records'],
204 + 'total' => $outcome['total'],
205 + );
267 206 }