WC_Admin_List_Table_Products::query_filters()   F
last analyzed

Complexity

Conditions 13
Paths 608

Size

Total Lines 55

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 182

Importance

Changes 0
Metric Value
cc 13
nc 608
nop 1
dl 0
loc 55
ccs 0
cts 43
cp 0
crap 182
rs 2.9944
c 0
b 0
f 0

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
 * List tables: products.
4
 *
5
 * @package  WooCommerce/Admin
6
 * @version  3.3.0
7
 */
8
9
if ( ! defined( 'ABSPATH' ) ) {
10
	exit;
11
}
12
13
if ( class_exists( 'WC_Admin_List_Table_Products', false ) ) {
14
	return;
15
}
16
17
if ( ! class_exists( 'WC_Admin_List_Table', false ) ) {
18
	include_once 'abstract-class-wc-admin-list-table.php';
19
}
20
21
/**
22
 * WC_Admin_List_Table_Products Class.
23
 */
24
class WC_Admin_List_Table_Products extends WC_Admin_List_Table {
25
26
	/**
27
	 * Post type.
28
	 *
29
	 * @var string
30
	 */
31
	protected $list_table_type = 'product';
32
33
	/**
34
	 * Constructor.
35
	 */
36
	public function __construct() {
37
		parent::__construct();
38
		add_filter( 'disable_months_dropdown', '__return_true' );
39
		add_filter( 'query_vars', array( $this, 'add_custom_query_var' ) );
40
		add_filter( 'views_edit-product', array( $this, 'product_views' ) );
41
		add_filter( 'get_search_query', array( $this, 'search_label' ) );
42
		add_filter( 'posts_clauses', array( $this, 'posts_clauses' ), 10, 2 );
43
	}
44
45
	/**
46
	 * Render blank state.
47
	 */
48
	protected function render_blank_state() {
49
		echo '<div class="woocommerce-BlankState">';
50
51
		echo '<h2 class="woocommerce-BlankState-message">' . esc_html__( 'Ready to start selling something awesome?', 'woocommerce' ) . '</h2>';
52
53
		echo '<div class="woocommerce-BlankState-buttons">';
54
55
		echo '<a class="woocommerce-BlankState-cta button-primary button" href="' . esc_url( admin_url( 'post-new.php?post_type=product&tutorial=true' ) ) . '">' . esc_html__( 'Create Product', 'woocommerce' ) . '</a>';
56
		echo '<a class="woocommerce-BlankState-cta button" href="' . esc_url( admin_url( 'edit.php?post_type=product&page=product_importer' ) ) . '">' . esc_html__( 'Start Import', 'woocommerce' ) . '</a>';
57
58
		echo '</div>';
59
60
		do_action( 'wc_marketplace_suggestions_products_empty_state' );
61
62
		echo '</div>';
63
	}
64
65
	/**
66
	 * Define primary column.
67
	 *
68
	 * @return string
69
	 */
70
	protected function get_primary_column() {
71
		return 'name';
72
	}
73
74
	/**
75
	 * Get row actions to show in the list table.
76
	 *
77
	 * @param array   $actions Array of actions.
78
	 * @param WP_Post $post Current post object.
79
	 * @return array
80
	 */
81
	protected function get_row_actions( $actions, $post ) {
82
		/* translators: %d: product ID. */
83
		return array_merge( array( 'id' => sprintf( __( 'ID: %d', 'woocommerce' ), $post->ID ) ), $actions );
84
	}
85
86
	/**
87
	 * Define which columns are sortable.
88
	 *
89
	 * @param array $columns Existing columns.
90
	 * @return array
91
	 */
92
	public function define_sortable_columns( $columns ) {
93
		$custom = array(
94
			'price' => 'price',
95
			'sku'   => 'sku',
96
			'name'  => 'title',
97
		);
98
		return wp_parse_args( $custom, $columns );
99
	}
100
101
	/**
102
	 * Define which columns to show on this screen.
103
	 *
104
	 * @param array $columns Existing columns.
105
	 * @return array
106
	 */
107
	public function define_columns( $columns ) {
108
		if ( empty( $columns ) && ! is_array( $columns ) ) {
109
			$columns = array();
110
		}
111
112
		unset( $columns['title'], $columns['comments'], $columns['date'] );
113
114
		$show_columns          = array();
115
		$show_columns['cb']    = '<input type="checkbox" />';
116
		$show_columns['thumb'] = '<span class="wc-image tips" data-tip="' . esc_attr__( 'Image', 'woocommerce' ) . '">' . __( 'Image', 'woocommerce' ) . '</span>';
117
		$show_columns['name']  = __( 'Name', 'woocommerce' );
118
119
		if ( wc_product_sku_enabled() ) {
120
			$show_columns['sku'] = __( 'SKU', 'woocommerce' );
121
		}
122
123
		if ( 'yes' === get_option( 'woocommerce_manage_stock' ) ) {
124
			$show_columns['is_in_stock'] = __( 'Stock', 'woocommerce' );
125
		}
126
127
		$show_columns['price']        = __( 'Price', 'woocommerce' );
128
		$show_columns['product_cat']  = __( 'Categories', 'woocommerce' );
129
		$show_columns['product_tag']  = __( 'Tags', 'woocommerce' );
130
		$show_columns['featured']     = '<span class="wc-featured parent-tips" data-tip="' . esc_attr__( 'Featured', 'woocommerce' ) . '">' . __( 'Featured', 'woocommerce' ) . '</span>';
131
		$show_columns['date']         = __( 'Date', 'woocommerce' );
132
133
		return array_merge( $show_columns, $columns );
134
	}
135
136
	/**
137
	 * Pre-fetch any data for the row each column has access to it. the_product global is there for bw compat.
138
	 *
139
	 * @param int $post_id Post ID being shown.
140
	 */
141 View Code Duplication
	protected function prepare_row_data( $post_id ) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
142
		global $the_product;
143
144
		if ( empty( $this->object ) || $this->object->get_id() !== $post_id ) {
145
			$the_product  = wc_get_product( $post_id );
146
			$this->object = $the_product;
0 ignored issues
show
Documentation Bug introduced by
It seems like $the_product can also be of type false. However, the property $object is declared as type object|null. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
147
		}
148
	}
