Completed
Pull Request — master (#11082)
by
unknown
16:48
created

WC_Query::pre_get_posts()   F

Complexity

Conditions 25
Paths 397

Size

Total Lines 103
Code Lines 56

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 25
eloc 56
c 2
b 0
f 0
nc 397
nop 1
dl 0
loc 103
rs 3.5466

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/**
3
 * Contains the query functions for WooCommerce which alter the front-end post queries and loops
4
 *
5
 * @class 		WC_Query
6
 * @version		2.6.0
7
 * @package		WooCommerce/Classes
8
 * @category	Class
9
 * @author 		WooThemes
10
 */
11
12
if ( ! defined( 'ABSPATH' ) ) {
13
	exit;
14
}
15
16
/**
17
 * WC_Query Class.
18
 */
19
class WC_Query {
20
21
	/** @public array Query vars to add to wp */
22
	public $query_vars = array();
23
24
	/**
25
	 * Stores chosen attributes
26
	 * @var array
27
	 */
28
	private static $_chosen_attributes;
29
30
	/**
31
	 * Constructor for the query class. Hooks in methods.
32
	 *
33
	 * @access public
34
	 */
35
	public function __construct() {
36
		add_action( 'init', array( $this, 'add_endpoints' ) );
37
		if ( ! is_admin() ) {
38
			add_action( 'wp_loaded', array( $this, 'get_errors' ), 20 );
39
			add_filter( 'query_vars', array( $this, 'add_query_vars'), 0 );
40
			add_action( 'parse_request', array( $this, 'parse_request'), 0 );
41
			add_action( 'pre_get_posts', array( $this, 'pre_get_posts' ) );
42
			add_action( 'wp', array( $this, 'remove_product_query' ) );
43
			add_action( 'wp', array( $this, 'remove_ordering_args' ) );
44
		}
45
		$this->init_query_vars();
46
	}
47
48
	/**
49
	 * Get any errors from querystring.
50
	 */
51
	public function get_errors() {
52
		if ( ! empty( $_GET['wc_error'] ) && ( $error = sanitize_text_field( $_GET['wc_error'] ) ) && ! wc_has_notice( $error, 'error' ) ) {
53
			wc_add_notice( $error, 'error' );
54
		}
55
	}
56
57
	/**
58
	 * Init query vars by loading options.
59
	 */
60
	public function init_query_vars() {
61
		// Query vars to add to WP.
62
		$this->query_vars = array(
63
			// Checkout actions.
64
			'order-pay'          => get_option( 'woocommerce_checkout_pay_endpoint', 'order-pay' ),
65
			'order-received'     => get_option( 'woocommerce_checkout_order_received_endpoint', 'order-received' ),
66
			// My account actions.
67
			'orders'                     => get_option( 'woocommerce_myaccount_orders_endpoint', 'orders' ),
68
			'view-order'                 => get_option( 'woocommerce_myaccount_view_order_endpoint', 'view-order' ),
69
			'downloads'                  => get_option( 'woocommerce_myaccount_downloads_endpoint', 'downloads' ),
70
			'edit-account'               => get_option( 'woocommerce_myaccount_edit_account_endpoint', 'edit-account' ),
71
			'edit-address'               => get_option( 'woocommerce_myaccount_edit_address_endpoint', 'edit-address' ),
72
			'payment-methods'            => get_option( 'woocommerce_myaccount_payment_methods_endpoint', 'payment-methods' ),
73
			'lost-password'              => get_option( 'woocommerce_myaccount_lost_password_endpoint', 'lost-password' ),
74
			'customer-logout'            => get_option( 'woocommerce_logout_endpoint', 'customer-logout' ),
75
			'add-payment-method'         => get_option( 'woocommerce_myaccount_add_payment_method_endpoint', 'add-payment-method' ),
76
			'delete-payment-method'      => get_option( 'woocommerce_myaccount_delete_payment_method_endpoint', 'delete-payment-method' ),
77
			'set-default-payment-method' => get_option( 'woocommerce_myaccount_set_default_payment_method_endpoint', 'set-default-payment-method' ),
78
		);
79
	}
80
81
	/**
82
	 * Get page title for an endpoint.
83
	 * @param  string
84
	 * @return string
85
	 */
86
	public function get_endpoint_title( $endpoint ) {
87
		global $wp;
88
89
		switch ( $endpoint ) {
90
			case 'order-pay' :
91
				$title = __( 'Pay for Order', 'woocommerce' );
92
			break;
93
			case 'order-received' :
94
				$title = __( 'Order Received', 'woocommerce' );
95
			break;
96
			case 'orders' :
97
				if ( ! empty( $wp->query_vars['orders'] ) ) {
98
					$title = sprintf( __( 'Orders (page %d)', 'woocommerce' ), intval( $wp->query_vars['orders'] ) );
99
				} else {
100
					$title = __( 'Orders', 'woocommerce' );
101
				}
102
			break;
103
			case 'view-order' :
104
				$order = wc_get_order( $wp->query_vars['view-order'] );
105
				$title = ( $order ) ? sprintf( __( 'Order #%s', 'woocommerce' ), $order->get_order_number() ) : '';
106
			break;
107
			case 'downloads' :
108
				$title = __( 'Downloads', 'woocommerce' );
109
			break;
110
			case 'edit-account' :
111
				$title = __( 'Account Details', 'woocommerce' );
112
			break;
113
			case 'edit-address' :
114
				$title = __( 'Addresses', 'woocommerce' );
115
			break;
116
			case 'payment-methods' :
117
				$title = __( 'Payment Methods', 'woocommerce' );
118
			break;
119
			case 'add-payment-method' :
120
				$title = __( 'Add Payment Method', 'woocommerce' );
121
			break;
122
			case 'lost-password' :
123
				$title = __( 'Lost Password', 'woocommerce' );
124
			break;
125
			default :
126
				$title = apply_filters( 'woocommerce_endpoint_' . $endpoint . '_title', '' );
127
			break;
128
		}
129
130
		return $title;
131
	}
132
133
	/**
134
	 * Endpoint mask describing the places the endpoint should be added.
135
	 *
136
	 * @since 2.6.2
137
	 * @return int
138
	 */
139
	protected function get_endpoints_mask() {
140
		if ( 'page' === get_option( 'show_on_front' ) ) {
141
			$page_on_front     = get_option( 'page_on_front' );
142
			$myaccount_page_id = get_option( 'woocommerce_myaccount_page_id' );
143
			$checkout_page_id  = get_option( 'woocommerce_checkout_page_id' );
144
145
			if ( in_array( $page_on_front, array( $myaccount_page_id, $checkout_page_id ) ) ) {
146
				return EP_ROOT | EP_PAGES;
147
			}
148
		}
149
150
		return EP_PAGES;
151
	}
152
153
	/**
154
	 * Add endpoints for query vars.
155
	 */
156
	public function add_endpoints() {
157
		$mask = $this->get_endpoints_mask();
158
159
		foreach ( $this->query_vars as $key => $var ) {
160
			if ( ! empty( $var ) ) {
161
				add_rewrite_endpoint( $var, $mask );
162
			}
163
		}
164
	}
165
166
	/**
167
	 * Add query vars.
168
	 *
169
	 * @access public
170
	 * @param array $vars
171
	 * @return array
172
	 */
173
	public function add_query_vars( $vars ) {
174
		foreach ( $this->query_vars as $key => $var ) {
175
			$vars[] = $key;
176
		}
177
		return $vars;
178
	}
179
180
	/**
181
	 * Get query vars.
182
	 *
183
	 * @return array
184
	 */
185
	public function get_query_vars() {
186
		return $this->query_vars;
187
	}
188
189
	/**
190
	 * Get query current active query var.
191
	 *
192
	 * @return string
193
	 */
194
	public function get_current_endpoint() {
195
		global $wp;
196
		foreach ( $this->get_query_vars() as $key => $value ) {
197
			if ( isset( $wp->query_vars[ $key ] ) ) {
198
				return $key;
199
			}
200
		}
201
		return '';
202
	}
203
204
	/**
205
	 * Parse the request and look for query vars - endpoints may not be supported.
206
	 */
207
	public function parse_request() {
208
		global $wp;
209
210
		// Map query vars to their keys, or get them if endpoints are not supported
211
		foreach ( $this->query_vars as $key => $var ) {
212
			if ( isset( $_GET[ $var ] ) ) {
213
				$wp->query_vars[ $key ] = $_GET[ $var ];
214
			}
215
216
			elseif ( isset( $wp->query_vars[ $var ] ) ) {
217
				$wp->query_vars[ $key ] = $wp->query_vars[ $var ];
218
			}
219
		}
220
	}
221
222
	/**
223
	 * Hook into pre_get_posts to do the main product query.
224
	 *
225
	 * @param mixed $q query object
226
	 */
227
	public function pre_get_posts( $q ) {
228
		// We only want to affect the main query
229
		if ( ! $q->is_main_query() ) {
230
			return;
231
		}
232
233
		// Fix for verbose page rules
234
		if ( $GLOBALS['wp_rewrite']->use_verbose_page_rules && isset( $q->queried_object->ID ) && $q->queried_object->ID === wc_get_page_id( 'shop' ) ) {
235
			$q->set( 'post_type', 'product' );
236
			$q->set( 'page', '' );
237
			$q->set( 'pagename', '' );
238
239
			// Fix conditional Functions
240
			$q->is_archive           = true;
241
			$q->is_post_type_archive = true;
242
			$q->is_singular          = false;
243
			$q->is_page              = false;
244
		}
245
246
		// Fix for endpoints on the homepage
247
		if ( $q->is_home() && 'page' === get_option( 'show_on_front' ) && absint( get_option( 'page_on_front' ) ) !== absint( $q->get( 'page_id' ) ) ) {
248
			$_query = wp_parse_args( $q->query );
249
			if ( ! empty( $_query ) && array_intersect( array_keys( $_query ), array_keys( $this->query_vars ) ) ) {
250
				$q->is_page     = true;
251
				$q->is_home     = false;
252
				$q->is_singular = true;
253
				$q->set( 'page_id', (int) get_option( 'page_on_front' ) );
254
				add_filter( 'redirect_canonical', '__return_false' );
255
			}
256
		}
257
258
		// When orderby is set, WordPress shows posts. Get around that here.
259
		if ( $q->is_home() && 'page' === get_option( 'show_on_front' ) && absint( get_option( 'page_on_front' ) ) === wc_get_page_id( 'shop' ) ) {
260
			$_query = wp_parse_args( $q->query );
261
			if ( empty( $_query ) || ! array_diff( array_keys( $_query ), array( 'preview', 'page', 'paged', 'cpage', 'orderby' ) ) ) {
262
				$q->is_page = true;
263
				$q->is_home = false;
264
				$q->set( 'page_id', (int) get_option( 'page_on_front' ) );
265
				$q->set( 'post_type', 'product' );
266
			}
267
		}
268
269
		// Fix product feeds
270
		if ( $q->is_feed() && $q->is_post_type_archive( 'product' ) ) {
271
			$q->is_comment_feed = false;
272
		}
273
274
		// Special check for shops with the product archive on front
275
		if ( $q->is_page() && 'page' === get_option( 'show_on_front' ) && absint( $q->get( 'page_id' ) ) === wc_get_page_id( 'shop' ) ) {
276
277
			// This is a front-page shop
278
			$q->set( 'post_type', 'product' );
279
			$q->set( 'page_id', '' );
280
281
			if ( isset( $q->query['paged'] ) ) {
282
				$q->set( 'paged', $q->query['paged'] );
283
			}
284
285
			// Define a variable so we know this is the front page shop later on
286
			define( 'SHOP_IS_ON_FRONT', true );
287
288
			// Get the actual WP page to avoid errors and let us use is_front_page()
289
			// This is hacky but works. Awaiting https://core.trac.wordpress.org/ticket/21096
290
			global $wp_post_types;
291
292
			$shop_page 	= get_post( wc_get_page_id( 'shop' ) );
293
294
			$wp_post_types['product']->ID 			= $shop_page->ID;
295
			$wp_post_types['product']->post_title 	= $shop_page->post_title;
296
			$wp_post_types['product']->post_name 	= $shop_page->post_name;
297
			$wp_post_types['product']->post_type    = $shop_page->post_type;
298
			$wp_post_types['product']->ancestors    = get_ancestors( $shop_page->ID, $shop_page->post_type );
299
300
			// Fix conditional Functions like is_front_page
301
			$q->is_singular          = false;
302
			$q->is_post_type_archive = true;
303
			$q->is_archive           = true;
304
			$q->is_page              = true;
305
306
			// Remove post type archive name from front page title tag
307
			add_filter( 'post_type_archive_title', '__return_empty_string', 5 );
308
309
			// Fix WP SEO
310
			if ( class_exists( 'WPSEO_Meta' ) ) {
311
				add_filter( 'wpseo_metadesc', array( $this, 'wpseo_metadesc' ) );
312
				add_filter( 'wpseo_metakey', array( $this, 'wpseo_metakey' ) );
313
			}
314
315
		// Only apply to product categories, the product post archive, the shop page, product tags, and product attribute taxonomies
316
		} elseif ( ! $q->is_post_type_archive( 'product' ) && ! $q->is_tax( get_object_taxonomies( 'product' ) ) ) {
317
			return;
318
		}
319
320
		$this->product_query( $q );
321
322
		if ( is_search() ) {
323
			add_filter( 'posts_where', array( $this, 'search_post_excerpt' ) );
324
			add_filter( 'wp', array( $this, 'remove_posts_where' ) );
325
		}
326
327
		// And remove the pre_get_posts hook
328
		$this->remove_product_query();
329
	}
330
331
	/**
332
	 * Search post excerpt.
333
	 *
334
	 * @access public
335
	 * @param string $where (default: '')
336
	 * @return string (modified where clause)
337
	 */
338
	public function search_post_excerpt( $where = '' ) {
339
		global $wp_the_query;
340
341
		// If this is not a WC Query, do not modify the query
342
		if ( empty( $wp_the_query->query_vars['wc_query'] ) || empty( $wp_the_query->query_vars['s'] ) )
343
			return $where;
344
345
		$where = preg_replace(
346
			"/post_title\s+LIKE\s*(\'\%[^\%]+\%\')/",
347
			"post_title LIKE $1) OR (post_excerpt LIKE $1", $where );
348
349
		return $where;
350
	}
351
352
	/**
353
	 * WP SEO meta description.
354
	 *
355
	 * Hooked into wpseo_ hook already, so no need for function_exist.
356
	 *
357
	 * @access public
358
	 * @return string
359
	 */
360
	public function wpseo_metadesc() {
361
		return WPSEO_Meta::get_value( 'metadesc', wc_get_page_id( 'shop' ) );
362
	}
363
364
	/**
365
	 * WP SEO meta key.
366
	 *
367
	 * Hooked into wpseo_ hook already, so no need for function_exist.
368
	 *
369
	 * @access public
370
	 * @return string
371
	 */
372
	public function wpseo_metakey() {
373
		return WPSEO_Meta::get_value( 'metakey', wc_get_page_id( 'shop' ) );
374
	}
375
376
	/**
377
	 * Query the products, applying sorting/ordering etc. This applies to the main wordpress loop.
378
	 *
379
	 * @param mixed $q
380
	 */
381
	public function product_query( $q ) {
382
		// Ordering query vars
383
		$ordering  = $this->get_catalog_ordering_args();
384
		$q->set( 'orderby', $ordering['orderby'] );
385
		$q->set( 'order', $ordering['order'] );
386
		if ( isset( $ordering['meta_key'] ) ) {
387
			$q->set( 'meta_key', $ordering['meta_key'] );
388
		}
389
390
		// Query vars that affect posts shown
391
		$q->set( 'meta_query', $this->get_meta_query( $q->get( 'meta_query' ) ) );
392
		$q->set( 'tax_query', $this->get_tax_query( $q->get( 'tax_query' ) ) );
393
		$q->set( 'posts_per_page', $q->get( 'posts_per_page' ) ? $q->get( 'posts_per_page' ) : apply_filters( 'loop_shop_per_page', get_option( 'posts_per_page' ) ) );
394
		$q->set( 'wc_query', 'product_query' );
395
		$q->set( 'post__in', array_unique( apply_filters( 'loop_shop_post_in', array() ) ) );
396
397
		do_action( 'woocommerce_product_query', $q, $this );
398
	}
399
400
401
	/**
402
	 * Remove the query.
403
	 */
404
	public function remove_product_query() {
405
		remove_action( 'pre_get_posts', array( $this, 'pre_get_posts' ) );
406
	}
407
408
	/**
409
	 * Remove ordering queries.
410
	 */
411
	public function remove_ordering_args() {
412
		remove_filter( 'posts_clauses', array( $this, 'order_by_popularity_post_clauses' ) );
413
		remove_filter( 'posts_clauses', array( $this, 'order_by_rating_post_clauses' ) );
414
	}
415
416
	/**
417
	 * Remove the posts_where filter.
418
	 */
419
	public function remove_posts_where() {
420
		remove_filter( 'posts_where', array( $this, 'search_post_excerpt' ) );
421
	}
422
423
	/**
424
	 * Returns an array of arguments for ordering products based on the selected values.
425
	 *
426
	 * @access public
427
	 * @return array
428
	 */
429
	public function get_catalog_ordering_args( $orderby = '', $order = '' ) {
430
		// Get ordering from query string unless defined
431
		if ( ! $orderby ) {
432
			$orderby_value = isset( $_GET['orderby'] ) ? wc_clean( $_GET['orderby'] ) : apply_filters( 'woocommerce_default_catalog_orderby', get_option( 'woocommerce_default_catalog_orderby' ) );
433
434
			// Get order + orderby args from string
435
			$orderby_value = explode( '-', $orderby_value );
436
			$orderby       = esc_attr( $orderby_value[0] );
437
			$order         = ! empty( $orderby_value[1] ) ? $orderby_value[1] : $order;
438
		}
439
440
		$orderby = strtolower( $orderby );
441
		$order   = strtoupper( $order );
442
		$args    = array();
443
444
		// default - menu_order
445
		$args['orderby']  = 'menu_order title';
446
		$args['order']    = $order == 'DESC' ? 'DESC' : 'ASC';
447
		$args['meta_key'] = '';
448
449
		switch ( $orderby ) {
450
			case 'rand' :
451
				$args['orderby']  = 'rand';
452
			break;
453
			case 'date' :
454
				$args['orderby']  = 'date ID';
455
				$args['order']    = $order == 'ASC' ? 'ASC' : 'DESC';
456
			break;
457
			case 'price' :
458
				$args['orderby']  = "meta_value_num ID";
459
				$args['order']    = $order == 'DESC' ? 'DESC' : 'ASC';
460
				$args['meta_key'] = '_price';
461
			break;
462
			case 'popularity' :
463
				$args['meta_key'] = 'total_sales';
464
465
				// Sorting handled later though a hook
466
				add_filter( 'posts_clauses', array( $this, 'order_by_popularity_post_clauses' ) );
467
			break;
468
			case 'rating' :
469
				// Sorting handled later though a hook
470
				add_filter( 'posts_clauses', array( $this, 'order_by_rating_post_clauses' ) );
471
			break;
472
			case 'title' :
473
				$args['orderby']  = 'title';
474
				$args['order']    = $order == 'DESC' ? 'DESC' : 'ASC';
475
			break;
476
		}
477
478
		return apply_filters( 'woocommerce_get_catalog_ordering_args', $args );
479
	}
480
481
	/**
482
	 * WP Core doens't let us change the sort direction for invidual orderby params - https://core.trac.wordpress.org/ticket/17065.
483
	 *
484
	 * This lets us sort by meta value desc, and have a second orderby param.
485
	 *
486
	 * @access public
487
	 * @param array $args
488
	 * @return array
489
	 */
490
	public function order_by_popularity_post_clauses( $args ) {
491
		global $wpdb;
492
		$args['orderby'] = "$wpdb->postmeta.meta_value+0 DESC, $wpdb->posts.post_date DESC";
493
		return $args;
494
	}
495
496
	/**
497
	 * Order by rating post clauses.
498
	 *
499
	 * @access public
500
	 * @param array $args
501
	 * @return array
502
	 */
503
	public function order_by_rating_post_clauses( $args ) {
504
		global $wpdb;
505
506
		$args['fields'] .= ", AVG( $wpdb->commentmeta.meta_value ) as average_rating ";
507
		$args['where']  .= " AND ( $wpdb->commentmeta.meta_key = 'rating' OR $wpdb->commentmeta.meta_key IS null ) ";
508
		$args['join']   .= "
509
			LEFT OUTER JOIN $wpdb->comments ON($wpdb->posts.ID = $wpdb->comments.comment_post_ID)
510
			LEFT JOIN $wpdb->commentmeta ON($wpdb->comments.comment_ID = $wpdb->commentmeta.comment_id)
511
		";
512
		$args['orderby'] = "average_rating DESC, $wpdb->posts.post_date DESC";
513
		$args['groupby'] = "$wpdb->posts.ID";
514
515
		return $args;
516
	}
517
518
	/**
519
	 * Appends meta queries to an array.
520
	 *
521
	 * @param  array $meta_query
522
	 * @return array
523
	 */
524
	public function get_meta_query( $meta_query = array() ) {
525
		if ( ! is_array( $meta_query ) ) {
526
			$meta_query = array();
527
		}
528
529
		$meta_query['visibility']    = $this->visibility_meta_query();
530
		$meta_query['stock_status']  = $this->stock_status_meta_query();
531
		$meta_query['price_filter']  = $this->price_filter_meta_query();
532
		$meta_query['rating_filter'] = $this->rating_filter_meta_query();
533
534
		return array_filter( apply_filters( 'woocommerce_product_query_meta_query', $meta_query, $this ) );
535
	}
536
537
	/**
538
	 * Return a meta query for filtering by price.
539
	 * @return array
540
	 */
541
	private function price_filter_meta_query() {
542
		if ( isset( $_GET['max_price'] ) || isset( $_GET['min_price'] ) ) {
543
			$min = isset( $_GET['min_price'] ) ? floatval( $_GET['min_price'] ) : 0;
544
			$max = isset( $_GET['max_price'] ) ? floatval( $_GET['max_price'] ) : 9999999999;
545
546
			/**
547
			 * Adjust if the store taxes are not displayed how they are stored.
548
			 * Max is left alone because the filter was already increased.
549
			 * Kicks in when prices excluding tax are displayed including tax.
550
			 */
551
			if ( wc_tax_enabled() && 'incl' === get_option( 'woocommerce_tax_display_shop' ) && ! wc_prices_include_tax() ) {
552
				$tax_classes = array_merge( array( '' ), WC_Tax::get_tax_classes() );
553
				$class_min   = $min;
554
555
				foreach ( $tax_classes as $tax_class ) {
556
					if ( $tax_rates = WC_Tax::get_rates( $tax_class ) ) {
557
						$class_min = $min - WC_Tax::get_tax_total( WC_Tax::calc_exclusive_tax( $min, $tax_rates ) );
558
					}
559
				}
560
561
				$min = $class_min;
562
			}
563
564
			return array(
565
				'key'          => '_price',
566
				'value'        => array( $min, $max ),
567
				'compare'      => 'BETWEEN',
568
				'type'         => 'DECIMAL',
569
				'price_filter' => true,
570
			);
571
		}
572
		return array();
573
	}
574
575
	/**
576
	 * Return a meta query for filtering by rating.
577
	 * @return array
578
	 */
579
	public function rating_filter_meta_query() {
580
		return isset( $_GET['min_rating'] ) ? array(
581
			'key'           => '_wc_average_rating',
582
			'value'         => isset( $_GET['min_rating'] ) ? floatval( $_GET['min_rating'] ) : 0,
583
			'compare'       => '>=',
584
			'type'          => 'DECIMAL',
585
			'rating_filter' => true,
586
		) : array();
587
	}
588
589
	/**
590
	 * Returns a meta query to handle product visibility.
591
	 * @param string $compare (default: 'IN')
592
	 * @return array
593
	 */
594
	public function visibility_meta_query( $compare = 'IN' ) {
595
		return array(
596
			'key'     => '_visibility',
597
			'value'   => is_search() ? array( 'visible', 'search' ) : array( 'visible', 'catalog' ),
598
			'compare' => $compare,
599
		);
600
	}
601
602
	/**
603
	 * Returns a meta query to handle product stock status.
604
	 *
605
	 * @access public
606
	 * @param string $status (default: 'instock')
607
	 * @return array
608
	 */
609
	public function stock_status_meta_query( $status = 'instock' ) {
610
		return 'yes' === get_option( 'woocommerce_hide_out_of_stock_items' ) ? array(
611
			'key' 		=> '_stock_status',
612
			'value' 	=> $status,
613
			'compare' 	=> '=',
614
		) : array();
615
	}
616
617
	/**
618
	 * Appends tax queries to an array.
619
	 * @param array $tax_query
620
	 * @return array
621
	 */
622
	public function get_tax_query( $tax_query = array() ) {
623
		if ( ! is_array( $tax_query ) ) {
624
			$tax_query = array();
625
		}
626
627
		// Layered nav filters on terms
628
		if ( $_chosen_attributes = $this->get_layered_nav_chosen_attributes() ) {
629
			foreach ( $_chosen_attributes as $taxonomy => $data ) {
630
				$tax_query[] = array(
631
					'taxonomy' => $taxonomy,
632
					'field'    => 'slug',
633
					'terms'    => $data['terms'],
634
					'operator' => 'and' === $data['query_type'] ? 'AND' : 'IN',
635
					'include_children' => false,
636
				);
637
			}
638
		}
639
640
		return array_filter( apply_filters( 'woocommerce_product_query_tax_query', $tax_query, $this ) );
641
	}
642
643
	/**
644
	 * Get the tax query which was used by the main query.
645
	 * @return array
646
	 */
647
	public static function get_main_tax_query() {
648
		global $wp_the_query;
649
650
		$args      = $wp_the_query->query_vars;
651
		$tax_query = isset( $args['tax_query'] ) ? $args['tax_query'] : array();
652
653
		if ( ! empty( $args['taxonomy'] ) && ! empty( $args['term'] ) ) {
654
			$tax_query[ $args['taxonomy'] ] = array(
655
				'taxonomy' => $args['taxonomy'],
656
				'terms'    => array( $args['term'] ),
657
				'field'    => 'slug',
658
			);
659
		}
660
661 View Code Duplication
		if ( ! empty( $args['product_cat'] ) ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
662
			$tax_query[ 'product_cat' ] = array(
663
				'taxonomy' => 'product_cat',
664
				'terms'    => array( $args['product_cat'] ),
665
				'field'    => 'slug',
666
			);
667
		}
668
669 View Code Duplication
		if ( ! empty( $args['product_tag'] ) ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
670
			$tax_query[ 'product_tag' ] = array(
671
				'taxonomy' => 'product_tag',
672
				'terms'    => array( $args['product_tag'] ),
673
				'field'    => 'slug',
674
			);
675
		}
676
677
		return $tax_query;
678
	}
679
680
	/**
681
	 * Get the meta query which was used by the main query.
682
	 * @return array
683
	 */
684
	public static function get_main_meta_query() {
685
		global $wp_the_query;
686
687
		$args       = $wp_the_query->query_vars;
688
		$meta_query = isset( $args['meta_query'] ) ? $args['meta_query'] : array();
689
690
		return $meta_query;
691
	}
692
693
	/**
694
	 * Based on WP_Query::parse_search
695
	 */
696
	public static function get_main_search_query_sql() {
697
		global $wp_the_query, $wpdb;
698
699
		$args         = $wp_the_query->query_vars;
700
		$search_terms = isset( $args['search_terms'] ) ? $args['search_terms'] : array();
701
		$sql          = array();
702
703
		foreach ( $search_terms as $term ) {
704
			// Terms prefixed with '-' should be excluded.
705
			$include = '-' !== substr( $term, 0, 1 );
706
707
			if ( $include ) {
708
				$like_op  = 'LIKE';
709
				$andor_op = 'OR';
710
			} else {
711
				$like_op  = 'NOT LIKE';
712
				$andor_op = 'AND';
713
				$term     = substr( $term, 1 );
714
			}
715
716
			$like  = '%' . $wpdb->esc_like( $term ) . '%';
717
			$sql[] = $wpdb->prepare( "(($wpdb->posts.post_title $like_op %s) $andor_op ($wpdb->posts.post_excerpt $like_op %s) $andor_op ($wpdb->posts.post_content $like_op %s))", $like, $like, $like );
718
		}
719
720
		if ( ! empty( $sql ) && ! is_user_logged_in() ) {
721
			$sql[] = "($wpdb->posts.post_password = '')";
722
		}
723
724
		return implode( ' AND ', $sql );
725
	}
726
727
	/**
728
	 * Layered Nav Init.
729
	 */
730
	public static function get_layered_nav_chosen_attributes() {
731
		if ( ! is_array( self::$_chosen_attributes ) ) {
732
			self::$_chosen_attributes = array();
733
734
			if ( $attribute_taxonomies = wc_get_attribute_taxonomies() ) {
735
				foreach ( $attribute_taxonomies as $tax ) {
736
					$attribute    = wc_sanitize_taxonomy_name( $tax->attribute_name );
737
					$taxonomy     = wc_attribute_taxonomy_name( $attribute );
738
					$filter_terms = ! empty( $_GET[ 'filter_' . $attribute ] ) ? explode( ',', wc_clean( $_GET[ 'filter_' . $attribute ] ) ) : array();
739
740
					if ( empty( $filter_terms ) || ! taxonomy_exists( $taxonomy ) ) {
741
						continue;
742
					}
743
744
					$query_type = ! empty( $_GET[ 'query_type_' . $attribute ] ) && in_array( $_GET[ 'query_type_' . $attribute ], array( 'and', 'or' ) ) ? wc_clean( $_GET[ 'query_type_' . $attribute ] ) : '';
745
					self::$_chosen_attributes[ $taxonomy ]['terms']      = array_map( 'sanitize_title', $filter_terms ); // Ensures correct encoding
746
					self::$_chosen_attributes[ $taxonomy ]['query_type'] = $query_type ? $query_type : apply_filters( 'woocommerce_layered_nav_default_query_type', 'and' );
747
				}
748
			}
749
		}
750
		return self::$_chosen_attributes;
751
	}
752
753
	/**
754
	 * @deprecated 2.6.0
755
	 */
756
	public function layered_nav_init() {
757
		_deprecated_function( 'layered_nav_init', '2.6', '' );
758
	}
759
760
	/**
761
	 * Get an unpaginated list all product ID's (both filtered and unfiltered). Makes use of transients.
762
	 * @deprecated 2.6.0 due to performance concerns
763
	 */
764
	public function get_products_in_view() {
765
		_deprecated_function( 'get_products_in_view', '2.6', '' );
766
	}
767
768
	/**
769
	 * Layered Nav post filter.
770
	 * @deprecated 2.6.0 due to performance concerns
771
	 */
772
	public function layered_nav_query( $filtered_posts ) {
0 ignored issues
show
Unused Code introduced by
The parameter $filtered_posts is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
773
		_deprecated_function( 'layered_nav_query', '2.6', '' );
774
	}
775
}
776