Completed
Push — master ( 887d39...c62674 )
by Mike
07:47
created

WC_Query::get_endpoints_mask()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 13
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 3
eloc 8
c 1
b 0
f 0
nc 3
nop 0
dl 0
loc 13
rs 9.4285
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
			add_rewrite_endpoint( $var, $mask );
161
		}
162
	}
163
164
	/**
165
	 * Add query vars.
166
	 *
167
	 * @access public
168
	 * @param array $vars
169
	 * @return array
170
	 */
171
	public function add_query_vars( $vars ) {
172
		foreach ( $this->query_vars as $key => $var ) {
173
			$vars[] = $key;
174
		}
175
		return $vars;
176
	}
177
178
	/**
179
	 * Get query vars.
180
	 *
181
	 * @return array
182
	 */
183
	public function get_query_vars() {
184
		return $this->query_vars;
185
	}
186
187
	/**
188
	 * Get query current active query var.
189
	 *
190
	 * @return string
191
	 */
192
	public function get_current_endpoint() {
193
		global $wp;
194
		foreach ( $this->get_query_vars() as $key => $value ) {
195
			if ( isset( $wp->query_vars[ $key ] ) ) {
196
				return $key;
197
			}
198
		}
199
		return '';
200
	}
201
202
	/**
203
	 * Parse the request and look for query vars - endpoints may not be supported.
204
	 */
205
	public function parse_request() {
206
		global $wp;
207
208
		// Map query vars to their keys, or get them if endpoints are not supported
209
		foreach ( $this->query_vars as $key => $var ) {
210
			if ( isset( $_GET[ $var ] ) ) {
211
				$wp->query_vars[ $key ] = $_GET[ $var ];
212
			}
213
214
			elseif ( isset( $wp->query_vars[ $var ] ) ) {
215
				$wp->query_vars[ $key ] = $wp->query_vars[ $var ];
216
			}
217
		}
218
	}
219
220
	/**
221
	 * Hook into pre_get_posts to do the main product query.
222
	 *
223
	 * @param mixed $q query object
224
	 */