149
150
	/**
151
	 * Render columm: thumb.
152
	 */
153
	protected function render_thumb_column() {
154
		echo '<a href="' . esc_url( get_edit_post_link( $this->object->get_id() ) ) . '">' . $this->object->get_image( 'thumbnail' ) . '</a>'; // WPCS: XSS ok.
155
	}
156
157
	/**
158
	 * Render column: name.
159
	 */
160
	protected function render_name_column() {
161
		global $post;
162
163
		$edit_link = get_edit_post_link( $this->object->get_id() );
164
		$title     = _draft_or_post_title();
165
166
		echo '<strong><a class="row-title" href="' . esc_url( $edit_link ) . '">' . esc_html( $title ) . '</a>';
167
168
		_post_states( $post );
169
170
		echo '</strong>';
171
172
		if ( $this->object->get_parent_id() > 0 ) {
173
			echo '&nbsp;&nbsp;&larr; <a href="' . esc_url( get_edit_post_link( $this->object->get_parent_id() ) ) . '">' . get_the_title( $this->object->get_parent_id() ) . '</a>'; // @codingStandardsIgnoreLine.
174
		}
175
176
		get_inline_data( $post );
177
178
		/* Custom inline data for woocommerce. */
179
		echo '
180
			<div class="hidden" id="woocommerce_inline_' . absint( $this->object->get_id() ) . '">
181
				<div class="menu_order">' . esc_html( $this->object->get_menu_order() ) . '</div>
182
				<div class="sku">' . esc_html( $this->object->get_sku() ) . '</div>
183
				<div class="regular_price">' . esc_html( $this->object->get_regular_price() ) . '</div>
184
				<div class="sale_price">' . esc_html( $this->object->get_sale_price() ) . '</div>
185
				<div class="weight">' . esc_html( $this->object->get_weight() ) . '</div>
186
				<div class="length">' . esc_html( $this->object->get_length() ) . '</div>
187
				<div class="width">' . esc_html( $this->object->get_width() ) . '</div>
188
				<div class="height">' . esc_html( $this->object->get_height() ) . '</div>
189
				<div class="shipping_class">' . esc_html( $this->object->get_shipping_class() ) . '</div>
190
				<div class="visibility">' . esc_html( $this->object->get_catalog_visibility() ) . '</div>
191
				<div class="stock_status">' . esc_html( $this->object->get_stock_status() ) . '</div>
192
				<div class="stock">' . esc_html( $this->object->get_stock_quantity() ) . '</div>
193
				<div class="manage_stock">' . esc_html( wc_bool_to_string( $this->object->get_manage_stock() ) ) . '</div>
194
				<div class="featured">' . esc_html( wc_bool_to_string( $this->object->get_featured() ) ) . '</div>
195
				<div class="product_type">' . esc_html( $this->object->get_type() ) . '</div>
196
				<div class="product_is_virtual">' . esc_html( wc_bool_to_string( $this->object->get_virtual() ) ) . '</div>
197
				<div class="tax_status">' . esc_html( $this->object->get_tax_status() ) . '</div>
198
				<div class="tax_class">' . esc_html( $this->object->get_tax_class() ) . '</div>
199
				<div class="backorders">' . esc_html( $this->object->get_backorders() ) . '</div>
200
				<div class="low_stock_amount">' . esc_html( $this->object->get_low_stock_amount() ) . '</div>
201
			</div>
202
		';
203
	}
