PluginProbe
MLSImport: IDX Plugin & MLS Plugin for Real Estate Listings / 7.1.1
MLSImport: IDX Plugin & MLS Plugin for Real Estate Listings v7.1.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
mlsimport / includes / live / live-search-options.php

live-search-options.php in MLSImport: IDX Plugin & MLS Plugin for Real Estate Listings 7.1.1, at includes/live/live-search-options.php

174 lines 6.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Live mode: search-form option lists and slider bounds from the MLS.
4 *
5 * The form, its field catalog and its controls are unchanged
6 * (Mlsimport_Page_Block_Search_Fields); only the option SOURCE switches. Each
7 * taxonomy-backed field draws its dropdown from its RESO field's enum in the
8 * saved MLS metadata (mlsimport_mls_metadata_mls_enums) — the full list, as
9 * the Import Task screen ships it. A field with no RESO enum renders empty
10 * and hides itself, exactly like an empty taxonomy in stored mode.
11 *
12 * @package Mlsimport
13 */
14
15 if ( ! defined( 'ABSPATH' ) ) {
16 exit;
17 }
18
19 /**
20 * Feed a search field's options from the MLS enums (seam #4). Hooked on the
21 * pre filter, BEFORE the taxonomy terms are queried — live mode never needs
22 * the local terms, so the DB work is skipped rather than discarded.
23 *
24 * @param array|null $pre Prior short-circuit value (null = query terms).
25 * @param array $def The field definition.
26 * @return array|null
27 */
28 function mlsimport_live_search_field_options( $pre, $def ) {
29 // Already answered, gate off, or no field def: leave term-querying to run.
30 if ( null !== $pre || ! mlsimport_live_mode_active() || ! is_array( $def ) ) {
31 return $pre;
32 }
33
34 // The same param => RESO field vocabulary the live query filters by, so a
35 // dropdown can only ever offer values the search will honour.
36 $lists = mlsimport_live_param_vocabulary()['lists'];
37 $key = isset( $def['key'] ) ? (string) $def['key'] : '';
38 // status maps to StandardStatus; other fields come from the vocabulary.
39 $field = 'status' === $key ? 'StandardStatus' : ( isset( $lists[ $key ] ) ? $lists[ $key ] : '' );
40 // No RESO field behind this control: render empty (field hides itself).
41 if ( '' === $field ) {
42 return array();
43 }
44
45 // Pull this field's enum from the saved MLS metadata.
46 $enums = mlsimport_live_property_enums();
47 if ( empty( $enums[ $field ] ) || ! is_array( $enums[ $field ] ) ) {
48 return array();
49 }
50
51 // value => label (falling back to the value itself when the label is blank).
52 $out = array();
53 foreach ( $enums[ $field ] as $value => $label ) {
54 $out[ (string) $value ] = is_string( $label ) && '' !== $label ? $label : (string) $value;
55 }
56 return $out;
57 }
58 add_filter( 'mlsimport_search_field_options_pre', 'mlsimport_live_search_field_options', 10, 2 );
59
60 /**
61 * Hide the keyword box in live mode: OData has no full-text search, so the
62 * field would silently do nothing (spec v1 limitation).
63 *
64 * @param array|null $fields Visible field keys; null means show every field.
65 * @return array|null
66 */
67 function mlsimport_live_hide_keyword_field( $fields ) {
68 // Gate off: leave the visible-field list untouched.
69 if ( ! mlsimport_live_mode_active() ) {
70 return $fields;
71 }
72 // null means "all fields": expand to the full default set first so the
73 // diff below has a concrete list to remove keywords from.
74 if ( null === $fields ) {
75 $fields = array_merge( array( 'keywords' ), Mlsimport_Page_Block_Search_Fields::catalog(), array( 'sort' ) );
76 }
77 // Drop the keyword field and re-index.
78 return array_values( array_diff( (array) $fields, array( 'keywords' ) ) );
79 }
80 add_filter( 'mlsimport_listings_visible_fields', 'mlsimport_live_hide_keyword_field' );
81
82 /**
83 * Hide the half-map draw-on-map tools in live mode: point-in-polygon search
84 * needs the local listings table (MLS OData has no portable geo.intersects),
85 * so a drawn shape could not be honoured.
86 *
87 * @param bool $enabled Whether the draw tools render.
88 * @return bool
89 */
90 function mlsimport_live_hide_draw_tools( $enabled ) {
91 // Force the draw tools off in live mode; otherwise honour the caller's value.
92 return mlsimport_live_mode_active() ? false : $enabled;
93 }
94 add_filter( 'mlsimport_half_map_draw_tools', 'mlsimport_live_hide_draw_tools' );
95
96 /**
97 * The saved MLS PropertyEnums map: RESO field => ( value => label ).
98 *
99 * The enum JSON runs to hundreds of KB, so the decode is memoized on the raw
100 * option value — a form full of dropdowns parses it once, while a mid-request
101 * option change (settings save, tests) still takes effect.
102 *
103 * @return array
104 */
105 function mlsimport_live_property_enums(): array {
106 // Memoize the decode on the raw option so a form of dropdowns parses once.
107 static $memo_raw = null;
108 static $memo = array();
109
110 // No saved enum blob: no options.
111 $raw = get_option( 'mlsimport_mls_metadata_mls_enums', '' );
112 if ( ! is_string( $raw ) || '' === $raw ) {
113 return array();
114 }
115 // Same raw string as last time: return the cached decode.
116 if ( $raw === $memo_raw ) {
117 return $memo;
118 }
119
120 // Decode and pull the PropertyEnums map; remember it against this raw value.
121 $decoded = json_decode( $raw, true );
122 $memo_raw = $raw;
123 $memo = isset( $decoded['global_array']['PropertyEnums'] ) && is_array( $decoded['global_array']['PropertyEnums'] )
124 ? $decoded['global_array']['PropertyEnums']
125 : array();
126
127 return $memo;
128 }
129
130 /**
131 * The price slider's ceiling from the MLS: the highest live list price,
132 * cached a day (the stored version reads MAX(price) from the local table).
133 *
134 * @param int $ceiling The stored-mode ceiling.
135 * @return int
136 */
137 function mlsimport_live_price_ceiling( $ceiling ) {
138 // Gate off: keep the stored-mode ceiling.
139 if ( ! mlsimport_live_mode_active() ) {
140 return $ceiling;
141 }
142
143 // Recompute only on a cold cache.
144 $cached = get_transient( 'mlsimport_live_price_ceiling' );
145 if ( false === $cached ) {
146 // One-record search sorted by price desc = the single most expensive listing.
147 $result = mlsimport_live_search(
148 array(
149 'orderby' => 'price',
150 'order' => 'DESC',
151 'limit' => 1,
152 'page' => 1,
153 )
154 );
155 // Pull that top listing's price via the reso-map row.
156 $top = null;
157 if ( is_array( $result ) && ! empty( $result['records'][0] ) && is_array( $result['records'][0] ) ) {
158 $top = mlsimport_live_row_from_reso( $result['records'][0] )->price;
159 }
160 if ( null === $top || (float) $top <= 0 ) {
161 // MLS unreachable or priceless top record: keep the stored ceiling,
162 // don't cache the failure.
163 return $ceiling;
164 }
165 // Round up to a whole currency unit and cache for a day.
166 $cached = (int) ceil( (float) $top );
167 set_transient( 'mlsimport_live_price_ceiling', $cached, DAY_IN_SECONDS );
168 }
169
170 // Never return below 1 (the slider needs a positive ceiling).
171 return max( 1, (int) $cached );
172 }
173 add_filter( 'mlsimport_search_price_ceiling', 'mlsimport_live_price_ceiling' );
174