225
	public function pre_get_posts( $q ) {
226
		// We only want to affect the main query
227
		if ( ! $q->is_main_query() ) {
228
			return;
229
		}
230
231
		// Fix for verbose page rules
232
		if ( $GLOBALS['wp_rewrite']->use_verbose_page_rules && isset( $q->queried_object->ID ) && $q->queried_object->ID === wc_get_page_id( 'shop' ) ) {
233
			$q->set( 'post_type', 'product' );
234
			$q->set( 'page', '' );
235
			$q->set( 'pagename', '' );
236
237
			// Fix conditional Functions
238
			$q->is_archive           = true;
239
			$q->is_post_type_archive = true;
240
			$q->is_singular          = false;
241
			$q->is_page              = false;
242
		}
243
244
		// Fix for endpoints on the homepage
245
		if ( $q->is_home() && 'page' === get_option( 'show_on_front' ) && absint( get_option( 'page_on_front' ) ) !== absint( $q->get( 'page_id' ) ) ) {
246
			$_query = wp_parse_args( $q->query );
247
			if ( ! empty( $_query ) && array_intersect( array_keys( $_query ), array_keys( $this->query_vars ) ) ) {
248
				$q->is_page     = true;
249
				$q->is_home     = false;
250
				$q->is_singular = true;
251
				$q->set( 'page_id', (int) get_option( 'page_on_front' ) );
252
				add_filter( 'redirect_canonical', '__return_false' );
253
			}
254
		}
255
256
		// When orderby is set, WordPress shows posts. Get around that here.
257
		if ( $q->is_home() && 'page' === get_option( 'show_on_front' ) && absint( get_option( 'page_on_front' ) ) === wc_get_page_id( 'shop' ) ) {
258
			$_query = wp_parse_args( $q->query );
259
			if ( empty( $_query ) || ! array_diff( array_keys( $_query ), array( 'preview', 'page', 'paged', 'cpage', 'orderby' ) ) ) {
260
				$q->is_page = true;
261
				$q->is_home = false;
262
				$q->set( 'page_id', (int) get_option( 'page_on_front' ) );
263
				$q->set( 'post_type', 'product' );
264
			}
265
		}
266
267
		// Special check for shops with the product archive on front
268
		if ( $q->is_page() && 'page' === get_option( 'show_on_front' ) && absint( $q->get( 'page_id' ) ) === wc_get_page_id( 'shop' ) ) {
269
270
			// This is a front-page shop
271
			$q->set( 'post_type', 'product' );
272
			$q->set( 'page_id', '' );
273
274
			if ( isset( $q->query['paged'] ) ) {
275
				$q->set( 'paged', $q->query['paged'] );
276
			}
277
278
			// Define a variable so we know this is the front page shop later on
279
			define( 'SHOP_IS_ON_FRONT', true );
280
281
			// Get the actual WP page to avoid errors and let us use is_front_page()
282
			// This is hacky but works. Awaiting https://core.trac.wordpress.org/ticket/21096
283
			global $wp_post_types;
284
285
			$shop_page 	= get_post( wc_get_page_id( 'shop' ) );
286
287
			$wp_post_types['product']->ID 			= $shop_page->ID;
288
			$wp_post_types['product']->post_title 	= $shop_page->post_title;
289
			$wp_post_types['product']->post_name 	= $shop_page->post_name;
290
			$wp_post_types['product']->post_type    = $shop_page->post_type;
291
			$wp_post_types['product']->ancestors    = get_ancestors( $shop_page->ID, $shop_page->post_type );
292
293
			// Fix conditional Functions like is_front_page
294
			$q->is_singular          = false;
295
			$q->is_post_type_archive = true;
296
			$q->is_archive           = true;
297
			$q->is_page              = true;
298
299
			// Remove post type archive name from front page title tag
300
			add_filter( 'post_type_archive_title', '__return_empty_string', 5 );
301
302
			// Fix WP SEO
303
			if ( class_exists( 'WPSEO_Meta' ) ) {
304
				add_filter( 'wpseo_metadesc', array( $this, 'wpseo_metadesc' ) );
305
				add_filter( 'wpseo_metakey', array( $this, 'wpseo_metakey' ) );
306
			}
307
308
		// Only apply to product categories, the product post archive, the shop page, product tags, and product attribute taxonomies
309
		} elseif ( ! $q->is_post_type_archive( 'product' ) && ! $q->is_tax( get_object_taxonomies( 'product' ) ) ) {
310
			return;
311
		}
312
313
		$this->product_query( $q );
314
315
		if ( is_search() ) {
316
			add_filter( 'posts_where', array( $this, 'search_post_excerpt' ) );
317
			add_filter( 'wp', array( $this, 'remove_posts_where' ) );
318
		}
319
320
		// And remove the pre_get_posts hook
321
		$this->remove_product_query();
322
	}
323
324
	/**
325
	 * Search post excerpt.
326
	 *
327
	 * @access public
328
	 * @param string $where (default: '')
329
	 * @return string (modified where clause)
330
	 */
331
	public function search_post_excerpt( $where = '' ) {
332
		global $wp_the_query;
333
334
		// If this is not a WC Query, do not modify the query
335
		if ( empty( $wp_the_query->query_vars['wc_query'] ) || empty( $wp_the_query->query_vars['s'] ) )
336
			return $where;
337
338
		$where = preg_replace(
339
			"/post_title\s+LIKE\s*(\'\%[^\%]+\%\')/",
340
			"post_title LIKE $1) OR (post_excerpt LIKE $1", $where );
341
342
		return $where;
343
	}