204
205
	/**
206
	 * Render columm: sku.
207
	 */
208
	protected function render_sku_column() {
209
		echo $this->object->get_sku() ? esc_html( $this->object->get_sku() ) : '<span class="na">&ndash;</span>';
210
	}
211
212
	/**
213
	 * Render columm: price.
214
	 */
215
	protected function render_price_column() {
216
		echo $this->object->get_price_html() ? wp_kses_post( $this->object->get_price_html() ) : '<span class="na">&ndash;</span>';
217
	}
218
219
	/**
220
	 * Render columm: product_cat.
221
	 */
222 View Code Duplication
	protected function render_product_cat_column() {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
223
		$terms = get_the_terms( $this->object->get_id(), 'product_cat' );
224
		if ( ! $terms ) {
225
			echo '<span class="na">&ndash;</span>';
226
		} else {
227
			$termlist = array();
228
			foreach ( $terms as $term ) {
229
				$termlist[] = '<a href="' . esc_url( admin_url( 'edit.php?product_cat=' . $term->slug . '&post_type=product' ) ) . ' ">' . esc_html( $term->name ) . '</a>';
230
			}
231
232
			echo apply_filters( 'woocommerce_admin_product_term_list', implode( ', ', $termlist ), 'product_cat', $this->object->get_id(), $termlist, $terms ); // WPCS: XSS ok.
233
		}
234
	}
235
236
	/**
237
	 * Render columm: product_tag.
238
	 */
239 View Code Duplication
	protected function render_product_tag_column() {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
240
		$terms = get_the_terms( $this->object->get_id(), 'product_tag' );
241
		if ( ! $terms ) {
242
			echo '<span class="na">&ndash;</span>';
243
		} else {
244
			$termlist = array();
245
			foreach ( $terms as $term ) {
246
				$termlist[] = '<a href="' . esc_url( admin_url( 'edit.php?product_tag=' . $term->slug . '&post_type=product' ) ) . ' ">' . esc_html( $term->name ) . '</a>';
247
			}
248
249
			echo apply_filters( 'woocommerce_admin_product_term_list', implode( ', ', $termlist ), 'product_tag', $this->object->get_id(), $termlist, $terms ); // WPCS: XSS ok.
250
		}
251
	}
252
253
	/**
254
	 * Render columm: featured.
255
	 */
256
	protected function render_featured_column() {
257
		$url = wp_nonce_url( admin_url( 'admin-ajax.php?action=woocommerce_feature_product&product_id=' . $this->object->get_id() ), 'woocommerce-feature-product' );
258
		echo '<a href="' . esc_url( $url ) . '" aria-label="' . esc_attr__( 'Toggle featured', 'woocommerce' ) . '">';
259
		if ( $this->object->is_featured() ) {
260
			echo '<span class="wc-featured tips" data-tip="' . esc_attr__( 'Yes', 'woocommerce' ) . '">' . esc_html__( 'Yes', 'woocommerce' ) . '</span>';
261
		} else {
262
			echo '<span class="wc-featured not-featured tips" data-tip="' . esc_attr__( 'No', 'woocommerce' ) . '">' . esc_html__( 'No', 'woocommerce' ) . '</span>';
263
		}
264
		echo '</a>';
265
	}
266
267
	/**
268
	 * Render columm: is_in_stock.
269
	 */
