PluginProbe
Property Hive / 2.3.0
Property Hive v2.3.0
2.3.0 2.2.6 2.2.5 2.2.4 2.2.3 2.2.2 1.4.46 1.4.47 1.4.48 1.4.49 1.4.5 1.4.50 1.4.51 1.4.52 1.4.53 1.4.54 1.4.55 1.4.56 1.4.57 1.4.58 1.4.59 1.4.6 1.4.60 1.4.61 1.4.62 All 260 releases
propertyhive / includes / class-ph-query.php

class-ph-query.php in Property Hive 2.3.0, at includes/class-ph-query.php

2,732 lines 119.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 // phpcs:set WordPress.Security.ValidatedSanitizedInput customSanitizingFunctions[] ph_clean
3 // ph_clean() recursively sanitizes text; presence, shape and unslashing checks remain separate.
4
5 /**
6 * Contains the query functions for PropertyHive which alter the front-end post queries and loops.
7 *
8 * @class PH_Query
9 * @version 1.0.0
10 * @package PropertyHive/Classes
11 * @category Class
12 * @author PropertyHive
13 */
14
15 if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
16
17 if ( ! class_exists( 'PH_Query' ) ) :
18
19 /**
20 * PH_Query Class
21 */
22 class PH_Query {
23
24 /** Keyword normalized by this request's meta-query builder, shared across query instances. */
25 private static $normalized_keyword = null;
26
27 /** Read a department slug for this query without changing the shared request. */
28 private function get_requested_department() {
29 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- A public search filter only; it does not authorize a write.
30 return isset( $_REQUEST['department'] ) && is_string( $_REQUEST['department'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['department'] ) ) : null;
31 }
32
33
34 /** @public array Query vars to add to wp */
35 public $query_vars = array();
36
37 /** @public array Unfiltered property ids (before layered nav etc) */
38 public $unfiltered_property_ids = array();
39
40 /** @public array Filtered property ids (after layered nav) */
41 public $filtered_property_ids = array();
42
43 /** @public array Filtered property ids (after layered nav, per taxonomy) */
44 public $filtered_property_ids_for_taxonomy = array();
45
46 /** @public array property IDs that match the layered nav + price filter */
47 public $post__in = array();
48
49 /** @public array The meta query for the page */
50 public $meta_query = '';
51
52 /** @public array The tax query for the page */
53 public $tax_query = '';
54
55 /** @public array Post IDs matching layered nav only */
56 public $layered_nav_post__in = array();
57
58 /** @public array Stores post IDs matching layered nav, so price filter can find max price in view */
59 public $layered_nav_property_ids = array();
60
61 /** @public array Stores post IDs matching layered nav, so price filter can find max price in view */
62 public $address_keyword_polygon_points = array();
63
64 /**
65 * Constructor for the query class. Hooks in methods.
66 *
67 * @access public
68 */
69 public function __construct() {
70
71 add_action( 'init', array( $this, 'layered_nav_init' ) );
72 add_action( 'init', array( $this, 'price_filter_init' ) );
73
74 if ( ! is_admin() ) {
75 add_action( 'init', array( $this, 'get_errors' ) );
76 add_filter( 'query_vars', array( $this, 'add_query_vars'), 0 );
77 add_action( 'parse_request', array( $this, 'parse_request'), 0 );
78 add_filter( 'pre_get_posts', array( $this, 'pre_get_posts' ) );
79 add_filter( 'the_posts', array( $this, 'the_posts' ), 11, 2 );
80 add_action( 'wp', array( $this, 'remove_property_query' ) );
81 add_action( 'wp', array( $this, 'remove_ordering_args' ) );
82 add_filter( 'posts_where', array( $this, 'commercial_display_where' ), 10, 2 );
83 add_filter( 'posts_where', array( $this, 'keyword_excerpt_where' ), 10, 2 );
84 add_action( 'pre_get_posts', array( $this, 'custom_order_properties_by_availability' ), 10, 2 );
85 }
86
87 $this->init_query_vars();
88 }
89
90 public function custom_order_properties_by_availability($query)
91 {
92 if ( is_admin() )
93 {
94 return;
95 }
96
97 if ( !$query->is_main_query() )
98 {
99 return;
100 }
101
102 if ( !is_post_type_archive('property') )
103 {
104 return;
105 }
106
107 if ( apply_filters( 'propertyhive_order_by_availability', false ) === false )
108 {
109 return;
110 }
111
112 $availability_order = get_option('propertyhive_taxonomy_terms_order_availability', array());
113
114 if ( empty($availability_order) )
115 {
116 return;
117 }
118
119 // Sanitize and prepare the order
120 $availability_order = explode("|", $availability_order);
121 $availability_order = array_map('intval', $availability_order);
122
123 // Modify the main query to join with term relationships and term taxonomy tables using custom aliases
124 add_filter('posts_join', function ($join, $query)
125 {
126 global $wpdb;
127
128 if ($query->is_main_query() && is_post_type_archive('property'))
129 {
130 $join .= " LEFT JOIN {$wpdb->term_relationships} AS avstr ON ({$wpdb->posts}.ID = avstr.object_id) ";
131 $join .= " LEFT JOIN {$wpdb->term_taxonomy} AS avstt ON (avstr.term_taxonomy_id = avstt.term_taxonomy_id) ";
132 }
133
134 return $join;
135 }, 10, 2);
136
137 // Add a custom ordering clause
138 add_filter('posts_orderby', function ($orderby, $query) use ($availability_order)
139 {
140 global $wpdb;
141
142 if ($query->is_main_query() && is_post_type_archive('property')) {
143 // Retrieve the original orderby clause
144 $original_orderby = $orderby ? $orderby : "{$wpdb->posts}.post_date DESC";
145
146 // Construct the custom order by clause
147 $order_by_custom = "FIELD(avstt.term_id, " . implode(',', $availability_order) . ")";
148
149 // Combine the custom order by with the original order by
150 $orderby_combined = "$order_by_custom, $original_orderby";
151
152 return $orderby_combined;
153 }
154
155 return $orderby;
156 }, 10, 2);
157 }
158
159 public function keyword_excerpt_where( $where, $query )
160 {
161 if ( ( is_array($query->get('post_type')) && in_array('property', $query->get('post_type')) ) || ( !is_array($query->get('post_type')) && $query->get('post_type') == 'property' ) )
162 {
163 global $wpdb;
164
165 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public keyword filter reads the request, sanitizes/SQL-escapes it, and contributes only to the current SQL WHERE clause. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
166 if ( isset($_REQUEST['keyword']) && is_string( $_REQUEST['keyword'] ) && $_REQUEST['keyword'] != '' )
167 {
168 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only search; reuse the already-unslashed value produced by keyword_meta_query when available.
169 $keyword = isset( self::$normalized_keyword ) && $_REQUEST['keyword'] === self::$normalized_keyword ? self::$normalized_keyword : sanitize_text_field( wp_unslash( $_REQUEST['keyword'] ) );
170 $ref_pos = strpos($where, '_features_concatenated');
171 if ( $ref_pos !== FALSE )
172 {
173 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public keyword filter reads the request, sanitizes/SQL-escapes it, and contributes only to the current SQL WHERE clause. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
174 $str_to_insert = " $wpdb->posts.post_excerpt LIKE '%" . esc_sql( $keyword ) . "%' OR ";
175 $where = substr_replace($where, $str_to_insert, $ref_pos - 18, 0);
176 }
177 }
178 }
179
180 return $where;
181 }
182
183 public function commercial_display_where( $where, $query )
184 {
185 if ( $query->get('post_type') == 'property' )
186 {
187 global $wpdb;
188
189 $commercial_display = get_option( 'propertyhive_commercial_display', '' );
190
191 switch ( $commercial_display )
192 {
193 case "top_level_only":
194 {
195 $where .= " AND $wpdb->posts.post_parent=0 ";
196 break;
197 }
198 case "top_level_only_but_units_when_filtered":
199 {
200 $unit_filter_parameters = apply_filters( 'propertyhive_unit_filter_parameters', array( 'minimum_floor_area', 'maximum_floor_area' ) );
201 $unit_filter_parameter_found = false;
202 foreach ( $unit_filter_parameters as $parameter )
203 {
204 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public commercial display filter reads a request flag and contributes only to the current SQL WHERE clause. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
205 if ( isset($_REQUEST[$parameter]) && ph_clean( wp_unslash( $_REQUEST[$parameter] ) ) != '' )
206 {
207 $unit_filter_parameter_found = true;
208 }
209 }
210 if ( !$unit_filter_parameter_found )
211 {
212 $where .= " AND $wpdb->posts.post_parent=0 ";
213 }
214 break;
215 }
216 default:
217 {
218 // do nothing
219 }
220 }
221 }
222
223 return $where;
224 }
225
226 /**
227 * Init query vars by loading options.
228 */
229 public function init_query_vars() {
230 // Query vars to add to WP
231 $this->query_vars = array(
232
233 );
234 }
235
236 /**
237 * Get any errors from querystring
238 */
239 public function get_errors() {
240 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public frontend reads ph_error to add a request-scoped notice; it does not write posts, options, user data, or other persistent state. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
241 if ( ! empty( $_GET['ph_error'] ) && ( $error = sanitize_text_field( wp_unslash( $_GET['ph_error'] ) ) ) && ! ph_has_notice( $error, 'error' ) )
242 ph_add_notice( $error, 'error' );
243 }
244
245 /**
246 * add_query_vars function.
247 *
248 * @access public
249 * @param array $vars
250 * @return array
251 */
252 public function add_query_vars( $vars ) {
253 foreach ( $this->query_vars as $key => $var )
254 $vars[] = $key;
255
256 return $vars;
257 }
258
259 /**
260 * Get query vars
261 * @return array()
262 */
263 public function get_query_vars() {
264 return $this->query_vars;
265 }
266
267 /**
268 * Parse the request and look for query vars - endpoints may not be supported
269 */
270 public function parse_request() {
271 global $wp;
272
273 // Map query vars to their keys, or get them if endpoints are not supported
274 foreach ( $this->query_vars as $key => $var ) {
275 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public frontend normalizes a URL query variable into the current WP request query_vars; this is request/query state only and has no persistent write. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
276 if ( isset( $_GET[ $var ] ) ) {
277 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public frontend normalizes a URL query variable into the current WP request query_vars; this is request/query state only and has no persistent write. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
278 $wp->query_vars[ $key ] = sanitize_text_field( wp_unslash( $_GET[ $var ] ) );
279 }
280
281 elseif ( isset( $wp->query_vars[ $var ] ) ) {
282 $wp->query_vars[ $key ] = $wp->query_vars[ $var ];
283 }
284 }
285 }
286
287 /**
288 * Hook into pre_get_posts to do the main property query
289 *
290 * @access public
291 * @param mixed $q query object
292 * @return void
293 */
294 public function pre_get_posts( $q ) {
295 // We only want to affect the main query
296 if ( ! $q->is_main_query() )
297 return;
298
299 // Fix for verbose page rules
300 if ( $GLOBALS['wp_rewrite']->use_verbose_page_rules && isset( $q->queried_object->ID ) && $q->queried_object->ID == ph_get_page_id( 'search_results' ) ) {
301 $q->set( 'post_type', 'property' );
302 $q->set( 'page', '' );
303 $q->set( 'pagename', '' );
304
305 // Fix conditional Functions
306 $q->is_archive = true;
307 $q->is_post_type_archive = true;
308 $q->is_singular = false;
309 $q->is_page = false;
310 }
311
312 // When orderby is set, WordPress shows posts. Get around that here.
313 if ( ($q->is_home() || $q->get( 'page_id' ) == get_option('page_on_front')) && 'page' == get_option('show_on_front') && get_option('page_on_front') == ph_get_page_id('search_results') )
314 {
315 $_query = wp_parse_args( $q->query );
316 if ( empty( $_query ) || ! array_diff( array_keys( $_query ), array( 'preview', 'page', 'paged', 'cpage', 'orderby' ) ) )
317 {
318
319 $q->is_page = true;
320 $q->is_home = false;
321 $q->set( 'page_id', (int) get_option('page_on_front') );
322 $q->set( 'post_type', 'property' );
323 }
324 }
325
326 // Special check for sites with the property search results on front page
327 if ( $q->is_page() && 'page' == get_option( 'show_on_front' ) && $q->get('page_id') == ph_get_page_id('search_results') ) {
328
329 // This is a front-page property listings
330 $q->set( 'post_type', 'property' );
331 $q->set( 'page_id', '' );
332 if ( isset( $q->query['paged'] ) )
333 $q->set( 'paged', $q->query['paged'] );
334
335 // Define a variable so we know this is the front page search results later on
336 // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedConstantFound -- Retain the existing frontend state constant for theme and extension compatibility.
337 define( 'SEARCH_RESULTS_IS_ON_FRONT', true );
338
339 // Get the actual WP page to avoid errors and let us use is_front_page()
340 // This is hacky but works. Awaiting http://core.trac.wordpress.org/ticket/21096
341 global $wp_post_types;
342
343 $search_results_page = get_post( ph_get_page_id('search_results') );
344 $q->is_page = true;
345
346 $wp_post_types['property']->ID = $search_results_page->ID;
347 $wp_post_types['property']->post_title = $search_results_page->post_title;
348 $wp_post_types['property']->post_name = $search_results_page->post_name;
349 $wp_post_types['property']->post_type = $search_results_page->post_type;
350 $wp_post_types['property']->ancestors = get_ancestors( $search_results_page->ID, $search_results_page->post_type );
351
352 // Fix conditional Functions like is_front_page
353 $q->is_singular = false;
354 $q->is_post_type_archive = true;
355 $q->is_archive = true;
356
357 // Fix WP SEO
358 if ( class_exists( 'WPSEO_Meta' ) ) {
359 add_filter( 'wpseo_metadesc', array( $this, 'wpseo_metadesc' ) );
360 add_filter( 'wpseo_metakey', array( $this, 'wpseo_metakey' ) );
361 }
362
363 } else {
364
365 // Only apply to property categories, the property post archive, the search results page, and property attribute taxonomies
366 if ( ! $q->is_post_type_archive( 'property' ) && ! $q->is_tax( get_object_taxonomies( 'property' ) ) )
367 return;
368
369 }
370
371 $this->property_query( $q );
372
373 if ( is_search() )
374 {
375 add_filter( 'posts_where', array( $this, 'search_post_excerpt' ) );
376 add_filter( 'wp', array( $this, 'remove_posts_where' ) );
377 }
378
379 add_filter( 'posts_where', array( $this, 'exclude_protected_properties' ) );
380
381 // We're on a property search page so queue the propertyhive_get_properties_in_view function
382 //add_action( 'wp', array( $this, 'get_properties_in_view' ), 2);
383
384 // And remove the pre_get_posts hook
385 $this->remove_property_query();
386 }
387
388 /**
389 * search_post_excerpt function.
390 *
391 * @access public
392 * @param string $where (default: '')
393 * @return string (modified where clause)
394 */
395 public function search_post_excerpt( $where = '' ) {
396 /*global $wp_the_query;
397
398 // If this is not a PH Query, do not modify the query
399 if ( empty( $wp_the_query->query_vars['ph_query'] ) || empty( $wp_the_query->query_vars['s'] ) )
400 return $where;
401
402 $where = preg_replace(
403 "/post_title\s+LIKE\s*(\'\%[^\%]+\%\')/",
404 "post_title LIKE $1) OR (post_excerpt LIKE $1", $where );*/
405
406 return $where;
407 }
408
409 /**
410 * Prevent password protected properties appearing in the loops
411 *
412 * @param string $where
413 * @return string
414 */
415 public function exclude_protected_properties( $where ) {
416 global $wpdb;
417 $where .= " AND {$wpdb->posts}.post_password = ''";
418 return $where;
419 }
420
421 /**
422 * wpseo_metadesc function.
423 * Hooked into wpseo_ hook already, so no need for function_exist
424 *
425 * @access public
426 * @return string
427 */
428 public function wpseo_metadesc() {
429 return WPSEO_Meta::get_value( 'metadesc', ph_get_page_id('search_results') );
430 }
431
432
433 /**
434 * wpseo_metakey function.
435 * Hooked into wpseo_ hook already, so no need for function_exist
436 *
437 * @access public
438 * @return string
439 */
440 public function wpseo_metakey() {
441 return WPSEO_Meta::get_value( 'metakey', ph_get_page_id('search_results') );
442 }
443
444
445 /**
446 * Hook into the_posts to do the main property query if needed - relevanssi compatibility
447 *
448 * @access public
449 * @param array $posts
450 * @param WP_Query|bool $query (default: false)
451 * @return array
452 */
453 public function the_posts( $posts, $query = false ) {
454
455 // Abort if there's no query
456 if ( ! $query )
457 return $posts;
458
459 // Abort if we're not filtering posts
460 if ( empty( $this->post__in ) )
461 return $posts;
462
463 // Abort if this query has already been done
464 if ( ! empty( $query->ph_query ) )
465 return $posts;
466
467 // Abort if this isn't a search query
468 if ( empty( $query->query_vars["s"] ) )
469 return $posts;
470
471 // Abort if we're not on a post type archive/property taxonomy
472 if ( ! $query->is_post_type_archive( 'property' ) && ! $query->is_tax( get_object_taxonomies( 'property' ) ) )
473 return $posts;
474
475 $filtered_posts = array();
476 $queried_post_ids = array();
477
478 foreach ( $posts as $post ) {
479 if ( in_array( $post->ID, $this->post__in ) ) {
480 $filtered_posts[] = $post;
481 $queried_post_ids[] = $post->ID;
482 }
483 }
484
485 $query->posts = $filtered_posts;
486 $query->post_count = count( $filtered_posts );
487
488 // Ensure filters are set
489 $this->unfiltered_property_ids = $queried_post_ids;
490 $this->filtered_property_ids = $queried_post_ids;
491
492 if ( sizeof( $this->layered_nav_post__in ) > 0 ) {
493 $this->layered_nav_property_ids = array_intersect( $this->unfiltered_property_ids, $this->layered_nav_post__in );
494 } else {
495 $this->layered_nav_property_ids = $this->unfiltered_property_ids;
496 }
497
498 return $filtered_posts;
499 }
500
501
502 /**
503 * Query the properties, applying sorting/ordering etc. This applies to the main wordpress loop
504 *
505 * @access public
506 * @param mixed $q
507 * @return void
508 */
509 public function property_query( $q ) {
510
511 // Meta query
512 $meta_query = $this->get_meta_query( $q );
513
514 // Tax query
515 $tax_query = $this->get_tax_query( $q->get( 'tax_query' ) );
516
517 // Date query
518 $date_query = $this->get_date_query();
519
520 // Ordering
521 $ordering = $this->get_search_results_ordering_args();
522
523 // Get a list of post id's which match the current filters set (in the layered nav and price filter)
524 $post__in = array();
525 //$post__in = array_unique( apply_filters( 'loop_shop_post_in', array() ) );
526
527 // Ordering query vars
528 $q->set( 'orderby', $ordering['orderby'] . ' post_title' );
529 $q->set( 'order', $ordering['order'] );
530 if ( isset( $ordering['meta_key'] ) )
531 $q->set( 'meta_key', $ordering['meta_key'] );
532
533 // Query vars that affect posts shown
534 $q->set( 'meta_query', $meta_query );
535 $q->set( 'tax_query', $tax_query );
536 $q->set( 'date_query', $date_query );
537 $q->set( 'post__in', $post__in );
538 // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- Existing public Property Hive extension hook loop_search_results_per_page; changing the established name would detach installed callbacks.
539 $q->set( 'posts_per_page', $q->get( 'posts_per_page' ) ? $q->get( 'posts_per_page' ) : apply_filters( 'loop_search_results_per_page', get_option( 'posts_per_page' ) ) );
540
541 // Set a special variable
542 $q->set( 'ph_query', true );
543
544 // Store variables
545 $this->post__in = $post__in;
546 $this->meta_query = $meta_query;
547 $this->tax_query = $tax_query;
548
549 do_action( 'propertyhive_property_query', $q, $this );
550 }
551
552
553 /**
554 * Remove the query
555 *
556 * @access public
557 * @return void
558 */
559 public function remove_property_query() {
560 remove_filter( 'pre_get_posts', array( $this, 'pre_get_posts' ) );
561 }
562
563 /**
564 * Remove ordering queries
565 */
566 public function remove_ordering_args() {
567
568 }
569
570 /**
571 * Remove the posts_where filter
572 *
573 * @access public
574 * @return void
575 */
576 public function remove_posts_where() {
577 remove_filter( 'posts_where', array( $this, 'search_post_excerpt' ) );
578 }
579
580
581 /**
582 * Get an unpaginated list all property ID's (both filtered and unfiltered). Makes use of transients.
583 *
584 * @access public
585 * @return void
586 */
587 public function get_properties_in_view() {
588 global $wp_the_query;
589
590 $unfiltered_property_ids = array();
591
592 // Get main query
593 $current_wp_query = $wp_the_query->query;
594
595 // Get WP Query for current page (without 'paged')
596 unset( $current_wp_query['paged'] );
597
598 // Generate a transient name based on current query
599 $transient_name = 'ph_uf_pid_' . md5( http_build_query( $current_wp_query ) );
600 $transient_name = ( is_search() ) ? $transient_name . '_s' : $transient_name;
601
602 if ( false === ( $unfiltered_property_ids = get_transient( $transient_name ) ) ) {
603
604 // Get all visible posts, regardless of filters
605 $unfiltered_property_ids = get_posts(
606 array_merge(
607 $current_wp_query,
608 array(
609 'post_type' => 'property',
610 'numberposts' => -1,
611 'post_status' => 'publish',
612 // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query -- Cached property IDs must preserve the current search metadata predicates; only IDs are fetched, without totals or metadata/term cache priming.
613 'meta_query' => $this->meta_query,
614 'fields' => 'ids',
615 'no_found_rows' => true,
616 'update_post_meta_cache' => false,
617 'update_post_term_cache' => false
618 )
619 )
620 );
621
622 set_transient( $transient_name, $unfiltered_property_ids, YEAR_IN_SECONDS );
623 }
624
625 // Store the variable
626 $this->unfiltered_property_ids = $unfiltered_property_ids;
627
628 // Also store filtered posts ids...
629 if ( sizeof( $this->post__in ) > 0 )
630 $this->filtered_property_ids = array_intersect( $this->unfiltered_property_ids, $this->post__in );
631 else
632 $this->filtered_property_ids = $this->unfiltered_property_ids;
633
634 // And filtered post ids which just take layered nav into consideration (to find max price in the price widget)
635 if ( sizeof( $this->layered_nav_post__in ) > 0 )
636 $this->layered_nav_property_ids = array_intersect( $this->unfiltered_property_ids, $this->layered_nav_post__in );
637 else
638 $this->layered_nav_property_ids = $this->unfiltered_property_ids;
639 }
640
641
642 /**
643 * Returns an array of arguments for ordering properties based on the selected values
644 *
645 * @access public
646 * @return array
647 */
648 public function get_search_results_ordering_args( $orderby = '', $order = '' ) {
649 $request_department = $this->get_requested_department();
650
651 // Get ordering from query string unless defined
652 if ( ! $orderby ) {
653 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads ordering/filter inputs and returns ordering arguments for the current query only. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
654 $orderby_value = isset( $_GET['orderby'] ) ? sanitize_text_field( wp_unslash( $_GET['orderby'] ) ) : apply_filters( 'propertyhive_default_search_results_orderby', get_option( 'propertyhive_default_search_results_orderby' ) );
655
656 // Get order + orderby args from string
657 $orderby_value = explode( '-', $orderby_value );
658 $orderby = esc_attr( $orderby_value[0] );
659 $order = ! empty( $orderby_value[1] ) ? $orderby_value[1] : $order;
660 }
661
662 $orderby = strtolower( $orderby );
663 $order = strtoupper( $order );
664
665 $args = array();
666
667 // default - menu_order
668 if (
669 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads ordering/filter inputs and returns ordering arguments for the current query only. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
670 ( isset($request_department) && $request_department != 'commercial' && ph_get_custom_department_based_on($request_department) != 'commercial' ) ||
671 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads ordering/filter inputs and returns ordering arguments for the current query only. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
672 ( !isset($request_department) && get_option( 'propertyhive_primary_department' ) != 'commercial' && ph_get_custom_department_based_on(get_option( 'propertyhive_primary_department' )) != 'commercial' )
673 )
674 {
675 $args['orderby'] = 'meta_value_num';
676 $args['order'] = $order == 'ASC' ? 'ASC' : 'DESC';
677 // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key -- Search ordering maps supported price/floor-area/date choices to literal stored meta keys. switch cases and department branches set fixed keys; order direction is constrained to ASC/DESC.
678 $args['meta_key'] = '_price_actual';
679 }
680 elseif (
681 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads ordering/filter inputs and returns ordering arguments for the current query only. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
682 ( isset($request_department) && ( $request_department == 'commercial' || ph_get_custom_department_based_on($request_department) == 'commercial' ) ) ||
683 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads ordering/filter inputs and returns ordering arguments for the current query only. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
684 ( !isset($request_department) && ( get_option( 'propertyhive_primary_department' ) == 'commercial' || ph_get_custom_department_based_on(get_option( 'propertyhive_primary_department' )) == 'commercial' ) )
685 )
686 {
687 $args['orderby'] = 'meta_value_num';
688 $args['order'] = $order == 'ASC' ? 'ASC' : 'DESC';
689 // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key -- Search ordering maps supported price/floor-area/date choices to literal stored meta keys. switch cases and department branches set fixed keys; order direction is constrained to ASC/DESC.
690 $args['meta_key'] = '_floor_area_from_sqft';
691 }
692
693 switch ( $orderby ) {
694 case 'price' :
695 $args['orderby'] = 'meta_value_num';
696 $args['order'] = $order == 'ASC' ? 'ASC' : 'DESC';
697 if (
698 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads ordering/filter inputs and returns ordering arguments for the current query only. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
699 ( isset($request_department) && ( $request_department == 'commercial' || ph_get_custom_department_based_on($request_department) == 'commercial' ) ) ||
700 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads ordering/filter inputs and returns ordering arguments for the current query only. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
701 ( !isset($request_department) && ( get_option( 'propertyhive_primary_department' ) == 'commercial' || ph_get_custom_department_based_on(get_option( 'propertyhive_primary_department' )) == 'commercial' ) )
702 )
703 {
704 // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key -- Search ordering maps supported price/floor-area/date choices to literal stored meta keys. switch cases and department branches set fixed keys; order direction is constrained to ASC/DESC.
705 $args['meta_key'] = '_price_from_actual';
706 }
707 else
708 {
709 // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key -- Search ordering maps supported price/floor-area/date choices to literal stored meta keys. switch cases and department branches set fixed keys; order direction is constrained to ASC/DESC.
710 $args['meta_key'] = '_price_actual';
711 }
712 break;
713 case 'floor_area' :
714 $args['orderby'] = 'meta_value_num';
715 $args['order'] = $order == 'ASC' ? 'ASC' : 'DESC';
716 // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key -- Search ordering maps supported price/floor-area/date choices to literal stored meta keys. switch cases and department branches set fixed keys; order direction is constrained to ASC/DESC.
717 $args['meta_key'] = '_floor_area_from_sqft';
718 break;
719 case 'date' :
720 $args['orderby'] = 'meta_value';
721 $args['order'] = $order == 'ASC' ? 'ASC' : 'DESC';
722 // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key -- Search ordering maps supported price/floor-area/date choices to literal stored meta keys. switch cases and department branches set fixed keys; order direction is constrained to ASC/DESC.
723 $args['meta_key'] = '_on_market_change_date';
724 break;
725 default :
726 {
727 if ( $orderby != '' )
728 {
729 $args['orderby'] = $orderby;
730 $args['order'] = $order == 'ASC' ? 'ASC' : 'DESC';
731 }
732 }
733 }
734
735 return apply_filters( 'propertyhive_get_search_results_ordering_args', $args );
736 }
737
738 /**
739 * Appends date queries to an array.
740 * @access public
741 * @param array $meta_query
742 * @return array
743 */
744 public function get_date_query() {
745
746 $date_query = array();
747
748 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads a filter value and contributes query/meta/taxonomy arguments for the current request only; no persistent state-changing operation is reached. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
749 if ( isset( $_REQUEST['added_from'] ) && $_REQUEST['added_from'] != '' )
750 {
751 $date_query = array(
752 'column' => 'post_date_gmt',
753 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads a filter value and contributes query/meta/taxonomy arguments for the current request only; no persistent state-changing operation is reached. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
754 'after' => sanitize_text_field( wp_unslash( $_REQUEST['added_from'] ) )
755 );
756 }
757
758 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads a filter value and contributes query/meta/taxonomy arguments for the current request only; no persistent state-changing operation is reached. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
759 if ( isset( $_REQUEST['added_from_hours'] ) && $_REQUEST['added_from_hours'] != '' )
760 {
761 $date_query = array(
762 'column' => 'post_date_gmt',
763 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads a filter value and contributes query/meta/taxonomy arguments for the current request only; no persistent state-changing operation is reached. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
764 'after' => sanitize_text_field( wp_unslash( $_REQUEST['added_from_hours'] ) ) . ' hours ago'
765 );
766 }
767
768 return array_filter( $date_query );
769 }
770
771 /**
772 * Appends meta queries to an array.
773 * @access public
774 * @param array $meta_query
775 * @return array
776 */
777 public function get_meta_query( $q = array() ) {
778
779 $meta_query = array();
780 if ( !empty($q) )
781 {
782 $meta_query = $q->get( 'meta_query' );
783 }
784
785 if ( ! is_array( $meta_query ) )
786 $meta_query = array();
787
788 $meta_query[] = $this->on_market_meta_query();
789 $meta_query[] = $this->department_meta_query($q);
790 $meta_query[] = $this->featured_meta_query();
791 $meta_query[] = $this->date_added_meta_query();
792 $meta_query[] = $this->address_keyword_meta_query();
793 $meta_query[] = $this->country_meta_query();
794 $meta_query[] = $this->minimum_price_meta_query();
795 $meta_query[] = $this->maximum_price_meta_query();
796 $meta_query[] = $this->price_range_meta_query();
797 $meta_query[] = $this->minimum_rent_meta_query();
798 $meta_query[] = $this->maximum_rent_meta_query();
799 $meta_query[] = $this->rent_range_meta_query();
800 $meta_query[] = $this->bedrooms_meta_query();
801 $meta_query[] = $this->minimum_bedrooms_meta_query();
802 $meta_query[] = $this->maximum_bedrooms_meta_query();
803 $meta_query[] = $this->minimum_bathrooms_meta_query();
804 $meta_query[] = $this->maximum_bathrooms_meta_query();
805 $meta_query[] = $this->minimum_reception_rooms_meta_query();
806 $meta_query[] = $this->maximum_reception_rooms_meta_query();
807 $meta_query[] = $this->available_date_from_meta_query();
808 $meta_query[] = $this->minimum_floor_area_meta_query();
809 $meta_query[] = $this->maximum_floor_area_meta_query();
810 $meta_query[] = $this->minimum_maximum_floor_area_meta_query();
811 $meta_query[] = $this->floor_area_range_meta_query();
812 $meta_query[] = $this->commercial_for_sale_to_rent_meta_query();
813 $meta_query[] = $this->commercial_for_sale_meta_query();
814 $meta_query[] = $this->commercial_to_rent_meta_query();
815 $meta_query[] = $this->commercial_minimum_price_meta_query();
816 $meta_query[] = $this->commercial_maximum_price_meta_query();
817 $meta_query[] = $this->commercial_minimum_rent_meta_query();
818 $meta_query[] = $this->commercial_maximum_rent_meta_query();
819 $meta_query[] = $this->negotiator_meta_query();
820 $meta_query[] = $this->office_meta_query();
821 $meta_query[] = $this->keyword_meta_query();
822
823 return array_filter( apply_filters( 'propertyhive_property_query_meta_query', $meta_query, $this ) );
824 }
825
826 /**
827 * Returns a meta query to handle property on market status
828 *
829 * @access public
830 * @param string $compare (default: 'IN')
831 * @return array
832 */
833 public function on_market_meta_query( ) {
834
835 $meta_query = array();
836
837 if ( !is_admin() )
838 {
839 $meta_query = array(
840 'key' => '_on_market',
841 'value' => 'yes',
842 'compare' => '='
843 );
844 }
845
846 return $meta_query;
847 }
848
849 /**
850 * Returns a meta query to handle property department
851 *
852 * @access public
853 * @return array
854 */
855 public function department_meta_query( $q ) {
856 $request_department = $this->get_requested_department();
857
858
859 $meta_query = array();
860
861 if ( isset( $request_department ) && $request_department != '' )
862 {
863 $meta_query = array(
864 'key' => '_department',
865 'value' => sanitize_text_field( $request_department ),
866 'compare' => '='
867 );
868 }
869 else
870 {
871 // Need a department set if on search results page
872 if (!empty($q) && $q->is_post_type_archive( 'property' ))
873 {
874 if (get_option( 'propertyhive_primary_department' ) != '')
875 {
876 $department = get_option( 'propertyhive_primary_department' );
877 }
878 else
879 {
880 // No primary department set. Use first active one. Should never get to this scenario
881 $department = '';
882
883 $departments = ph_get_departments();
884
885 foreach ( $departments as $key => $value )
886 {
887 if ( get_option( 'propertyhive_active_departments_' . str_replace("residential-", "", $key) ) == 'yes' )
888 {
889 if ( $department == '' )
890 {
891 $department = $key;
892 }
893 }
894 }
895 }
896 $meta_query = array(
897 'key' => '_department',
898 'value' => $department,
899 'compare' => '='
900 );
901 }
902 }
903
904 return $meta_query;
905 }
906
907 /**
908 * Returns a meta query to handle featured
909 *
910 * @access public
911 * @return array
912 */
913 public function featured_meta_query( ) {
914
915 $meta_query = array();
916
917 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads a filter value and contributes query/meta/taxonomy arguments for the current request only; no persistent state-changing operation is reached. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
918 if ( isset( $_REQUEST['featured'] ) && $_REQUEST['featured'] != '' )
919 {
920 $meta_query = array(
921 'key' => '_featured',
922 'value' => 'yes',
923 'compare' => '='
924 );
925 }
926
927 return $meta_query;
928 }
929
930 /**
931 * Returns a meta query to handle date added
932 *
933 * @access public
934 * @return array
935 */
936 public function date_added_meta_query( ) {
937
938 $meta_query = array();
939
940 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads a filter value and contributes query/meta/taxonomy arguments for the current request only; no persistent state-changing operation is reached. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
941 if ( isset( $_REQUEST['date_added'] ) && $_REQUEST['date_added'] != '' && is_numeric($_REQUEST['date_added']) )
942 {
943 $meta_query = array(
944 'key' => '_on_market_change_date',
945 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads a filter value and contributes query/meta/taxonomy arguments for the current request only; no persistent state-changing operation is reached. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
946 'value' => gmdate('Y-m-d H:i:s', strtotime('-' . sanitize_text_field( wp_unslash( $_REQUEST['date_added'] ) ) . ' days')),
947 'compare' => '>=',
948 'type' => 'DATETIME',
949 );
950 }
951
952 return $meta_query;
953 }
954
955 /**
956 * Returns a meta query to handle searching for a keyword in the address
957 *
958 * @access public
959 * @return array
960 */
961 public function address_keyword_meta_query( ) {
962
963 $meta_query = array();
964
965 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Public read-only search control; no persistent state change.
966 if ( isset( $_REQUEST['address_keyword'] ) && !empty($_REQUEST['address_keyword']) )
967 {
968
969 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Public read-only address search; values are validated below before query construction.
970 $address_input = ph_clean( wp_unslash( $_REQUEST['address_keyword'] ) );
971 if ( ! is_string( $address_input ) && ! is_array( $address_input ) ) {
972 return $meta_query;
973 }
974 foreach ( (array) $address_input as $address_value ) {
975 if ( ! is_string( $address_value ) ) {
976 return $meta_query;
977 }
978 }
979 $address_input = ph_clean( $address_input );
980 // Preserve the normalized request value consumed by existing extensions.
981 $_REQUEST['address_keyword'] = $address_input;
982
983 $do_address_search = true;
984 if ( is_string( $address_input ) && get_option( 'propertyhive_address_keyword_compare', '=' ) == 'polygon' )
985 {
986 $address_keyword_polygon = new PH_Address_Keyword_Polygon();
987
988 $polygon_coordinates = $address_keyword_polygon->get_address_keyword_polygon_coordinates( $address_input . ', UK' );
989
990 if ( $polygon_coordinates !== FALSE )
991 {
992 $this->address_keyword_polygon_points = $polygon_coordinates;
993 add_filter( 'posts_where' , array( $this, 'where_properties_in_polygon' ), 1, 2 );
994 $do_address_search = false;
995 }
996 }
997
998 if ( $do_address_search )
999 {
1000
1001 $address_keywords_to_query = is_array($address_input) ? $address_input : array( $address_input );
1002
1003 $address_fields_to_query = array(
1004 '_reference_number',
1005 '_address_street',
1006 '_address_two',
1007 '_address_three',
1008 '_address_four',
1009 '_address_postcode',
1010 '_address_concatenated',
1011 );
1012
1013 $address_keywords = array();
1014
1015 if ( !empty($address_keywords_to_query) )
1016 {
1017 foreach ( $address_keywords_to_query as $address_keyword )
1018 {
1019 // Remove country code from end (i.e. ', UK')
1020 $address_keyword = preg_replace('/\,\s?[A-Z][A-Z]$/', '', $address_keyword);
1021
1022 // Extract postcode and use that if exists
1023 $postcode_pattern = '/\b([A-Z]{1,2}[0-9][0-9A-Z]? ?[0-9]?[A-Z]{0,2})\b/i';
1024 if ( preg_match($postcode_pattern, $address_keyword, $matches) )
1025 {
1026 $address_keyword = $matches[1];
1027 }
1028
1029 $address_keyword = trim($address_keyword);
1030
1031 $address_keywords[] = ph_clean($address_keyword);
1032
1033 if ( strpos( $address_keyword, ' ' ) !== FALSE )
1034 {
1035 $address_keywords[] = str_replace(" ", "-", ph_clean($address_keyword));
1036 }
1037 if ( strpos( $address_keyword, '-' ) !== FALSE )
1038 {
1039 $address_keywords[] = str_replace("-", " ", ph_clean($address_keyword));
1040 }
1041 if ( strpos( $address_keyword, '.' ) !== FALSE )
1042 {
1043 $address_keywords[] = str_replace(".", "", ph_clean($address_keyword));
1044 }
1045 if ( stripos( $address_keyword, 'st ' ) !== FALSE )
1046 {
1047 $address_keywords[] = str_ireplace("st ", "st. ", ph_clean($address_keyword));
1048 }
1049 if ( strpos( $address_keyword, '\'' ) !== FALSE )
1050 {
1051 $address_keywords[] = str_replace("'", "", ph_clean($address_keyword));
1052 }
1053 }
1054 }
1055
1056 $address_keywords = apply_filters( 'propertyhive_address_keywords_to_query', $address_keywords );
1057
1058 $meta_query = array('relation' => 'OR');
1059
1060 // add country to list of fields to query if it looks like we're working with an overseas site
1061 $countries = get_option( 'propertyhive_countries', array() );
1062 if ( !is_array($countries) ) { $countries = array(); }
1063 if ( count($countries) > 1 )
1064 {
1065 $address_fields_to_query[] = '_address_country';
1066 }
1067
1068 $address_fields_to_query = array_unique($address_fields_to_query);
1069 $address_fields_to_query = apply_filters( 'propertyhive_address_fields_to_query', $address_fields_to_query );
1070
1071 foreach ( $address_keywords as $address_keyword )
1072 {
1073 foreach ( $address_fields_to_query as $address_field )
1074 {
1075 if ( in_array( $address_field, array('_address_postcode', '_address_country', '_address_concatenated') ) ) { continue; } // ignore postcode and country as they're handled differently afterwards
1076
1077 $meta_query[] = array(
1078 'key' => $address_field,
1079 'value' => $address_keyword,
1080 'compare' => get_option( 'propertyhive_address_keyword_compare', '=' )
1081 );
1082 }
1083
1084 if ( in_array('_address_postcode', $address_fields_to_query) )
1085 {
1086 if ( strlen($address_keyword) <= 4 )
1087 {
1088 $meta_query[] = array(
1089 'key' => '_address_postcode',
1090 'value' => ph_clean($address_keyword),
1091 'compare' => '='
1092 );
1093 // Run regex match where given keyword is at the start of the postcode ^
1094 // followed by one or zero letters (for WC2E-style postcodes) [a-zA-Z]?
1095 // then a single space [ ]
1096 $meta_query[] = array(
1097 'key' => '_address_postcode',
1098 'value' => '^' . ph_clean($address_keyword) . '[a-zA-Z]?[ ]',
1099 'compare' => 'RLIKE'
1100 );
1101 }
1102 else
1103 {
1104 $postcode = ph_clean($address_keyword);
1105
1106 if ( preg_match('#^(GIR ?0AA|[A-PR-UWYZ]([0-9]{1,2}|([A-HK-Y][0-9]([0-9ABEHMNPRV-Y])?)|[0-9][A-HJKPS-UW])[0-9][ABD-HJLNP-UW-Z]{2})$#i', $postcode) )
1107 {
1108 // UK postcode found with no space
1109
1110 if ( strlen($postcode) == 5 )
1111 {
1112 $first_part = substr($postcode, 0, 2);
1113 $last_part = substr($postcode, 2, 3);
1114
1115 $postcode = $first_part . ' ' . $last_part;
1116 }
1117 elseif ( strlen($postcode) == 6 )
1118 {
1119 $first_part = substr($postcode, 0, 3);
1120 $last_part = substr($postcode, 3, 3);
1121
1122 $postcode = $first_part . ' ' . $last_part;
1123 }
1124 elseif ( strlen($postcode) == 7 )
1125 {
1126 $first_part = substr($postcode, 0, 4);
1127 $last_part = substr($postcode, 4, 3);
1128
1129 $postcode = $first_part . ' ' . $last_part;
1130 }
1131 }
1132
1133 $meta_query[] = array(
1134 'key' => '_address_postcode',
1135 'value' => ph_clean( $postcode ),
1136 'compare' => 'LIKE'
1137 );
1138 }
1139 }
1140
1141 if ( in_array('_address_country', $address_fields_to_query) )
1142 {
1143 $meta_query[] = array(
1144 'key' => '_address_country',
1145 'value' => $address_keyword,
1146 'compare' => '='
1147 );
1148
1149 // get country code for country entered
1150 $PH_Countries = new PH_Countries();
1151 $countries = $PH_Countries->countries;
1152 if ( is_array($countries) && !empty($countries) )
1153 {
1154 foreach ( $countries as $country_code => $country )
1155 {
1156 if ( strtolower($address_keyword) == strtolower($country['name']) )
1157 {
1158 $meta_query[] = array(
1159 'key' => '_address_country',
1160 'value' => $country_code,
1161 'compare' => '='
1162 );
1163 break;
1164 }
1165 }
1166 }
1167 }
1168
1169 if (
1170 !preg_match('/^(?:[A-Z]{2}\d|[A-Z]\d)/i', $address_keyword) &&
1171 in_array('_address_concatenated', $address_fields_to_query)
1172 )
1173 {
1174 $meta_query[] = array(
1175 'key' => '_address_concatenated',
1176 'value' => $address_keyword,
1177 'compare' => 'LIKE'
1178 );
1179 }
1180 }
1181
1182 }
1183 }
1184
1185 return $meta_query;
1186 }
1187
1188 public function where_properties_in_polygon( $where, $query )
1189 {
1190 global $wpdb;
1191
1192 if ( !empty($this->address_keyword_polygon_points) )
1193 {
1194 $where .= " AND
1195 ST_CONTAINS(
1196 ST_GEOMFROMTEXT('POLYGON((" . implode(", ", $this->address_keyword_polygon_points) . "))'),
1197 ST_GEOMFROMTEXT(
1198 CONCAT(
1199 'POINT(',
1200 COALESCE((SELECT meta_value FROM $wpdb->postmeta WHERE $wpdb->postmeta.meta_key='_latitude' AND $wpdb->postmeta.meta_value != '' AND $wpdb->postmeta.meta_value != 0 AND $wpdb->postmeta.post_id = $wpdb->posts.ID LIMIT 1), '0'),
1201 ' ',
1202 COALESCE((SELECT meta_value FROM $wpdb->postmeta WHERE $wpdb->postmeta.meta_key='_longitude' AND $wpdb->postmeta.meta_value != '' AND $wpdb->postmeta.meta_value != 0 AND $wpdb->postmeta.post_id = $wpdb->posts.ID LIMIT 1), '0'),
1203 ')'
1204 )
1205 )
1206 )";
1207 }
1208
1209 remove_filter( 'posts_where' , array( $this, 'where_properties_in_polygon' ), 1, 2 );
1210
1211 return $where;
1212 }
1213
1214 /**
1215 * Returns a meta query to handle country
1216 *
1217 * @access public
1218 * @return array
1219 */
1220 public function country_meta_query( ) {
1221
1222 $meta_query = array();
1223
1224 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads a filter value and contributes query/meta/taxonomy arguments for the current request only; no persistent state-changing operation is reached. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
1225 if ( isset( $_REQUEST['country'] ) && $_REQUEST['country'] != '' )
1226 {
1227 $meta_query = array(
1228 'key' => '_address_country',
1229 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads a filter value and contributes query/meta/taxonomy arguments for the current request only; no persistent state-changing operation is reached. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
1230 'value' => ph_clean( wp_unslash( $_REQUEST['country'] ) )
1231 );
1232 }
1233
1234 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads a filter value and contributes query/meta/taxonomy arguments for the current request only; no persistent state-changing operation is reached. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
1235 if ( isset( $_REQUEST['country_not'] ) && $_REQUEST['country_not'] != '' )
1236 {
1237 $meta_query = array(
1238 'key' => '_address_country',
1239 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads a filter value and contributes query/meta/taxonomy arguments for the current request only; no persistent state-changing operation is reached. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
1240 'value' => ph_clean( wp_unslash( $_REQUEST['country_not'] ) ),
1241 'compare' => '!='
1242 );
1243 }
1244
1245 return $meta_query;
1246 }
1247
1248 /**
1249 * Returns a meta query to handle minimum price
1250 *
1251 * @access public
1252 * @return array
1253 */
1254 public function minimum_price_meta_query( ) {
1255 $request_department = $this->get_requested_department();
1256
1257
1258 $meta_query = array();
1259
1260 if (
1261 isset( $request_department ) && ( $request_department == 'residential-sales' || ph_get_custom_department_based_on($request_department) == 'residential-sales' ) &&
1262 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads a filter value and contributes query/meta/taxonomy arguments for the current request only; no persistent state-changing operation is reached. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
1263 isset( $_REQUEST['minimum_price'] ) && $_REQUEST['minimum_price'] != ''
1264 )
1265 {
1266 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads a filter value and contributes query/meta/taxonomy arguments for the current request only; no persistent state-changing operation is reached. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
1267 $minimum_price = is_string( $_REQUEST['minimum_price'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['minimum_price'] ) ) : '';
1268
1269 if ( !is_numeric($minimum_price) )
1270 {
1271 return $meta_query;
1272 }
1273
1274 $search_form_currency = get_option( 'propertyhive_search_form_currency', 'GBP' );
1275 $search_form_currency = apply_filters( 'propertyhive_query_search_form_currency', $search_form_currency );
1276
1277 if ( $search_form_currency != 'GBP' )
1278 {
1279 // Convert $_REQUEST['minimum_price'] to GBP
1280 $ph_countries = new PH_Countries();
1281
1282 $minimum_price = $ph_countries->convert_price_to_gbp( $minimum_price, $search_form_currency );
1283 }
1284
1285 $meta_query = array(
1286 'key' => '_price_actual',
1287 'value' => ph_clean( floor( $minimum_price ) ),
1288 'compare' => '>=',
1289 'type' => 'NUMERIC'
1290 );
1291 }
1292
1293 return $meta_query;
1294 }
1295
1296 /**
1297 * Returns a meta query to handle maximum price
1298 *
1299 * @access public
1300 * @return array
1301 */
1302 public function maximum_price_meta_query( ) {
1303 $request_department = $this->get_requested_department();
1304
1305
1306 $meta_query = array();
1307
1308 if (
1309 isset( $request_department ) && ( $request_department == 'residential-sales' || ph_get_custom_department_based_on($request_department) == 'residential-sales' ) &&
1310 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads a filter value and contributes query/meta/taxonomy arguments for the current request only; no persistent state-changing operation is reached. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
1311 isset( $_REQUEST['maximum_price'] ) && $_REQUEST['maximum_price'] != ''
1312 )
1313 {
1314 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads a filter value and contributes query/meta/taxonomy arguments for the current request only; no persistent state-changing operation is reached. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
1315 $maximum_price = is_string( $_REQUEST['maximum_price'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['maximum_price'] ) ) : '';
1316
1317 if ( !is_numeric($maximum_price) )
1318 {
1319 return $meta_query;
1320 }
1321
1322 $search_form_currency = get_option( 'propertyhive_search_form_currency', 'GBP' );
1323 $search_form_currency = apply_filters( 'propertyhive_query_search_form_currency', $search_form_currency );
1324
1325 if ( $search_form_currency != 'GBP' )
1326 {
1327 // Convert $_REQUEST['maximum_price'] to GBP
1328 $ph_countries = new PH_Countries();
1329
1330 $maximum_price = $ph_countries->convert_price_to_gbp( $maximum_price, $search_form_currency );
1331 }
1332
1333 $meta_query = array(
1334 'key' => '_price_actual',
1335 'value' => ph_clean( ceil( $maximum_price ) ),
1336 'compare' => '<=',
1337 'type' => 'NUMERIC'
1338 );
1339 }
1340
1341 return $meta_query;
1342 }
1343
1344 /**
1345 * Returns a meta query to handle price range
1346 *
1347 * @access public
1348 * @return array
1349 */
1350 public function price_range_meta_query( ) {
1351 $request_department = $this->get_requested_department();
1352
1353
1354 $meta_query = array();
1355
1356 if (
1357 isset( $request_department ) && ( $request_department == 'residential-sales' || ph_get_custom_department_based_on($request_department) == 'residential-sales' ) &&
1358 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads a filter value and contributes query/meta/taxonomy arguments for the current request only; no persistent state-changing operation is reached. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
1359 isset( $_REQUEST['price_range'] ) && is_string( $_REQUEST['price_range'] ) && $_REQUEST['price_range'] != ''
1360 )
1361 {
1362 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads a filter value and contributes query/meta/taxonomy arguments for the current request only; no persistent state-changing operation is reached. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
1363 $explode_price_range = explode("-", ph_clean( wp_unslash( $_REQUEST['price_range'] ) ));
1364
1365 $search_form_currency = get_option( 'propertyhive_search_form_currency', 'GBP' );
1366 $search_form_currency = apply_filters( 'propertyhive_query_search_form_currency', $search_form_currency );
1367
1368 if ( isset($explode_price_range[0]) && $explode_price_range[0] != '' )
1369 {
1370 $minimum_price = $explode_price_range[0];
1371
1372 if ( !is_numeric($minimum_price) )
1373 {
1374 return $meta_query;
1375 }
1376
1377 if ( $search_form_currency != 'GBP' )
1378 {
1379 // Convert $explode_price_range[0] to GBP
1380 $ph_countries = new PH_Countries();
1381
1382 $minimum_price = $ph_countries->convert_price_to_gbp( $minimum_price, $search_form_currency );
1383 }
1384
1385 $meta_query[] = array(
1386 'key' => '_price_actual',
1387 'value' => sanitize_text_field( floor( $minimum_price ) ),
1388 'compare' => '>=',
1389 'type' => 'NUMERIC'
1390 );
1391 }
1392 if ( isset($explode_price_range[1]) && $explode_price_range[1] != '' )
1393 {
1394 $maximum_price = $explode_price_range[1];
1395
1396 if ( !is_numeric($maximum_price) )
1397 {
1398 return $meta_query;
1399 }
1400
1401 if ( $search_form_currency != 'GBP' )
1402 {
1403 // Convert $explode_price_range[1] to GBP
1404 $ph_countries = new PH_Countries();
1405
1406 $maximum_price = $ph_countries->convert_price_to_gbp( $maximum_price, $search_form_currency );
1407 }
1408
1409 $meta_query[] = array(
1410 'key' => '_price_actual',
1411 'value' => sanitize_text_field( ceil( $maximum_price ) ),
1412 'compare' => '<=',
1413 'type' => 'NUMERIC'
1414 );
1415 }
1416 }
1417
1418 return $meta_query;
1419 }
1420
1421 /**
1422 * Returns a meta query to handle minimum rent
1423 *
1424 * @access public
1425 * @return array
1426 */
1427 public function minimum_rent_meta_query( ) {
1428 $request_department = $this->get_requested_department();
1429
1430
1431 $meta_query = array();
1432
1433 if (
1434 isset( $request_department ) && ( $request_department == 'residential-lettings' || ph_get_custom_department_based_on($request_department) == 'residential-lettings' ) &&
1435 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads a filter value and contributes query/meta/taxonomy arguments for the current request only; no persistent state-changing operation is reached. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
1436 isset( $_REQUEST['minimum_rent'] ) && $_REQUEST['minimum_rent'] != ''
1437 )
1438 {
1439 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads a filter value and contributes query/meta/taxonomy arguments for the current request only; no persistent state-changing operation is reached. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
1440 $minimum_rent = is_string( $_REQUEST['minimum_rent'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['minimum_rent'] ) ) : '';
1441
1442 if ( !is_numeric($minimum_rent) )
1443 {
1444 return $meta_query;
1445 }
1446
1447 $search_form_currency = get_option( 'propertyhive_search_form_currency', 'GBP' );
1448 $search_form_currency = apply_filters( 'propertyhive_query_search_form_currency', $search_form_currency );
1449
1450 if ( $search_form_currency != 'GBP' )
1451 {
1452 // Convert $_REQUEST['minimum_rent'] to GBP
1453 $ph_countries = new PH_Countries();
1454
1455 $minimum_rent = $ph_countries->convert_price_to_gbp( $minimum_rent, $search_form_currency );
1456 }
1457
1458 $rent_frequency = apply_filters( 'propertyhive_search_form_rent_frequency', 'pcm' );
1459 switch ($rent_frequency)
1460 {
1461 case "pd": { $minimum_rent = ($minimum_rent * 365) / 12; break; }
1462 case "pw": { $minimum_rent = ($minimum_rent * 52) / 12; break; }
1463 case "pq": { $minimum_rent = ($minimum_rent * 4) / 12; break; }
1464 case "pa": { $minimum_rent = $minimum_rent / 12; break; }
1465 }
1466
1467 $meta_query = array(
1468 'key' => '_price_actual',
1469 'value' => ph_clean( floor( $minimum_rent ) ),
1470 'compare' => '>=',
1471 'type' => 'NUMERIC'
1472 );
1473 }
1474
1475 return $meta_query;
1476 }
1477
1478 /**
1479 * Returns a meta query to handle maximum rent
1480 *
1481 * @access public
1482 * @return array
1483 */
1484 public function maximum_rent_meta_query( ) {
1485 $request_department = $this->get_requested_department();
1486
1487
1488 $meta_query = array();
1489
1490 if (
1491 isset( $request_department ) && ( $request_department == 'residential-lettings' || ph_get_custom_department_based_on($request_department) == 'residential-lettings' ) &&
1492 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads a filter value and contributes query/meta/taxonomy arguments for the current request only; no persistent state-changing operation is reached. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
1493 isset( $_REQUEST['maximum_rent'] ) && $_REQUEST['maximum_rent'] != ''
1494 )
1495 {
1496 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads a filter value and contributes query/meta/taxonomy arguments for the current request only; no persistent state-changing operation is reached. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
1497 $maximum_rent = is_string( $_REQUEST['maximum_rent'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['maximum_rent'] ) ) : '';
1498
1499 if ( !is_numeric($maximum_rent) )
1500 {
1501 return $meta_query;
1502 }
1503
1504 $search_form_currency = get_option( 'propertyhive_search_form_currency', 'GBP' );
1505 $search_form_currency = apply_filters( 'propertyhive_query_search_form_currency', $search_form_currency );
1506
1507 if ( $search_form_currency != 'GBP' )
1508 {
1509 // Convert $_REQUEST['maximum_rent'] to GBP
1510 $ph_countries = new PH_Countries();
1511
1512 $maximum_rent = $ph_countries->convert_price_to_gbp( $maximum_rent, $search_form_currency );
1513 }
1514
1515 $rent_frequency = apply_filters( 'propertyhive_search_form_rent_frequency', 'pcm' );
1516 switch ($rent_frequency)
1517 {
1518 case "pd": { $maximum_rent = ($maximum_rent * 365) / 12; break; }
1519 case "pw": { $maximum_rent = ($maximum_rent * 52) / 12; break; }
1520 case "pq": { $maximum_rent = ($maximum_rent * 4) / 12; break; }
1521 case "pa": { $maximum_rent = $maximum_rent / 12; break; }
1522 }
1523
1524 $meta_query = array(
1525 'key' => '_price_actual',
1526 'value' => ph_clean( ceil( $maximum_rent ) ),
1527 'compare' => '<=',
1528 'type' => 'NUMERIC'
1529 );
1530 }
1531
1532 return $meta_query;
1533 }
1534
1535 /**
1536 * Returns a meta query to handle rent range
1537 *
1538 * @access public
1539 * @return array
1540 */
1541 public function rent_range_meta_query( ) {
1542 $request_department = $this->get_requested_department();
1543
1544
1545 $meta_query = array();
1546
1547 if (
1548 isset( $request_department ) && ( $request_department == 'residential-lettings' || ph_get_custom_department_based_on($request_department) == 'residential-lettings' ) &&
1549 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads a filter value and contributes query/meta/taxonomy arguments for the current request only; no persistent state-changing operation is reached. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
1550 isset( $_REQUEST['rent_range'] ) && is_string( $_REQUEST['rent_range'] ) && $_REQUEST['rent_range'] != ''
1551 )
1552 {
1553 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads a filter value and contributes query/meta/taxonomy arguments for the current request only; no persistent state-changing operation is reached. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
1554 $explode_rent_range = explode("-", ph_clean( wp_unslash( $_REQUEST['rent_range'] ) ));
1555
1556 $search_form_currency = get_option( 'propertyhive_search_form_currency', 'GBP' );
1557 $search_form_currency = apply_filters( 'propertyhive_query_search_form_currency', $search_form_currency );
1558
1559 $rent_frequency = apply_filters( 'propertyhive_search_form_rent_frequency', 'pcm' );
1560
1561 if ( isset($explode_rent_range[0]) && $explode_rent_range[0] != '' )
1562 {
1563 $minimum_rent = $explode_rent_range[0];
1564
1565 if ( !is_numeric($minimum_rent) )
1566 {
1567 return $meta_query;
1568 }
1569
1570 if ( $search_form_currency != 'GBP' )
1571 {
1572 // Convert $explode_rent_range[0] to GBP
1573 $ph_countries = new PH_Countries();
1574
1575 $minimum_rent = $ph_countries->convert_price_to_gbp( $minimum_rent, $search_form_currency );
1576 }
1577
1578 switch ($rent_frequency)
1579 {
1580 case "pd": { $minimum_rent = ($minimum_rent * 365) / 12; break; }
1581 case "pw": { $minimum_rent = ($minimum_rent * 52) / 12; break; }
1582 case "pq": { $minimum_rent = ($minimum_rent * 4) / 12; break; }
1583 case "pa": { $minimum_rent = $minimum_rent / 12; break; }
1584 }
1585
1586 $meta_query[] = array(
1587 'key' => '_price_actual',
1588 'value' => sanitize_text_field( floor( $minimum_rent ) ),
1589 'compare' => '>=',
1590 'type' => 'NUMERIC'
1591 );
1592 }
1593 if ( isset($explode_rent_range[1]) && $explode_rent_range[1] != '' )
1594 {
1595 $maximum_rent = $explode_rent_range[1];
1596
1597 if ( !is_numeric($maximum_rent) )
1598 {
1599 return $meta_query;
1600 }
1601
1602 if ( $search_form_currency != 'GBP' )
1603 {
1604 // Convert $explode_rent_range[1] to GBP
1605 $ph_countries = new PH_Countries();
1606
1607 $maximum_rent = $ph_countries->convert_price_to_gbp( $maximum_rent, $search_form_currency );
1608 }
1609
1610 switch ($rent_frequency)
1611 {
1612 case "pd": { $maximum_rent = ($maximum_rent * 365) / 12; break; }
1613 case "pw": { $maximum_rent = ($maximum_rent * 52) / 12; break; }
1614 case "pq": { $maximum_rent = ($maximum_rent * 4) / 12; break; }
1615 case "pa": { $maximum_rent = $maximum_rent / 12; break; }
1616 }
1617
1618 $meta_query[] = array(
1619 'key' => '_price_actual',
1620 'value' => sanitize_text_field( ceil( $maximum_rent ) ),
1621 'compare' => '<=',
1622 'type' => 'NUMERIC'
1623 );
1624 }
1625 }
1626
1627 return $meta_query;
1628 }
1629
1630 /**
1631 * Returns a meta query to handle exact bedrooms
1632 *
1633 * @access public
1634 * @return array
1635 */
1636 public function bedrooms_meta_query( ) {
1637 $request_department = $this->get_requested_department();
1638
1639
1640 $meta_query = array();
1641
1642 if (
1643 (
1644 (isset( $request_department ) && ( $request_department == 'residential-sales' || ph_get_custom_department_based_on($request_department) == 'residential-sales' )) ||
1645 (isset( $request_department ) && ( $request_department == 'residential-lettings' || ph_get_custom_department_based_on($request_department) == 'residential-lettings' ))
1646 ) &&
1647 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads a filter value and contributes query/meta/taxonomy arguments for the current request only; no persistent state-changing operation is reached. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
1648 isset( $_REQUEST['bedrooms'] ) && $_REQUEST['bedrooms'] != ''
1649 )
1650 {
1651 $meta_query = array(
1652 'key' => '_bedrooms',
1653 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads a filter value and contributes query/meta/taxonomy arguments for the current request only; no persistent state-changing operation is reached. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
1654 'value' => ph_clean( wp_unslash( $_REQUEST['bedrooms'] ) ),
1655 'compare' => '=',
1656 'type' => 'NUMERIC'
1657 );
1658 }
1659
1660 return $meta_query;
1661 }
1662
1663 /**
1664 * Returns a meta query to handle minimum bedrooms
1665 *
1666 * @access public
1667 * @return array
1668 */
1669 public function minimum_bedrooms_meta_query( ) {
1670 $request_department = $this->get_requested_department();
1671
1672
1673 $meta_query = array();
1674
1675 if (
1676 (
1677 (isset( $request_department ) && ( $request_department == 'residential-sales' || ph_get_custom_department_based_on($request_department) == 'residential-sales' )) ||
1678 (isset( $request_department ) && ( $request_department == 'residential-lettings' || ph_get_custom_department_based_on($request_department) == 'residential-lettings' ))
1679 ) &&
1680 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads a filter value and contributes query/meta/taxonomy arguments for the current request only; no persistent state-changing operation is reached. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
1681 isset( $_REQUEST['minimum_bedrooms'] ) && $_REQUEST['minimum_bedrooms'] != ''
1682 )
1683 {
1684 $meta_query = array(
1685 'key' => '_bedrooms',
1686 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads a filter value and contributes query/meta/taxonomy arguments for the current request only; no persistent state-changing operation is reached. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
1687 'value' => ph_clean( wp_unslash( $_REQUEST['minimum_bedrooms'] ) ),
1688 'compare' => '>=',
1689 'type' => 'NUMERIC'
1690 );
1691 }
1692
1693 return $meta_query;
1694 }
1695
1696 /**
1697 * Returns a meta query to handle maximum bedrooms
1698 *
1699 * @access public
1700 * @return array
1701 */
1702 public function maximum_bedrooms_meta_query( ) {
1703 $request_department = $this->get_requested_department();
1704
1705
1706 $meta_query = array();
1707
1708 if (
1709 (
1710 (isset( $request_department ) && ( $request_department == 'residential-sales' || ph_get_custom_department_based_on($request_department) == 'residential-sales' )) ||
1711 (isset( $request_department ) && ( $request_department == 'residential-lettings' || ph_get_custom_department_based_on($request_department) == 'residential-lettings' ))
1712 ) &&
1713 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads a filter value and contributes query/meta/taxonomy arguments for the current request only; no persistent state-changing operation is reached. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
1714 isset( $_REQUEST['maximum_bedrooms'] ) && $_REQUEST['maximum_bedrooms'] != ''
1715 )
1716 {
1717 $meta_query = array(
1718 'key' => '_bedrooms',
1719 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads a filter value and contributes query/meta/taxonomy arguments for the current request only; no persistent state-changing operation is reached. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
1720 'value' => ph_clean( wp_unslash( $_REQUEST['maximum_bedrooms'] ) ),
1721 'compare' => '<=',
1722 'type' => 'NUMERIC'
1723 );
1724 }
1725
1726 return $meta_query;
1727 }
1728
1729 /**
1730 * Returns a meta query to handle minimum bathrooms
1731 *
1732 * @access public
1733 * @return array
1734 */
1735 public function minimum_bathrooms_meta_query( ) {
1736 $request_department = $this->get_requested_department();
1737
1738
1739 $meta_query = array();
1740
1741 if (
1742 (
1743 (isset( $request_department ) && ( $request_department == 'residential-sales' || ph_get_custom_department_based_on($request_department) == 'residential-sales' )) ||
1744 (isset( $request_department ) && ( $request_department == 'residential-lettings' || ph_get_custom_department_based_on($request_department) == 'residential-lettings' ))
1745 ) &&
1746 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads a filter value and contributes query/meta/taxonomy arguments for the current request only; no persistent state-changing operation is reached. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
1747 isset( $_REQUEST['minimum_bathrooms'] ) && $_REQUEST['minimum_bathrooms'] != ''
1748 )
1749 {
1750 $meta_query = array(
1751 'key' => '_bathrooms',
1752 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads a filter value and contributes query/meta/taxonomy arguments for the current request only; no persistent state-changing operation is reached. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
1753 'value' => ph_clean( wp_unslash( $_REQUEST['minimum_bathrooms'] ) ),
1754 'compare' => '>=',
1755 'type' => 'NUMERIC'
1756 );
1757 }
1758
1759 return $meta_query;
1760 }
1761
1762 /**
1763 * Returns a meta query to handle maximum bathrooms
1764 *
1765 * @access public
1766 * @return array
1767 */
1768 public function maximum_bathrooms_meta_query( ) {
1769 $request_department = $this->get_requested_department();
1770
1771
1772 $meta_query = array();
1773
1774 if (
1775 (
1776 (isset( $request_department ) && ( $request_department == 'residential-sales' || ph_get_custom_department_based_on($request_department) == 'residential-sales' )) ||
1777 (isset( $request_department ) && ( $request_department == 'residential-lettings' || ph_get_custom_department_based_on($request_department) == 'residential-lettings' ))
1778 ) &&
1779 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads a filter value and contributes query/meta/taxonomy arguments for the current request only; no persistent state-changing operation is reached. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
1780 isset( $_REQUEST['maximum_bathrooms'] ) && $_REQUEST['maximum_bathrooms'] != ''
1781 )
1782 {
1783 $meta_query = array(
1784 'key' => '_bathrooms',
1785 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads a filter value and contributes query/meta/taxonomy arguments for the current request only; no persistent state-changing operation is reached. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
1786 'value' => ph_clean( wp_unslash( $_REQUEST['maximum_bathrooms'] ) ),
1787 'compare' => '<=',
1788 'type' => 'NUMERIC'
1789 );
1790 }
1791
1792 return $meta_query;
1793 }
1794
1795 /**
1796 * Returns a meta query to handle minimum reception rooms
1797 *
1798 * @access public
1799 * @return array
1800 */
1801 public function minimum_reception_rooms_meta_query( ) {
1802 $request_department = $this->get_requested_department();
1803
1804
1805 $meta_query = array();
1806
1807 if (
1808 (
1809 (isset( $request_department ) && ( $request_department == 'residential-sales' || ph_get_custom_department_based_on($request_department) == 'residential-sales' )) ||
1810 (isset( $request_department ) && ( $request_department == 'residential-lettings' || ph_get_custom_department_based_on($request_department) == 'residential-lettings' ))
1811 ) &&
1812 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads a filter value and contributes query/meta/taxonomy arguments for the current request only; no persistent state-changing operation is reached. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
1813 isset( $_REQUEST['minimum_reception_rooms'] ) && $_REQUEST['minimum_reception_rooms'] != ''
1814 )
1815 {
1816 $meta_query = array(
1817 'key' => '_reception_rooms',
1818 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads a filter value and contributes query/meta/taxonomy arguments for the current request only; no persistent state-changing operation is reached. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
1819 'value' => ph_clean( wp_unslash( $_REQUEST['minimum_reception_rooms'] ) ),
1820 'compare' => '>=',
1821 'type' => 'NUMERIC'
1822 );
1823 }
1824
1825 return $meta_query;
1826 }
1827
1828 /**
1829 * Returns a meta query to handle maximum reception rooms
1830 *
1831 * @access public
1832 * @return array
1833 */
1834 public function maximum_reception_rooms_meta_query( ) {
1835 $request_department = $this->get_requested_department();
1836
1837
1838 $meta_query = array();
1839
1840 if (
1841 (
1842 (isset( $request_department ) && ( $request_department == 'residential-sales' || ph_get_custom_department_based_on($request_department) == 'residential-sales' )) ||
1843 (isset( $request_department ) && ( $request_department == 'residential-lettings' || ph_get_custom_department_based_on($request_department) == 'residential-lettings' ))
1844 ) &&
1845 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads a filter value and contributes query/meta/taxonomy arguments for the current request only; no persistent state-changing operation is reached. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
1846 isset( $_REQUEST['maximum_reception_rooms'] ) && $_REQUEST['maximum_reception_rooms'] != ''
1847 )
1848 {
1849 $meta_query = array(
1850 'key' => '_reception_rooms',
1851 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads a filter value and contributes query/meta/taxonomy arguments for the current request only; no persistent state-changing operation is reached. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
1852 'value' => ph_clean( wp_unslash( $_REQUEST['maximum_reception_rooms'] ) ),
1853 'compare' => '<=',
1854 'type' => 'NUMERIC'
1855 );
1856 }
1857
1858 return $meta_query;
1859 }
1860
1861 /**
1862 * Returns a meta query to handle available date from
1863 *
1864 * @access public
1865 * @return array
1866 */
1867 public function available_date_from_meta_query( ) {
1868 $request_department = $this->get_requested_department();
1869
1870
1871 $meta_query = array();
1872
1873 if (
1874 isset( $request_department ) && ( $request_department == 'residential-lettings' || ph_get_custom_department_based_on($request_department) == 'residential-lettings' ) &&
1875 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads a filter value and contributes query/meta/taxonomy arguments for the current request only; no persistent state-changing operation is reached. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
1876 isset( $_REQUEST['available_date_from'] ) && is_string( $_REQUEST['available_date_from'] ) && $_REQUEST['available_date_from'] != ''
1877 )
1878 {
1879 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads a filter value and contributes query/meta/taxonomy arguments for the current request only; no persistent state-changing operation is reached. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
1880 $available_date = ph_clean( wp_unslash( $_REQUEST['available_date_from'] ) );
1881 if ( strpos($available_date, '/') !== FALSE )
1882 {
1883 // it's been provided in the format dd/mm/yyyy
1884 $explode_available_date = explode("/", $available_date);
1885 if ( count($explode_available_date) == 3 )
1886 {
1887 $available_date = $explode_available_date[0] . '-' . $explode_available_date[1] . '-' . $explode_available_date[2];
1888 }
1889 }
1890 $meta_query = array(
1891 'key' => '_available_date',
1892 'value' => ph_clean( $available_date ),
1893 'compare' => '<=',
1894 );
1895 }
1896
1897 return $meta_query;
1898 }
1899
1900 /**
1901 * Returns a meta query to handle minimum floor area
1902 *
1903 * @access public
1904 * @return array
1905 */
1906 public function minimum_floor_area_meta_query( ) {
1907 $request_department = $this->get_requested_department();
1908
1909
1910 $meta_query = array();
1911
1912 if (
1913 isset( $request_department ) && ( $request_department == 'commercial' || ph_get_custom_department_based_on($request_department) == 'commercial' ) &&
1914 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads a filter value and contributes query/meta/taxonomy arguments for the current request only; no persistent state-changing operation is reached. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
1915 isset( $_REQUEST['minimum_floor_area'] ) && $_REQUEST['minimum_floor_area'] != '' &&
1916 (
1917 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads a filter value and contributes query/meta/taxonomy arguments for the current request only; no persistent state-changing operation is reached. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
1918 !isset( $_REQUEST['maximum_floor_area'] ) ||
1919 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads a filter value and contributes query/meta/taxonomy arguments for the current request only; no persistent state-changing operation is reached. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
1920 ( isset( $_REQUEST['maximum_floor_area'] ) && $_REQUEST['maximum_floor_area'] == '' )
1921 )
1922 )
1923 {
1924 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads a filter value and contributes query/meta/taxonomy arguments for the current request only; no persistent state-changing operation is reached. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
1925 $value = ph_clean( wp_unslash( $_REQUEST['minimum_floor_area'] ) );
1926 if ( apply_filters('propertyhive_default_commercial_search_floor_area_unit', 'sqft') != 'sqft' )
1927 {
1928 // Convert value from square metres to square feet
1929 $value = $value * 10.76391041671;
1930 }
1931
1932 $meta_query = array(
1933 'key' => '_floor_area_to_sqft',
1934 'value' => $value,
1935 'compare' => '>=',
1936 'type' => 'NUMERIC'
1937 );
1938 }
1939
1940 return $meta_query;
1941 }
1942
1943 /**
1944 * Returns a meta query to handle maximum floor area
1945 *
1946 * @access public
1947 * @return array
1948 */
1949 public function maximum_floor_area_meta_query( ) {
1950 $request_department = $this->get_requested_department();
1951
1952
1953 $meta_query = array();
1954
1955 if (
1956 isset( $request_department ) && ( $request_department == 'commercial' || ph_get_custom_department_based_on($request_department) == 'commercial' ) &&
1957 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads a filter value and contributes query/meta/taxonomy arguments for the current request only; no persistent state-changing operation is reached. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
1958 isset( $_REQUEST['maximum_floor_area'] ) && $_REQUEST['maximum_floor_area'] != '' &&
1959 (
1960 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads a filter value and contributes query/meta/taxonomy arguments for the current request only; no persistent state-changing operation is reached. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
1961 !isset( $_REQUEST['minimum_floor_area'] ) ||
1962 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads a filter value and contributes query/meta/taxonomy arguments for the current request only; no persistent state-changing operation is reached. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
1963 ( isset( $_REQUEST['minimum_floor_area'] ) && $_REQUEST['minimum_floor_area'] == '' )
1964 )
1965 )
1966 {
1967 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads a filter value and contributes query/meta/taxonomy arguments for the current request only; no persistent state-changing operation is reached. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
1968 $value = ph_clean( wp_unslash( $_REQUEST['maximum_floor_area'] ) );
1969 if ( apply_filters('propertyhive_default_commercial_search_floor_area_unit', 'sqft') != 'sqft' )
1970 {
1971 // Convert value from square metres to square feet
1972 $value = $value * 10.76391041671;
1973 }
1974
1975 $meta_query = array(
1976 'key' => '_floor_area_from_sqft',
1977 'value' => $value,
1978 'compare' => '<=',
1979 'type' => 'NUMERIC'
1980 );
1981 }
1982
1983 return $meta_query;
1984 }
1985
1986 /**
1987 * Returns a meta query to handle minimum AND maximum floor area
1988 *
1989 * @access public
1990 * @return array
1991 */
1992 public function minimum_maximum_floor_area_meta_query( ) {
1993 $request_department = $this->get_requested_department();
1994
1995
1996 $meta_query = array();
1997
1998 if (
1999 isset( $request_department ) && ( $request_department == 'commercial' || ph_get_custom_department_based_on($request_department) == 'commercial' ) &&
2000 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads a filter value and contributes query/meta/taxonomy arguments for the current request only; no persistent state-changing operation is reached. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
2001 isset( $_REQUEST['minimum_floor_area'] ) && $_REQUEST['minimum_floor_area'] != '' &&
2002 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads a filter value and contributes query/meta/taxonomy arguments for the current request only; no persistent state-changing operation is reached. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
2003 isset( $_REQUEST['maximum_floor_area'] ) && $_REQUEST['maximum_floor_area'] != ''
2004 )
2005 {
2006 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads a filter value and contributes query/meta/taxonomy arguments for the current request only; no persistent state-changing operation is reached. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
2007 $maximum_floor_area = ph_clean( wp_unslash( $_REQUEST['maximum_floor_area'] ) );
2008 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads a filter value and contributes query/meta/taxonomy arguments for the current request only; no persistent state-changing operation is reached. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
2009 $minimum_floor_area = ph_clean( wp_unslash( $_REQUEST['minimum_floor_area'] ) );
2010 if ( apply_filters('propertyhive_default_commercial_search_floor_area_unit', 'sqft') != 'sqft' )
2011 {
2012 // Convert value from square metres to square feet
2013 $maximum_floor_area = $maximum_floor_area * 10.76391041671;
2014 $minimum_floor_area = $minimum_floor_area * 10.76391041671;
2015 }
2016
2017 $meta_query[] = array(
2018 'key' => '_floor_area_from_sqft',
2019 'value' => $maximum_floor_area,
2020 'compare' => '<=',
2021 'type' => 'NUMERIC'
2022 );
2023 $meta_query[] = array(
2024 'key' => '_floor_area_to_sqft',
2025 'value' => $minimum_floor_area,
2026 'compare' => '>=',
2027 'type' => 'NUMERIC'
2028 );
2029 }
2030
2031 return $meta_query;
2032 }
2033
2034
2035 /**
2036 * Returns a meta query to handle floor area range
2037 *
2038 * @access public
2039 * @return array
2040 */
2041 public function floor_area_range_meta_query( ) {
2042 $request_department = $this->get_requested_department();
2043
2044
2045 $meta_query = array();
2046
2047 if (
2048 isset( $request_department ) && ( $request_department == 'commercial' || ph_get_custom_department_based_on($request_department) == 'commercial' ) &&
2049 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads a filter value and contributes query/meta/taxonomy arguments for the current request only; no persistent state-changing operation is reached. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
2050 isset( $_REQUEST['floor_area_range'] ) && is_string( $_REQUEST['floor_area_range'] ) && $_REQUEST['floor_area_range'] != ''
2051 )
2052 {
2053 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads a filter value and contributes query/meta/taxonomy arguments for the current request only; no persistent state-changing operation is reached. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
2054 $explode_floor_area_range = explode("-", ph_clean( wp_unslash( $_REQUEST['floor_area_range'] ) ));
2055
2056 if ( isset($explode_floor_area_range[0]) && $explode_floor_area_range[0] != '' )
2057 {
2058 $meta_query = array(
2059 'key' => '_floor_area_from_sqft',
2060 'value' => ph_clean( $explode_floor_area_range[0] ),
2061 'compare' => '>=',
2062 'type' => 'NUMERIC'
2063 );
2064 }
2065 if ( isset($explode_floor_area_range[1]) && $explode_floor_area_range[1] != '' )
2066 {
2067 $meta_query = array(
2068 'key' => '_floor_area_to_sqft',
2069 'value' => ph_clean( $explode_floor_area_range[1] ),
2070 'compare' => '<=',
2071 'type' => 'NUMERIC'
2072 );
2073 }
2074 }
2075
2076 return $meta_query;
2077 }
2078
2079 /**
2080 * Returns a meta query to handle commercial for sale or to rent
2081 *
2082 * @access public
2083 * @return array
2084 */
2085 public function commercial_for_sale_to_rent_meta_query( ) {
2086 $request_department = $this->get_requested_department();
2087
2088
2089 $meta_query = array();
2090
2091 if (
2092 isset( $request_department ) && ( $request_department == 'commercial' || ph_get_custom_department_based_on($request_department) == 'commercial' ) &&
2093 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads a filter value and contributes query/meta/taxonomy arguments for the current request only; no persistent state-changing operation is reached. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
2094 isset( $_REQUEST['commercial_for_sale_to_rent'] ) && $_REQUEST['commercial_for_sale_to_rent'] == 'for_sale'
2095 )
2096 {
2097 $meta_query = array(
2098 'key' => '_for_sale',
2099 'value' => 'yes',
2100 'compare' => '=',
2101 );
2102 }
2103
2104 if (
2105 isset( $request_department ) && ( $request_department == 'commercial' || ph_get_custom_department_based_on($request_department) == 'commercial' ) &&
2106 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads a filter value and contributes query/meta/taxonomy arguments for the current request only; no persistent state-changing operation is reached. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
2107 isset( $_REQUEST['commercial_for_sale_to_rent'] ) && $_REQUEST['commercial_for_sale_to_rent'] == 'to_rent'
2108 )
2109 {
2110 $meta_query = array(
2111 'key' => '_to_rent',
2112 'value' => 'yes',
2113 'compare' => '=',
2114 );
2115 }
2116
2117 return $meta_query;
2118 }
2119
2120 /**
2121 * Returns a meta query to handle commercial for sale
2122 *
2123 * @access public
2124 * @return array
2125 */
2126 public function commercial_for_sale_meta_query( ) {
2127 $request_department = $this->get_requested_department();
2128
2129
2130 $meta_query = array();
2131
2132 if (
2133 isset( $request_department ) && ( $request_department == 'commercial' || ph_get_custom_department_based_on($request_department) == 'commercial' ) &&
2134 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads a filter value and contributes query/meta/taxonomy arguments for the current request only; no persistent state-changing operation is reached. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
2135 isset( $_REQUEST['commercial_for_sale'] ) && $_REQUEST['commercial_for_sale'] == '1'
2136 )
2137 {
2138 $meta_query = array(
2139 'key' => '_for_sale',
2140 'value' => 'yes',
2141 'compare' => '=',
2142 );
2143 }
2144
2145 return $meta_query;
2146 }
2147
2148 /**
2149 * Returns a meta query to handle commercial to rent
2150 *
2151 * @access public
2152 * @return array
2153 */
2154 public function commercial_to_rent_meta_query( ) {
2155 $request_department = $this->get_requested_department();
2156
2157
2158 $meta_query = array();
2159
2160 if (
2161 isset( $request_department ) && ( $request_department == 'commercial' || ph_get_custom_department_based_on($request_department) == 'commercial' ) &&
2162 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads a filter value and contributes query/meta/taxonomy arguments for the current request only; no persistent state-changing operation is reached. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
2163 isset( $_REQUEST['commercial_to_rent'] ) && $_REQUEST['commercial_to_rent'] == '1'
2164 )
2165 {
2166 $meta_query = array(
2167 'key' => '_to_rent',
2168 'value' => 'yes',
2169 'compare' => '=',
2170 );
2171 }
2172
2173 return $meta_query;
2174 }
2175
2176 /**
2177 * Returns a meta query to handle commercial minimum price
2178 *
2179 * @access public
2180 * @return array
2181 */
2182 public function commercial_minimum_price_meta_query( ) {
2183 $request_department = $this->get_requested_department();
2184
2185
2186 $meta_query = array();
2187
2188 if (
2189 isset( $request_department ) && ( $request_department == 'commercial' || ph_get_custom_department_based_on($request_department) == 'commercial' ) &&
2190 (
2191 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads a filter value and contributes query/meta/taxonomy arguments for the current request only; no persistent state-changing operation is reached. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
2192 ( isset( $_REQUEST['commercial_for_sale_to_rent'] ) && $_REQUEST['commercial_for_sale_to_rent'] == 'for_sale' )
2193 ||
2194 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads a filter value and contributes query/meta/taxonomy arguments for the current request only; no persistent state-changing operation is reached. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
2195 ( isset( $_REQUEST['commercial_for_sale'] ) && $_REQUEST['commercial_for_sale'] == '1' )
2196 ) &&
2197 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads a filter value and contributes query/meta/taxonomy arguments for the current request only; no persistent state-changing operation is reached. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
2198 isset( $_REQUEST['commercial_minimum_price'] ) && $_REQUEST['commercial_minimum_price'] != ''
2199 )
2200 {
2201 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads a filter value and contributes query/meta/taxonomy arguments for the current request only; no persistent state-changing operation is reached. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
2202 $minimum_price = is_string( $_REQUEST['commercial_minimum_price'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['commercial_minimum_price'] ) ) : '';
2203
2204 if ( !is_numeric($minimum_price) )
2205 {
2206 return $meta_query;
2207 }
2208
2209 $search_form_currency = get_option( 'propertyhive_search_form_currency', 'GBP' );
2210 $search_form_currency = apply_filters( 'propertyhive_query_search_form_currency', $search_form_currency );
2211
2212 if ( $search_form_currency != 'GBP' )
2213 {
2214 // Convert $_REQUEST['minimum_price'] to GBP
2215 $ph_countries = new PH_Countries();
2216
2217 $minimum_price = $ph_countries->convert_price_to_gbp( $minimum_price, $search_form_currency );
2218 }
2219
2220 $meta_query = array(
2221 'key' => '_price_to_actual',
2222 'value' => ph_clean( floor( $minimum_price ) ),
2223 'compare' => '>=',
2224 'type' => 'NUMERIC'
2225 );
2226 }
2227
2228 return $meta_query;
2229 }
2230
2231 /**
2232 * Returns a meta query to handle commercial maximum price
2233 *
2234 * @access public
2235 * @return array
2236 */
2237 public function commercial_maximum_price_meta_query( ) {
2238 $request_department = $this->get_requested_department();
2239
2240
2241 $meta_query = array();
2242
2243 if (
2244 isset( $request_department ) && ( $request_department == 'commercial' || ph_get_custom_department_based_on($request_department) == 'commercial' ) &&
2245 (
2246 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads a filter value and contributes query/meta/taxonomy arguments for the current request only; no persistent state-changing operation is reached. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
2247 ( isset( $_REQUEST['commercial_for_sale_to_rent'] ) && $_REQUEST['commercial_for_sale_to_rent'] == 'for_sale' )
2248 ||
2249 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads a filter value and contributes query/meta/taxonomy arguments for the current request only; no persistent state-changing operation is reached. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
2250 ( isset( $_REQUEST['commercial_for_sale'] ) && $_REQUEST['commercial_for_sale'] == '1' )
2251 ) &&
2252 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads a filter value and contributes query/meta/taxonomy arguments for the current request only; no persistent state-changing operation is reached. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
2253 isset( $_REQUEST['commercial_maximum_price'] ) && $_REQUEST['commercial_maximum_price'] != ''
2254 )
2255 {
2256 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads a filter value and contributes query/meta/taxonomy arguments for the current request only; no persistent state-changing operation is reached. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
2257 $maximum_price = is_string( $_REQUEST['commercial_maximum_price'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['commercial_maximum_price'] ) ) : '';
2258
2259 if ( !is_numeric($maximum_price) )
2260 {
2261 return $meta_query;
2262 }
2263
2264 $search_form_currency = get_option( 'propertyhive_search_form_currency', 'GBP' );
2265 $search_form_currency = apply_filters( 'propertyhive_query_search_form_currency', $search_form_currency );
2266
2267 if ( $search_form_currency != 'GBP' )
2268 {
2269 // Convert $_REQUEST['maximum_price'] to GBP
2270 $ph_countries = new PH_Countries();
2271
2272 $maximum_price = $ph_countries->convert_price_to_gbp( $maximum_price, $search_form_currency );
2273 }
2274
2275 $meta_query = array(
2276 'key' => '_price_from_actual',
2277 'value' => ph_clean( ceil( $maximum_price ) ),
2278 'compare' => '<=',
2279 'type' => 'NUMERIC'
2280 );
2281 }
2282
2283 return $meta_query;
2284 }
2285
2286 /**
2287 * Returns a meta query to handle commercial minimum rent
2288 *
2289 * @access public
2290 * @return array
2291 */
2292 public function commercial_minimum_rent_meta_query( ) {
2293 $request_department = $this->get_requested_department();
2294
2295
2296 $meta_query = array();
2297
2298 if (
2299 isset( $request_department ) && ( $request_department == 'commercial' || ph_get_custom_department_based_on($request_department) == 'commercial' ) &&
2300 (
2301 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads a filter value and contributes query/meta/taxonomy arguments for the current request only; no persistent state-changing operation is reached. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
2302 ( isset( $_REQUEST['commercial_for_sale_to_rent'] ) && $_REQUEST['commercial_for_sale_to_rent'] == 'to_rent' )
2303 ||
2304 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads a filter value and contributes query/meta/taxonomy arguments for the current request only; no persistent state-changing operation is reached. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
2305 ( isset( $_REQUEST['commercial_to_rent'] ) && $_REQUEST['commercial_to_rent'] == '1' )
2306 ) &&
2307 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads a filter value and contributes query/meta/taxonomy arguments for the current request only; no persistent state-changing operation is reached. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
2308 isset( $_REQUEST['commercial_minimum_rent'] ) && $_REQUEST['commercial_minimum_rent'] != ''
2309 )
2310 {
2311 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads a filter value and contributes query/meta/taxonomy arguments for the current request only; no persistent state-changing operation is reached. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
2312 $minimum_rent = is_string( $_REQUEST['commercial_minimum_rent'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['commercial_minimum_rent'] ) ) : '';
2313
2314 if ( !is_numeric($minimum_rent) )
2315 {
2316 return $meta_query;
2317 }
2318
2319 $search_form_currency = get_option( 'propertyhive_search_form_currency', 'GBP' );
2320 $search_form_currency = apply_filters( 'propertyhive_query_search_form_currency', $search_form_currency );
2321
2322 if ( $search_form_currency != 'GBP' )
2323 {
2324 // Convert $_REQUEST['minimum_rent'] to GBP
2325 $ph_countries = new PH_Countries();
2326
2327 $minimum_rent = $ph_countries->convert_price_to_gbp( $minimum_rent, $search_form_currency );
2328 }
2329
2330 $meta_query = array(
2331 'key' => '_rent_to_actual',
2332 'value' => ph_clean( floor( $minimum_rent ) ),
2333 'compare' => '>=',
2334 'type' => 'NUMERIC'
2335 );
2336 }
2337
2338 return $meta_query;
2339 }
2340
2341 /**
2342 * Returns a meta query to handle commercial maximum rent
2343 *
2344 * @access public
2345 * @return array
2346 */
2347 public function commercial_maximum_rent_meta_query( ) {
2348 $request_department = $this->get_requested_department();
2349
2350
2351 $meta_query = array();
2352
2353 if (
2354 isset( $request_department ) && ( $request_department == 'commercial' || ph_get_custom_department_based_on($request_department) == 'commercial' ) &&
2355 (
2356 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads a filter value and contributes query/meta/taxonomy arguments for the current request only; no persistent state-changing operation is reached. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
2357 ( isset( $_REQUEST['commercial_for_sale_to_rent'] ) && $_REQUEST['commercial_for_sale_to_rent'] == 'to_rent' )
2358 ||
2359 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads a filter value and contributes query/meta/taxonomy arguments for the current request only; no persistent state-changing operation is reached. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
2360 ( isset( $_REQUEST['commercial_to_rent'] ) && $_REQUEST['commercial_to_rent'] == '1' )
2361 ) &&
2362 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads a filter value and contributes query/meta/taxonomy arguments for the current request only; no persistent state-changing operation is reached. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
2363 isset( $_REQUEST['commercial_maximum_rent'] ) && $_REQUEST['commercial_maximum_rent'] != ''
2364 )
2365 {
2366 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads a filter value and contributes query/meta/taxonomy arguments for the current request only; no persistent state-changing operation is reached. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
2367 $maximum_rent = is_string( $_REQUEST['commercial_maximum_rent'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['commercial_maximum_rent'] ) ) : '';
2368
2369 if ( !is_numeric($maximum_rent) )
2370 {
2371 return $meta_query;
2372 }
2373
2374 $search_form_currency = get_option( 'propertyhive_search_form_currency', 'GBP' );
2375 $search_form_currency = apply_filters( 'propertyhive_query_search_form_currency', $search_form_currency );
2376
2377 if ( $search_form_currency != 'GBP' )
2378 {
2379 // Convert $_REQUEST['maximum_rent'] to GBP
2380 $ph_countries = new PH_Countries();
2381
2382 $maximum_rent = $ph_countries->convert_price_to_gbp( $maximum_rent, $search_form_currency );
2383 }
2384
2385 $meta_query = array(
2386 'key' => '_rent_from_actual',
2387 'value' => ph_clean( ceil( $maximum_rent ) ),
2388 'compare' => '<=',
2389 'type' => 'NUMERIC'
2390 );
2391 }
2392
2393 return $meta_query;
2394 }
2395
2396 /**
2397 * Returns a meta query to handle property negotiator
2398 *
2399 * @access public
2400 * @return array
2401 */
2402 public function negotiator_meta_query( ) {
2403
2404 $meta_query = array();
2405
2406 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads a filter value and contributes query/meta/taxonomy arguments for the current request only; no persistent state-changing operation is reached. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
2407 if ( isset( $_REQUEST['negotiator_id'] ) && $_REQUEST['negotiator_id'] != '' )
2408 {
2409 $meta_query = array(
2410 'key' => '_negotiator_id',
2411 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads a filter value and contributes query/meta/taxonomy arguments for the current request only; no persistent state-changing operation is reached. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
2412 'value' => (int)$_REQUEST['negotiator_id'],
2413 'compare' => '='
2414 );
2415 }
2416
2417 return $meta_query;
2418 }
2419
2420 /**
2421 * Returns a meta query to handle property office
2422 *
2423 * @access public
2424 * @param string $compare (default: 'IN')
2425 * @return array
2426 */
2427 public function office_meta_query( ) {
2428
2429 $meta_query = array();
2430
2431 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads a filter value and contributes query/meta/taxonomy arguments for the current request only; no persistent state-changing operation is reached. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
2432 if ( isset( $_REQUEST['officeID'] ) && $_REQUEST['officeID'] != '' )
2433 {
2434 $meta_query = array(
2435 'key' => '_office_id',
2436 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- The public property search reads a filter value and contributes query/meta/taxonomy arguments for the current request only; no persistent state-changing operation is reached. This exact annotation covers only NonceVerification.Recommended; retain all sanitizer and SQL-safety checks.
2437 'value' => ph_clean( wp_unslash( (array) $_REQUEST['officeID'] ) ),
2438 'compare' => 'IN'
2439 );
2440 }
2441
2442 return $meta_query;
2443 }
2444
2445 /**
2446 * Returns a meta query to handle searching for a keyword in the features and descriptions
2447 *
2448 * @access public
2449 * @return array
2450 */
2451 public function keyword_meta_query( ) {
2452
2453 $meta_query = array();
2454
2455 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Public read-only search input; query construction does not change persistent state.
2456 if ( isset( $_REQUEST['keyword'] ) && is_string( $_REQUEST['keyword'] ) && $_REQUEST['keyword'] != '' )
2457 {
2458
2459 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Public read-only search input is type checked above.
2460 $request_keyword = sanitize_text_field( wp_unslash( $_REQUEST['keyword'] ) );
2461
2462 // Remove country code from end (i.e. ', UK')
2463
2464 $request_keyword = preg_replace('/\,\s?[A-Z][A-Z]$/', '', $request_keyword);
2465
2466 // Extract postcode and use that if exists
2467 $postcode_pattern = '/\b([A-Z]{1,2}[0-9][0-9A-Z]? ?[0-9]?[A-Z]{0,2})\b/i';
2468
2469 if ( preg_match($postcode_pattern, $request_keyword, $matches) )
2470 {
2471 $request_keyword = $matches[1];
2472 }
2473
2474 $request_keyword = trim($request_keyword);
2475
2476 // Keep the normalized request value available to the existing excerpt query and extension filters.
2477 $_REQUEST['keyword'] = $request_keyword;
2478 self::$normalized_keyword = $request_keyword;
2479
2480 $keywords = array( $request_keyword );
2481
2482 if ( strpos( $request_keyword, ' ' ) !== FALSE )
2483 {
2484
2485 $keywords[] = str_replace(" ", "-", ph_clean($request_keyword));
2486 }
2487
2488 if ( strpos( $request_keyword, '-' ) !== FALSE )
2489 {
2490
2491 $keywords[] = str_replace("-", " ", ph_clean($request_keyword));
2492 }
2493
2494 if ( strpos( $request_keyword, '.' ) !== FALSE )
2495 {
2496
2497 $keywords[] = str_replace(".", "", ph_clean($request_keyword));
2498 }
2499
2500 if ( stripos( $request_keyword, 'st ' ) !== FALSE )
2501 {
2502
2503 $keywords[] = str_ireplace("st ", "st. ", ph_clean($request_keyword));
2504 }
2505
2506 if ( strpos( $request_keyword, '\'' ) !== FALSE )
2507 {
2508
2509 $keywords[] = str_replace("'", "", ph_clean($request_keyword));
2510 }
2511
2512 $meta_query = array( 'relation' => 'OR' );
2513
2514 $fields_to_query = array(
2515 '_features_concatenated',
2516 '_descriptions_concatenated',
2517 '_reference_number',
2518 '_address_street',
2519 '_address_two',
2520 '_address_three',
2521 '_address_four',
2522 '_address_postcode',
2523 );
2524
2525 $fields_to_query = apply_filters( 'propertyhive_keyword_fields_to_query', $fields_to_query );
2526
2527 foreach ( $keywords as $keyword )
2528 {
2529 foreach ( $fields_to_query as $field )
2530 {
2531 if ( $field == '_address_postcode' ) { continue; } // ignore postcode as that is handled differently afterwards
2532
2533 $meta_query[] = array(
2534 'key' => $field,
2535 'value' => $keyword,
2536 'compare' => 'LIKE'
2537 );
2538 }
2539 }
2540 if ( in_array('_address_postcode', $fields_to_query) )
2541 {
2542
2543 if ( strlen($request_keyword) <= 4 )
2544 {
2545 $meta_query[] = array(
2546 'key' => '_address_postcode',
2547
2548 'value' => ph_clean( $request_keyword ),
2549 'compare' => '='
2550 );
2551 // Run regex match where given keyword is at the start of the postcode ^
2552 // followed by one or zero letters (for WC2E-style postcodes) [a-zA-Z]?
2553 // then a single space [ ]
2554 $meta_query[] = array(
2555 'key' => '_address_postcode',
2556
2557 'value' => '^' . ph_clean( $request_keyword ) . '[a-zA-Z]?[ ]',
2558 'compare' => 'RLIKE'
2559 );
2560 }
2561 else
2562 {
2563
2564 $postcode = ph_clean( $request_keyword );
2565
2566 if ( preg_match('#^(GIR ?0AA|[A-PR-UWYZ]([0-9]{1,2}|([A-HK-Y][0-9]([0-9ABEHMNPRV-Y])?)|[0-9][A-HJKPS-UW])[0-9][ABD-HJLNP-UW-Z]{2})$#i', $postcode) )
2567 {
2568 // UK postcode found with no space
2569
2570 if ( strlen($postcode) == 5 )
2571 {
2572 $first_part = substr($postcode, 0, 2);
2573 $last_part = substr($postcode, 2, 3);
2574
2575 $postcode = $first_part . ' ' . $last_part;
2576 }
2577 elseif ( strlen($postcode) == 6 )
2578 {
2579 $first_part = substr($postcode, 0, 3);
2580 $last_part = substr($postcode, 3, 3);
2581
2582 $postcode = $first_part . ' ' . $last_part;
2583 }
2584 elseif ( strlen($postcode) == 7 )
2585 {
2586 $first_part = substr($postcode, 0, 4);
2587 $last_part = substr($postcode, 4, 3);
2588
2589 $postcode = $first_part . ' ' . $last_part;
2590 }
2591 }
2592
2593 $meta_query[] = array(
2594 'key' => '_address_postcode',
2595 'value' => ph_clean( $postcode ),
2596 'compare' => 'LIKE'
2597 );
2598 }
2599 }
2600 }
2601
2602 return $meta_query;
2603 }
2604
2605 /**
2606 * Appends taxonomy queries to an array.
2607 * @access public
2608 * @param array $tax_query
2609 * @return array
2610 */
2611 public function get_tax_query( $tax_query = array() ) {
2612 if ( ! is_array( $tax_query ) )
2613 $tax_query = array();
2614
2615 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Public read-only taxonomy search; this does not authorize a write.
2616 if ( isset($_REQUEST) && !empty($_REQUEST) )
2617 {
2618
2619 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Public read-only taxonomy search; each value is validated and sanitized below.
2620 foreach ( $_REQUEST as $key => $value )
2621 {
2622
2623 if ( taxonomy_exists($key) && !empty($value) && $this->taxonomy_allowed_for_department( $key ) )
2624 {
2625 $terms = (array) $value;
2626 foreach ( $terms as $term ) {
2627 if ( ! is_string( $term ) && ! is_int( $term ) ) {
2628 continue 2;
2629 }
2630 }
2631 $operator = $key == 'property_feature' ? 'AND' : 'IN';
2632
2633 $tax_query[] = array(
2634 'taxonomy' => $key,
2635 'terms' => ph_clean( wp_unslash( $terms ) ),
2636 'operator' => $operator,
2637 );
2638 }
2639 }
2640 }
2641
2642 return array_filter( apply_filters( 'propertyhive_property_query_tax_query', $tax_query, $this ) );
2643 }
2644
2645 private function taxonomy_allowed_for_department( $taxonomy )
2646 {
2647 $request_department = $this->get_requested_department();
2648
2649 if ( isset( $request_department ) && $request_department != '' )
2650 {
2651 $department = ph_clean($request_department);
2652 }
2653 else
2654 {
2655 $department = get_option( 'propertyhive_primary_department', 'residential-sales' );
2656 }
2657
2658 if ( ph_get_custom_department_based_on( $department ) !== false )
2659 {
2660 $department = ph_get_custom_department_based_on( $department );
2661 }
2662
2663 switch ( $department )
2664 {
2665 case 'residential-sales':
2666 {
2667 if ( in_array( $taxonomy, array('commercial_property_type', 'commercial_tenure', 'furnished') ) )
2668 {
2669 return false;
2670 }
2671 break;
2672 }
2673 case 'residential-lettings':
2674 {
2675 if ( in_array( $taxonomy, array('commercial_property_type', 'commercial_tenure', 'tenure', 'sale_by', 'price_qualifier') ) )
2676 {
2677 return false;
2678 }
2679 break;
2680 }
2681 case 'commercial':
2682 {
2683 if ( in_array( $taxonomy, array('property_type', 'tenure', 'parking', 'outside_space', 'furnished') ) )
2684 {
2685 return false;
2686 }
2687 break;
2688 }
2689 }
2690
2691
2692 return true;
2693 }
2694
2695 /**
2696 * Layered Nav Init
2697 */
2698 public function layered_nav_init( ) {
2699
2700 }
2701
2702 /**
2703 * Layered Nav post filter
2704 *
2705 * @param array $filtered_posts
2706 * @return array
2707 */
2708 public function layered_nav_query( $filtered_posts ) {
2709
2710 }
2711
2712 /**
2713 * Price filter Init
2714 */
2715 public function price_filter_init() {
2716
2717 }
2718
2719 /**
2720 * Price Filter post filter
2721 *
2722 * @param array $filtered_posts
2723 * @return array
2724 */
2725 public function price_filter( $filtered_posts ) {
2726
2727 }
2728
2729 }
2730
2731 endif;
2732