344
345
	/**
346
	 * WP SEO meta description.
347
	 *
348
	 * Hooked into wpseo_ hook already, so no need for function_exist.
349
	 *
350
	 * @access public
351
	 * @return string
352
	 */
353
	public function wpseo_metadesc() {
354
		return WPSEO_Meta::get_value( 'metadesc', wc_get_page_id( 'shop' ) );
355
	}
356
357
	/**
358
	 * WP SEO meta key.
359
	 *
360
	 * Hooked into wpseo_ hook already, so no need for function_exist.
361
	 *
362
	 * @access public
363
	 * @return string
364
	 */
365
	public function wpseo_metakey() {
366
		return WPSEO_Meta::get_value( 'metakey', wc_get_page_id( 'shop' ) );
367
	}
368
369
	/**
370
	 * Query the products, applying sorting/ordering etc. This applies to the main wordpress loop.
371
	 *
372
	 * @param mixed $q
373
	 */
374
	public function product_query( $q ) {
375
		// Ordering query vars
376
		$ordering  = $this->get_catalog_ordering_args();
377
		$q->set( 'orderby', $ordering['orderby'] );
378
		$q->set( 'order', $ordering['order'] );
379
		if ( isset( $ordering['meta_key'] ) ) {
380
			$q->set( 'meta_key', $ordering['meta_key'] );
381
		}
382
383
		// Query vars that affect posts shown
384
		$q->set( 'meta_query', $this->get_meta_query( $q->get( 'meta_query' ) ) );
385
		$q->set( 'tax_query', $this->get_tax_query( $q->get( 'tax_query' ) ) );
386
		$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' ) ) );
387
		$q->set( 'wc_query', 'product_query' );
388
		$q->set( 'post__in', array_unique( apply_filters( 'loop_shop_post_in', array() ) ) );
389
390
		do_action( 'woocommerce_product_query', $q, $this );
391
	}
392
393
394
	/**
395
	 * Remove the query.
396
	 */
397
	public function remove_product_query() {
398
		remove_action( 'pre_get_posts', array( $this, 'pre_get_posts' ) );
399
	}
400
401
	/**
402
	 * Remove ordering queries.
403
	 */
404
	public function remove_ordering_args() {
405
		remove_filter( 'posts_clauses', array( $this, 'order_by_popularity_post_clauses' ) );
406
		remove_filter( 'posts_clauses', array( $this, 'order_by_rating_post_clauses' ) );
407
	}
408
409
	/**
410
	 * Remove the posts_where filter.
411
	 */
412
	public function remove_posts_where() {
413
		remove_filter( 'posts_where', array( $this, 'search_post_excerpt' ) );
414
	}
415
416
	/**
417
	 * Returns an array of arguments for ordering products based on the selected values.
418
	 *
419
	 * @access public
420
	 * @return array
421
	 */