270
	protected function render_is_in_stock_column() {
271
		if ( $this->object->is_on_backorder() ) {
272
			$stock_html = '<mark class="onbackorder">' . __( 'On backorder', 'woocommerce' ) . '</mark>';
273
		} elseif ( $this->object->is_in_stock() ) {
274
			$stock_html = '<mark class="instock">' . __( 'In stock', 'woocommerce' ) . '</mark>';
275
		} else {
276
			$stock_html = '<mark class="outofstock">' . __( 'Out of stock', 'woocommerce' ) . '</mark>';
277
		}
278
279
		if ( $this->object->managing_stock() ) {
280
			$stock_html .= ' (' . wc_stock_amount( $this->object->get_stock_quantity() ) . ')';
281
		}
282
283
		echo wp_kses_post( apply_filters( 'woocommerce_admin_stock_html', $stock_html, $this->object ) );
284
	}
285
286
	/**
287
	 * Query vars for custom searches.
288
	 *
289
	 * @param mixed $public_query_vars Array of query vars.
290
	 * @return array
291
	 */
292
	public function add_custom_query_var( $public_query_vars ) {
293
		$public_query_vars[] = 'sku';
294
		return $public_query_vars;
295
	}
296
297
	/**
298
	 * Render any custom filters and search inputs for the list table.
299
	 */
300
	protected function render_filters() {
301
		$filters = apply_filters(
302
			'woocommerce_products_admin_list_table_filters',
303
			array(
304
				'product_category' => array( $this, 'render_products_category_filter' ),
305
				'product_type'     => array( $this, 'render_products_type_filter' ),
306
				'stock_status'     => array( $this, 'render_products_stock_status_filter' ),
307
			)
308
		);
309
310
		ob_start();
311
		foreach ( $filters as $filter_callback ) {
312
			call_user_func( $filter_callback );
313
		}
314
		$output = ob_get_clean();
315
316
		echo apply_filters( 'woocommerce_product_filters', $output ); // WPCS: XSS ok.
317
	}
318
319
	/**
320
	 * Render the product category filter for the list table.
321
	 *
322
	 * @since 3.5.0
323
	 */
324
	protected function render_products_category_filter() {
325
		$categories_count = (int) wp_count_terms( 'product_cat' );
326
327
		if ( $categories_count <= apply_filters( 'woocommerce_product_category_filter_threshold', 100 ) ) {
328
			wc_product_dropdown_categories(
329
				array(
330
					'option_select_text' => __( 'Filter by category', 'woocommerce' ),
331
					'hide_empty'         => 0,
332
				)
333
			);
334
		} else {
335
			$current_category_slug = isset( $_GET['product_cat'] ) ? wc_clean( wp_unslash( $_GET['product_cat'] ) ) : false; // WPCS: input var ok, CSRF ok.
336
			$current_category      = $current_category_slug ? get_term_by( 'slug', $current_category_slug, 'product_cat' ) : false;
337
			?>
338
			<select class="wc-category-search" name="product_cat" data-placeholder="<?php esc_attr_e( 'Filter by category', 'woocommerce' ); ?>" data-allow_clear="true">
339
				<?php if ( $current_category_slug && $current_category ) : ?>
340
					<option value="<?php echo esc_attr( $current_category_slug ); ?>" selected="selected"><?php echo esc_html( htmlspecialchars( wp_kses_post( $current_category->name ) ) ); ?><option>
341
				<?php endif; ?>
342
			</select>
343
			<?php
344
		}
345
	}
346
347
	/**
348
	 * Render the product type filter for the list table.
349
	 *
350
	 * @since 3.5.0
351
	 */
352
	protected function render_products_type_filter() {
353
		$current_product_type = isset( $_REQUEST['product_type'] ) ? wc_clean( wp_unslash( $_REQUEST['product_type'] ) ) : false; // WPCS: input var ok, sanitization ok.
354
		$output               = '<select name="product_type" id="dropdown_product_type"><option value="">' . __( 'Filter by product type', 'woocommerce' ) . '</option>';
355
356
		foreach ( wc_get_product_types() as $value => $label ) {
357
			$output .= '<option value="' . esc_attr( $value ) . '" ';
358
			$output .= selected( $value, $current_product_type, false );
359
			$output .= '>' . esc_html( $label ) . '</option>';
360
361
			if ( 'simple' === $value ) {
362
363
				$output .= '<option value="downloadable" ';
364
				$output .= selected( 'downloadable', $current_product_type, false );
365
				$output .= '> ' . ( is_rtl() ? '&larr;' : '&rarr;' ) . ' ' . __( 'Downloadable', 'woocommerce' ) . '</option>';
366
367
				$output .= '<option value="virtual" ';
368
				$output .= selected( 'virtual', $current_product_type, false );
369
				$output .= '> ' . ( is_rtl() ? '&larr;' : '&rarr;' ) . ' ' . __( 'Virtual', 'woocommerce' ) . '</option>';
370
			}
371
		}
372
373
		$output .= '</select>';
374
		echo $output; // WPCS: XSS ok.
375
	}
376
377
	/**
378
	 * Render the stock status filter for the list table.
379
	 *
380
	 * @since 3.5.0
381
	 */
382
	public function render_products_stock_status_filter() {
383
		$current_stock_status = isset( $_REQUEST['stock_status'] ) ? wc_clean( wp_unslash( $_REQUEST['stock_status'] ) ) : false; // WPCS: input var ok, sanitization ok.
384
		$stock_statuses       = wc_get_product_stock_status_options();
385
		$output               = '<select name="stock_status"><option value="">' . esc_html__( 'Filter by stock status', 'woocommerce' ) . '</option>';
386
387
		foreach ( $stock_statuses as $status => $label ) {
388
			$output .= '<option ' . selected( $status, $current_stock_status, false ) . ' value="' . esc_attr( $status ) . '">' . esc_html( $label ) . '</option>';
389
		}
390
391
		$output .= '</select>';
392
		echo $output; // WPCS: XSS ok.
393
	}
394
395
	/**
396
	 * Search by SKU or ID for products.
397
	 *
398
	 * @deprecated Logic moved to query_filters.
399
	 * @param string $where Where clause SQL.
400
	 * @return string
401
	 */
402
	public function sku_search( $where ) {
403
		return $where;
404
	}
405
406
	/**
407
	 * Change views on the edit product screen.
408
	 *
409
	 * @param  array $views Array of views.
410
	 * @return array
411
	 */
412
	public function product_views( $views ) {
413
		global $wp_query;
414
415
		// Products do not have authors.
416
		unset( $views['mine'] );
417
418
		// Add sorting link.
419
		if ( current_user_can( 'edit_others_pages' ) ) {
420
			$class            = ( isset( $wp_query->query['orderby'] ) && 'menu_order title' === $wp_query->query['orderby'] ) ? 'current' : '';
421
			$query_string     = remove_query_arg( array( 'orderby', 'order' ) );
422
			$query_string     = add_query_arg( 'orderby', rawurlencode( 'menu_order title' ), $query_string );
423
			$query_string     = add_query_arg( 'order', rawurlencode( 'ASC' ), $query_string );
424
			$views['byorder'] = '<a href="' . esc_url( $query_string ) . '" class="' . esc_attr( $class ) . '">' . __( 'Sorting', 'woocommerce' ) . '</a>';
425
		}
426
427
		return $views;
428
	}
429
430
	/**
431
	 * Change the label when searching products
432
	 *
433
	 * @param string $query Search Query.
434
	 * @return string
435
	 */
436 View Code Duplication
	public function search_label( $query ) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
437
		global $pagenow, $typenow;
438
439
		if ( 'edit.php' !== $pagenow || 'product' !== $typenow || ! get_query_var( 'product_search' ) || ! isset( $_GET['s'] ) ) { // WPCS: input var ok.
440
			return $query;
441
		}
442
443
		return wc_clean( wp_unslash( $_GET['s'] ) ); // WPCS: input var ok, sanitization ok.
444
	}
445
446
	/**
447
	 * Handle any custom filters.
448
	 *
449
	 * @param array $query_vars Query vars.
450
	 * @return array
451
	 */