422
	public function get_catalog_ordering_args( $orderby = '', $order = '' ) {
423
		global $wpdb;
424
425
		// Get ordering from query string unless defined
426
		if ( ! $orderby ) {
427
			$orderby_value = isset( $_GET['orderby'] ) ? wc_clean( $_GET['orderby'] ) : apply_filters( 'woocommerce_default_catalog_orderby', get_option( 'woocommerce_default_catalog_orderby' ) );
428
429
			// Get order + orderby args from string
430
			$orderby_value = explode( '-', $orderby_value );
431
			$orderby       = esc_attr( $orderby_value[0] );
432
			$order         = ! empty( $orderby_value[1] ) ? $orderby_value[1] : $order;
433
		}
434
435
		$orderby = strtolower( $orderby );
436
		$order   = strtoupper( $order );
437
		$args    = array();
438
439
		// default - menu_order
440
		$args['orderby']  = 'menu_order title';
441
		$args['order']    = $order == 'DESC' ? 'DESC' : 'ASC';
442
		$args['meta_key'] = '';
443
444
		switch ( $orderby ) {
445
			case 'rand' :
446
				$args['orderby']  = 'rand';
447
			break;
448
			case 'date' :
449
				$args['orderby']  = 'date ID';
450
				$args['order']    = $order == 'ASC' ? 'ASC' : 'DESC';
451
			break;
452
			case 'price' :
453
				$args['orderby']  = "meta_value_num ID";
454
				$args['order']    = $order == 'DESC' ? 'DESC' : 'ASC';
455
				$args['meta_key'] = '_price';
456
			break;
457
			case 'popularity' :
458
				$args['meta_key'] = 'total_sales';
459
460
				// Sorting handled later though a hook
461
				add_filter( 'posts_clauses', array( $this, 'order_by_popularity_post_clauses' ) );
462
			break;
463
			case 'rating' :
464
				// Sorting handled later though a hook
465
				add_filter( 'posts_clauses', array( $this, 'order_by_rating_post_clauses' ) );
466
			break;
467
			case 'title' :
468
				$args['orderby']  = 'title';
469
				$args['order']    = $order == 'DESC' ? 'DESC' : 'ASC';
470
			break;
471
		}
472
473
		return apply_filters( 'woocommerce_get_catalog_ordering_args', $args );
474
	}
475
476
	/**
477
	 * WP Core doens't let us change the sort direction for invidual orderby params - https://core.trac.wordpress.org/ticket/17065.
478
	 *
479
	 * This lets us sort by meta value desc, and have a second orderby param.
480
	 *
481
	 * @access public
482
	 * @param array $args
483
	 * @return array
484
	 */
485
	public function order_by_popularity_post_clauses( $args ) {
486
		global $wpdb;
487
		$args['orderby'] = "$wpdb->postmeta.meta_value+0 DESC, $wpdb->posts.post_date DESC";
488
		return $args;
489
	}
490
491
	/**
492
	 * Order by rating post clauses.
493
	 *
494
	 * @access public
495
	 * @param array $args
496
	 * @return array
497
	 */
498
	public function order_by_rating_post_clauses( $args ) {
499
		global $wpdb;
500
501
		$args['fields'] .= ", AVG( $wpdb->commentmeta.meta_value ) as average_rating ";
502
		$args['where']  .= " AND ( $wpdb->commentmeta.meta_key = 'rating' OR $wpdb->commentmeta.meta_key IS null ) ";
503
		$args['join']   .= "
504
			LEFT OUTER JOIN $wpdb->comments ON($wpdb->posts.ID = $wpdb->comments.comment_post_ID)
505
			LEFT JOIN $wpdb->commentmeta ON($wpdb->comments.comment_ID = $wpdb->commentmeta.comment_id)
506
		";
507
		$args['orderby'] = "average_rating DESC, $wpdb->posts.post_date DESC";
508
		$args['groupby'] = "$wpdb->posts.ID";
509
510
		return $args;
511
	}
512
513
	/**
514
	 * Appends meta queries to an array.
515
	 * @access public
516
	 * @param array $meta_query
517
	 * @return array
518
	 */
519
	public function get_meta_query( $meta_query = array() ) {
520
		if ( ! is_array( $meta_query ) ) {
521
			$meta_query = array();
522
		}
523
524
		$meta_query[] = $this->visibility_meta_query();
525
		$meta_query[] = $this->stock_status_meta_query();
526
		$meta_query[] = $this->price_filter_meta_query();
527
		$meta_query[] = $this->rating_filter_meta_query();
528
529
		return array_filter( apply_filters( 'woocommerce_product_query_meta_query', $meta_query, $this ) );
530
	}
531
532
	/**
533
	 * Return a meta query for filtering by price.
534
	 * @return array
535
	 */