452
	protected function query_filters( $query_vars ) {
453
		$this->remove_ordering_args();
454
		// Custom order by arguments.
455
		if ( isset( $query_vars['orderby'] ) ) {
456
			$orderby = strtolower( $query_vars['orderby'] );
457
			$order   = isset( $query_vars['order'] ) ? strtoupper( $query_vars['order'] ) : 'DESC';
458
459
			if ( 'price' === $orderby ) {
460
				$callback = 'DESC' === $order ? 'order_by_price_desc_post_clauses' : 'order_by_price_asc_post_clauses';
461
				add_filter( 'posts_clauses', array( $this, $callback ) );
462
			}
463
464
			if ( 'sku' === $orderby ) {
465
				$callback = 'DESC' === $order ? 'order_by_sku_desc_post_clauses' : 'order_by_sku_asc_post_clauses';
466
				add_filter( 'posts_clauses', array( $this, $callback ) );
467
			}
468
		}
469
470
		// Type filtering.
471
		if ( isset( $query_vars['product_type'] ) ) {
472
			if ( 'downloadable' === $query_vars['product_type'] ) {
473
				$query_vars['product_type'] = '';
474
				add_filter( 'posts_clauses', array( $this, 'filter_downloadable_post_clauses' ) );
475
			} elseif ( 'virtual' === $query_vars['product_type'] ) {
476
				$query_vars['product_type'] = '';
477
				add_filter( 'posts_clauses', array( $this, 'filter_virtual_post_clauses' ) );
478
			}
479
		}
480
481
		// Stock status filter.
482
		if ( ! empty( $_GET['stock_status'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.NoNonceVerification
483
			add_filter( 'posts_clauses', array( $this, 'filter_stock_status_post_clauses' ) );
484
		}
485
486
		// Shipping class taxonomy.
487
		if ( ! empty( $_GET['product_shipping_class'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.NoNonceVerification
488
			$query_vars['tax_query'][] = array(
489
				'taxonomy' => 'product_shipping_class',
490
				'field'    => 'slug',
491
				'terms'    => sanitize_title( wp_unslash( $_GET['product_shipping_class'] ) ),
492
				'operator' => 'IN',
493
			);
494
		}
495
496
		// Search using CRUD.
497
		if ( ! empty( $query_vars['s'] ) ) {
498
			$data_store                   = WC_Data_Store::load( 'product' );
499
			$ids                          = $data_store->search_products( wc_clean( wp_unslash( $query_vars['s'] ) ), '', true, true );
0 ignored issues
show
Documentation Bug introduced by
The method search_products does not exist on object<WC_Data_Store>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
500
			$query_vars['post__in']       = array_merge( $ids, array( 0 ) );
501
			$query_vars['product_search'] = true;
502
			unset( $query_vars['s'] );
503
		}
504
505
		return $query_vars;
506
	}
507
508
	/**
509
	 * Undocumented function
510
	 *
511
	 * @param array    $args  Array of SELECT statement pieces (from, where, etc).
512
	 * @param WP_Query $query WP_Query instance.
513
	 * @return array
514
	 */
515
	public function posts_clauses( $args, $query ) {
0 ignored issues
show
Unused Code introduced by
The parameter $query 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...
516
517
		return $args;
518
	}
519
520
	/**
521
	 * Remove ordering queries.
522
	 *
523
	 * @param array $posts Posts array, keeping this for backwards compatibility defaulting to empty array.
524
	 * @return array
525
	 */
526
	public function remove_ordering_args( $posts = array() ) {
527
		remove_filter( 'posts_clauses', array( $this, 'order_by_price_asc_post_clauses' ) );
528
		remove_filter( 'posts_clauses', array( $this, 'order_by_price_desc_post_clauses' ) );
529
		remove_filter( 'posts_clauses', array( $this, 'order_by_sku_asc_post_clauses' ) );
530
		remove_filter( 'posts_clauses', array( $this, 'order_by_sku_desc_post_clauses' ) );
531
		remove_filter( 'posts_clauses', array( $this, 'filter_downloadable_post_clauses' ) );
532
		remove_filter( 'posts_clauses', array( $this, 'filter_virtual_post_clauses' ) );
533
		remove_filter( 'posts_clauses', array( $this, 'filter_stock_status_post_clauses' ) );
534
		return $posts; // Keeping this here for backward compatibility.
535
	}
536
537
	/**
538
	 * Handle numeric price sorting.
539
	 *
540
	 * @param array $args Query args.
541
	 * @return array
542
	 */
543
	public function order_by_price_asc_post_clauses( $args ) {
544
		$args['join']    = $this->append_product_sorting_table_join( $args['join'] );
545
		$args['orderby'] = ' wc_product_meta_lookup.min_price ASC, wc_product_meta_lookup.product_id ASC ';
546
		return $args;
547
	}
548
549
	/**
550
	 * Handle numeric price sorting.
551
	 *
552
	 * @param array $args Query args.
553
	 * @return array
554
	 */
555
	public function order_by_price_desc_post_clauses( $args ) {
556
		$args['join']    = $this->append_product_sorting_table_join( $args['join'] );
557
		$args['orderby'] = ' wc_product_meta_lookup.max_price DESC, wc_product_meta_lookup.product_id DESC ';
558
		return $args;
559
	}
560
561
	/**
562
	 * Handle sku sorting.
563
	 *
564
	 * @param array $args Query args.
565
	 * @return array
566
	 */
567
	public function order_by_sku_asc_post_clauses( $args ) {
568
		$args['join']    = $this->append_product_sorting_table_join( $args['join'] );
569
		$args['orderby'] = ' wc_product_meta_lookup.sku ASC, wc_product_meta_lookup.product_id ASC ';
570
		return $args;
571
	}
572
573
	/**
574
	 * Handle sku sorting.
575
	 *
576
	 * @param array $args Query args.
577
	 * @return array
578
	 */
579
	public function order_by_sku_desc_post_clauses( $args ) {
580
		$args['join']    = $this->append_product_sorting_table_join( $args['join'] );
581
		$args['orderby'] = ' wc_product_meta_lookup.sku DESC, wc_product_meta_lookup.product_id DESC ';
582
		return $args;
583
	}
584
585
	/**
586
	 * Filter by type.
587
	 *
588
	 * @param array $args Query args.
589
	 * @return array
590
	 */
591
	public function filter_downloadable_post_clauses( $args ) {
592
		$args['join']   = $this->append_product_sorting_table_join( $args['join'] );
593
		$args['where'] .= ' AND wc_product_meta_lookup.downloadable=1 ';
594
		return $args;
595
	}
596
597
	/**
598
	 * Filter by type.
599
	 *
600
	 * @param array $args Query args.
601
	 * @return array
602
	 */
603
	public function filter_virtual_post_clauses( $args ) {
604
		$args['join']   = $this->append_product_sorting_table_join( $args['join'] );
605
		$args['where'] .= ' AND wc_product_meta_lookup.virtual=1 ';
606
		return $args;
607
	}
608
609
	/**
610
	 * Filter by stock status.
611
	 *
612
	 * @param array $args Query args.
613
	 * @return array
614
	 */
615
	public function filter_stock_status_post_clauses( $args ) {
616
		global $wpdb;
617
		if ( ! empty( $_GET['stock_status'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.NoNonceVerification
618
			$args['join']   = $this->append_product_sorting_table_join( $args['join'] );
619
			$args['where'] .= $wpdb->prepare( ' AND wc_product_meta_lookup.stock_status=%s ', wc_clean( wp_unslash( $_GET['stock_status'] ) ) ); // phpcs:ignore WordPress.Security.NonceVerification.NoNonceVerification
620
		}
621
		return $args;
622
	}
623
624
	/**
625
	 * Join wc_product_meta_lookup to posts if not already joined.
626
	 *
627
	 * @param string $sql SQL join.
628
	 * @return string
629
	 */
630 View Code Duplication
	private function append_product_sorting_table_join( $sql ) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
631
		global $wpdb;
632
633
		if ( ! strstr( $sql, 'wc_product_meta_lookup' ) ) {
634
			$sql .= " LEFT JOIN {$wpdb->wc_product_meta_lookup} wc_product_meta_lookup ON $wpdb->posts.ID = wc_product_meta_lookup.product_id ";
635
		}
636
		return $sql;
637
	}
638
639
	/**
640
	 * Modifies post query so that it includes parent products whose variations have particular shipping class assigned.
641
	 *
642
	 * @param array    $pieces   Array of SELECT statement pieces (from, where, etc).
643
	 * @param WP_Query $wp_query WP_Query instance.
644
	 * @return array             Array of products, including parents of variations.
645
	 */
646
	public function add_variation_parents_for_shipping_class( $pieces, $wp_query ) {
0 ignored issues
show
Unused Code introduced by
The parameter $wp_query 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...
647
		global $wpdb;
648
		if ( isset( $_GET['product_shipping_class'] ) && '0' !== $_GET['product_shipping_class'] ) { // WPCS: input var ok.
649
			$replaced_where   = str_replace( ".post_type = 'product'", ".post_type = 'product_variation'", $pieces['where'] );
650
			$pieces['where'] .= " OR {$wpdb->posts}.ID in (
651
				SELECT {$wpdb->posts}.post_parent FROM
652
				{$wpdb->posts} LEFT JOIN {$wpdb->term_relationships} ON ({$wpdb->posts}.ID = {$wpdb->term_relationships}.object_id)
653
				WHERE 1=1 $replaced_where
654
			)";
655
			return $pieces;
656
		}
657
		return $pieces;
658
	}
659
660
}
661