536
	private function price_filter_meta_query() {
537
		if ( isset( $_GET['max_price'] ) || isset( $_GET['min_price'] ) ) {
538
			$min = isset( $_GET['min_price'] ) ? floatval( $_GET['min_price'] ) : 0;
539
			$max = isset( $_GET['max_price'] ) ? floatval( $_GET['max_price'] ) : 9999999999;
540
541
			/**
542
			 * Adjust if the store taxes are not displayed how they are stored.
543
			 * Max is left alone because the filter was already increased.
544
			 * Kicks in when prices excluding tax are displayed including tax.
545
			 */
546
			if ( wc_tax_enabled() && 'incl' === get_option( 'woocommerce_tax_display_shop' ) && ! wc_prices_include_tax() ) {
547
				$tax_classes = array_merge( array( '' ), WC_Tax::get_tax_classes() );
548
				$class_min   = $min;
549
550
				foreach ( $tax_classes as $tax_class ) {
551
					if ( $tax_rates = WC_Tax::get_rates( $tax_class ) ) {
552
						$class_min = $min - WC_Tax::get_tax_total( WC_Tax::calc_exclusive_tax( $min, $tax_rates ) );
553
					}
554
				}
555
556
				$min = $class_min;
557
			}
558
559
			return array(
560
				'key'          => '_price',
561
				'value'        => array( $min, $max ),
562
				'compare'      => 'BETWEEN',
563
				'type'         => 'DECIMAL',
564
				'price_filter' => true,
565
			);
566
		}
567
		return array();
568
	}
569
570
	/**
571
	 * Return a meta query for filtering by rating.
572
	 * @return array
573
	 */
574
	public function rating_filter_meta_query() {
575
		return isset( $_GET['min_rating'] ) ? array(
576
			'key'           => '_wc_average_rating',
577
			'value'         => isset( $_GET['min_rating'] ) ? floatval( $_GET['min_rating'] ) : 0,
578
			'compare'       => '>=',
579
			'type'          => 'DECIMAL',
580
			'rating_filter' => true,
581
		) : array();
582
	}
583
584
	/**
585
	 * Returns a meta query to handle product visibility.
586
	 * @param string $compare (default: 'IN')
587
	 * @return array
588
	 */
589
	public function visibility_meta_query( $compare = 'IN' ) {
590
		return array(
591
			'key'     => '_visibility',
592
			'value'   => is_search() ? array( 'visible', 'search' ) : array( 'visible', 'catalog' ),
593
			'compare' => $compare,
594
		);
595
	}
596
597
	/**
598
	 * Returns a meta query to handle product stock status.
599
	 *
600
	 * @access public
601
	 * @param string $status (default: 'instock')
602
	 * @return array
603
	 */
604
	public function stock_status_meta_query( $status = 'instock' ) {
605
		return 'yes' === get_option( 'woocommerce_hide_out_of_stock_items' ) ? array(
606
			'key' 		=> '_stock_status',
607
			'value' 	=> $status,
608
			'compare' 	=> '=',
609
		) : array();
610
	}
611
612
	/**
613
	 * Appends tax queries to an array.
614
	 * @param array $tax_query
615
	 * @return array
616
	 */
617
	public function get_tax_query( $tax_query = array() ) {
618
		if ( ! is_array( $tax_query ) ) {
619
			$tax_query = array();
620
		}
621
622
		// Layered nav filters on terms
623
		if ( $_chosen_attributes = $this->get_layered_nav_chosen_attributes() ) {
624
			foreach ( $_chosen_attributes as $taxonomy => $data ) {
625
				$tax_query[] = array(
626
					'taxonomy' => $taxonomy,
627
					'field'    => 'slug',
628
					'terms'    => $data['terms'],
629
					'operator' => 'and' === $data['query_type'] ? 'AND' : 'IN',
630
					'include_children' => false,
631
				);
632
			}
633
		}
634
635
		return array_filter( apply_filters( 'woocommerce_product_query_tax_query', $tax_query, $this ) );
636
	}
637
638
	/**
639
	 * Get the tax query which was used by the main query.
640
	 * @return array
641
	 */
642
	public static function get_main_tax_query() {
643
		global $wp_the_query;
644
645
		$args      = $wp_the_query->query_vars;
646
		$tax_query = isset( $args['tax_query'] ) ? $args['tax_query'] : array();
647
648
		if ( ! empty( $args['taxonomy'] ) && ! empty( $args['term'] ) ) {
649
			$tax_query[ $args['taxonomy'] ] = array(
650
				'taxonomy' => $args['taxonomy'],
651
				'terms'    => array( $args['term'] ),
652
				'field'    => 'slug',
653
			);
654
		}
655
656 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...
657
			$tax_query[ 'product_cat' ] = array(
658
				'taxonomy' => 'product_cat',
659
				'terms'    => array( $args['product_cat'] ),
660
				'field'    => 'slug',
661
			);
662
		}
663
664 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...
665
			$tax_query[ 'product_tag' ] = array(
666
				'taxonomy' => 'product_tag',
667
				'terms'    => array( $args['product_tag'] ),
668
				'field'    => 'slug',
669
			);
670
		}
671
672
		return $tax_query;
673
	}
674
675
	/**
676
	 * Get the meta query which was used by the main query.
677
	 * @return array
678
	 */
679
	public static function get_main_meta_query() {
680
		global $wp_the_query;
681
682
		$args       = $wp_the_query->query_vars;
683
		$meta_query = isset( $args['meta_query'] ) ? $args['meta_query'] : array();
684
685
		return $meta_query;
686
	}
687
688
	/**
689
	 * Layered Nav Init.
690
	 */
691
	public static function get_layered_nav_chosen_attributes() {
692
		if ( ! is_array( self::$_chosen_attributes ) ) {
693
			self::$_chosen_attributes = array();
694
695
			if ( $attribute_taxonomies = wc_get_attribute_taxonomies() ) {
696
				foreach ( $attribute_taxonomies as $tax ) {
697
					$attribute    = wc_sanitize_taxonomy_name( $tax->attribute_name );
698
					$taxonomy     = wc_attribute_taxonomy_name( $attribute );
699
					$filter_terms = ! empty( $_GET[ 'filter_' . $attribute ] ) ? explode( ',', wc_clean( $_GET[ 'filter_' . $attribute ] ) ) : array();
700
701
					if ( empty( $filter_terms ) || ! taxonomy_exists( $taxonomy ) ) {
702
						continue;
703
					}
704
705
					$query_type = ! empty( $_GET[ 'query_type_' . $attribute ] ) && in_array( $_GET[ 'query_type_' . $attribute ], array( 'and', 'or' ) ) ? wc_clean( $_GET[ 'query_type_' . $attribute ] ) : '';
706
					self::$_chosen_attributes[ $taxonomy ]['terms']      = array_map( 'sanitize_title', $filter_terms ); // Ensures correct encoding
707
					self::$_chosen_attributes[ $taxonomy ]['query_type'] = $query_type ? $query_type : apply_filters( 'woocommerce_layered_nav_default_query_type', 'and' );
708
				}
709
			}
710
		}
711
		return self::$_chosen_attributes;
712
	}
713
714
	/**
715
	 * @deprecated 2.6.0
716
	 */
717
	public function layered_nav_init() {
718
		_deprecated_function( 'layered_nav_init', '2.6', '' );
719
	}
720
721
	/**
722
	 * Get an unpaginated list all product ID's (both filtered and unfiltered). Makes use of transients.
723
	 * @deprecated 2.6.0 due to performance concerns
724
	 */
725
	public function get_products_in_view() {
726
		_deprecated_function( 'get_products_in_view', '2.6', '' );
727
	}
728
729
	/**
730
	 * Layered Nav post filter.
731
	 * @deprecated 2.6.0 due to performance concerns
732
	 */
733
	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...
734
		_deprecated_function( 'layered_nav_query', '2.6', '' );
735
	}
736
}
737