Completed
Push — master ( 8ef395...730019 )
by Mike
08:12
created

wc-term-functions.php ➔ wc_taxonomy_metadata_wpdbfix()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 11
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
dl 0
loc 11
rs 9.4285
c 0
b 0
f 0
eloc 8
nc 1
nop 0
1
<?php
1 ignored issue
show
Coding Style Compatibility introduced by
For compatibility and reusability of your code, PSR1 recommends that a file should introduce either new symbols (like classes, functions, etc.) or have side-effects (like outputting something, or including other files), but not both at the same time. The first symbol is defined on line 28 and the first side effect is on line 14.

The PSR-1: Basic Coding Standard recommends that a file should either introduce new symbols, that is classes, functions, constants or similar, or have side effects. Side effects are anything that executes logic, like for example printing output, changing ini settings or writing to a file.

The idea behind this recommendation is that merely auto-loading a class should not change the state of an application. It also promotes a cleaner style of programming and makes your code less prone to errors, because the logic is not spread out all over the place.

To learn more about the PSR-1, please see the PHP-FIG site on the PSR-1.

Loading history...
2
/**
3
 * WooCommerce Terms
4
 *
5
 * Functions for handling terms/term meta.
6
 *
7
 * @author 		WooThemes
8
 * @category 	Core
9
 * @package 	WooCommerce/Functions
10
 * @version     2.1.0
11
 */
12
13
if ( ! defined( 'ABSPATH' ) ) {
14
	exit; // Exit if accessed directly
15
}
16
17
/**
18
 * Wrapper for wp_get_post_terms which supports ordering by parent.
19
 *
20
 * NOTE: At this point in time, ordering by menu_order for example isn't possible with this function. wp_get_post_terms has no.
21
 *   filters which we can utilise to modify it's query. https://core.trac.wordpress.org/ticket/19094.
22
 *
23
 * @param  int $product_id
24
 * @param  string $taxonomy
25
 * @param  array  $args
26
 * @return array
27
 */
28
function wc_get_product_terms( $product_id, $taxonomy, $args = array() ) {
29
	if ( ! taxonomy_exists( $taxonomy ) ) {
30
		return array();
31
	}
32
33
	if ( empty( $args['orderby'] ) && taxonomy_is_product_attribute( $taxonomy ) ) {
34
		$args['orderby'] = wc_attribute_orderby( $taxonomy );
35
	}
36
37
	// Support ordering by parent
38
	if ( ! empty( $args['orderby'] ) && in_array( $args['orderby'], array( 'name_num', 'parent' ) ) ) {
39
		$fields  = isset( $args['fields'] ) ? $args['fields'] : 'all';
40
		$orderby = $args['orderby'];
41
42
		// Unset for wp_get_post_terms
43
		unset( $args['orderby'] );
44
		unset( $args['fields'] );
45
46
		$terms = wp_get_post_terms( $product_id, $taxonomy, $args );
47
48 View Code Duplication
		switch ( $orderby ) {
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...
49
			case 'name_num' :
50
				usort( $terms, '_wc_get_product_terms_name_num_usort_callback' );
51
			break;
52
			case 'parent' :
53
				usort( $terms, '_wc_get_product_terms_parent_usort_callback' );
54
			break;
55
		}
56
57
		switch ( $fields ) {
58
			case 'names' :
59
				$terms = wp_list_pluck( $terms, 'name' );
60
				break;
61
			case 'ids' :
62
				$terms = wp_list_pluck( $terms, 'term_id' );
63
				break;
64
			case 'slugs' :
65
				$terms = wp_list_pluck( $terms, 'slug' );
66
				break;
67
		}
68
	} elseif ( ! empty( $args['orderby'] ) && $args['orderby'] === 'menu_order' ) {
69
		// wp_get_post_terms doesn't let us use custom sort order
70
		$args['include'] = wp_get_post_terms( $product_id, $taxonomy, array( 'fields' => 'ids' ) );
71
72
		if ( empty( $args['include'] ) ) {
73
			$terms = array();
74
		} else {
75
			// This isn't needed for get_terms
76
			unset( $args['orderby'] );
77
78
			// Set args for get_terms
79
			$args['menu_order'] = isset( $args['order'] ) ? $args['order'] : 'ASC';
80
			$args['hide_empty'] = isset( $args['hide_empty'] ) ? $args['hide_empty'] : 0;
81
			$args['fields']     = isset( $args['fields'] ) ? $args['fields'] : 'names';
82
83
			// Ensure slugs is valid for get_terms - slugs isn't supported
84
			$args['fields']     = $args['fields'] === 'slugs' ? 'id=>slug' : $args['fields'];
85
			$terms              = get_terms( $taxonomy, $args );
86
		}
87
	} else {
88
		$terms = wp_get_post_terms( $product_id, $taxonomy, $args );
89
	}
90
91
	return apply_filters( 'woocommerce_get_product_terms' , $terms, $product_id, $taxonomy, $args );
92
}
93
94
95
/**
96
 * Sort by name (numeric).
97
 * @param  WP_POST object $a
98
 * @param  WP_POST object $b
99
 * @return int
100
 */
101
function _wc_get_product_terms_name_num_usort_callback( $a, $b ) {
102
	if ( $a->name + 0 === $b->name + 0 ) {
103
		return 0;
104
	}
105
	return ( $a->name + 0 < $b->name + 0 ) ? -1 : 1;
106
}
107
/**
108
 * Sort by parent.
109
 * @param  WP_POST object $a
110
 * @param  WP_POST object $b
111
 * @return int
112
 */
113
function _wc_get_product_terms_parent_usort_callback( $a, $b ) {
114
	if ( $a->parent === $b->parent ) {
115
		return 0;
116
	}
117
	return ( $a->parent < $b->parent ) ? 1 : -1;
118
}
119
120
/**
121
 * WooCommerce Dropdown categories.
122
 *
123
 * Stuck with this until a fix for https://core.trac.wordpress.org/ticket/13258.
124
 * We use a custom walker, just like WordPress does.
125
 *
126
 * @param int $deprecated_show_uncategorized (default: 1)
127
 * @return string
128
 */
129
function wc_product_dropdown_categories( $args = array(), $deprecated_hierarchical = 1, $deprecated_show_uncategorized = 1, $deprecated_orderby = '' ) {
130
	global $wp_query;
131
132
	if ( ! is_array( $args ) ) {
133
		_deprecated_argument( 'wc_product_dropdown_categories()', '2.1', 'show_counts, hierarchical, show_uncategorized and orderby arguments are invalid - pass a single array of values instead.' );
134
135
		$args['show_count']         = $args;
136
		$args['hierarchical']       = $deprecated_hierarchical;
137
		$args['show_uncategorized'] = $deprecated_show_uncategorized;
138
		$args['orderby']            = $deprecated_orderby;
139
	}
140
141
	$current_product_cat = isset( $wp_query->query_vars['product_cat'] ) ? $wp_query->query_vars['product_cat'] : '';
142
	$defaults            = array(
143
		'pad_counts'         => 1,
144
		'show_count'         => 1,
145
		'hierarchical'       => 1,
146
		'hide_empty'         => 1,
147
		'show_uncategorized' => 1,
148
		'orderby'            => 'name',
149
		'selected'           => $current_product_cat,
150
		'menu_order'         => false
151
	);
152
153
	$args = wp_parse_args( $args, $defaults );
154
155
	if ( $args['orderby'] == 'order' ) {
156
		$args['menu_order'] = 'asc';
157
		$args['orderby']    = 'name';
158
	}
159
160
	$terms = get_terms( 'product_cat', apply_filters( 'wc_product_dropdown_categories_get_terms_args', $args ) );
161
162
	if ( empty( $terms ) ) {
163
		return;
164
	}
165
166
	$output  = "<select name='product_cat' class='dropdown_product_cat'>";
167
	$output .= '<option value="" ' .  selected( $current_product_cat, '', false ) . '>' . __( 'Select a category', 'woocommerce' ) . '</option>';
168
	$output .= wc_walk_category_dropdown_tree( $terms, 0, $args );
169
	if ( $args['show_uncategorized'] ) {
170
		$output .= '<option value="0" ' . selected( $current_product_cat, '0', false ) . '>' . __( 'Uncategorized', 'woocommerce' ) . '</option>';
171
	}
172
	$output .= "</select>";
173
174
	echo $output;
175
}
176
177
/**
178
 * Walk the Product Categories.
179
 *
180
 * @return mixed
181
 */
182
function wc_walk_category_dropdown_tree() {
183
	if ( ! class_exists( 'WC_Product_Cat_Dropdown_Walker' ) ) {
184
		include_once( WC()->plugin_path() . '/includes/walkers/class-product-cat-dropdown-walker.php' );
185
	}
186
187
	$args = func_get_args();
188
189
	// the user's options are the third parameter
190
	if ( empty( $args[2]['walker']) || !is_a($args[2]['walker'], 'Walker' ) ) {
191
		$walker = new WC_Product_Cat_Dropdown_Walker;
192
	} else {
193
		$walker = $args[2]['walker'];
194
	}
195
196
	return call_user_func_array( array( &$walker, 'walk' ), $args );
197
}
198
199
/**
200
 * When a term is split, ensure meta data maintained.
201
 * @param  int $old_term_id
202
 * @param  int $new_term_id
203
 * @param  string $term_taxonomy_id
204
 * @param  string $taxonomy
205
 */
206
function wc_taxonomy_metadata_update_content_for_split_terms( $old_term_id, $new_term_id, $term_taxonomy_id, $taxonomy ) {
0 ignored issues
show
Unused Code introduced by
The parameter $term_taxonomy_id 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...
207
	global $wpdb;
208
209
	if ( get_option( 'db_version' ) < 34370 ) {
210
		if ( 'product_cat' === $taxonomy || taxonomy_is_product_attribute( $taxonomy ) ) {
211
			$old_meta_data = $wpdb->get_results( $wpdb->prepare( "SELECT * FROM {$wpdb->prefix}woocommerce_termmeta WHERE woocommerce_term_id = %d;", $old_term_id ) );
212
213
			// Copy across to split term
214
			if ( $old_meta_data ) {
215
				foreach ( $old_meta_data as $meta_data ) {
216
					$wpdb->insert(
217
						"{$wpdb->prefix}woocommerce_termmeta",
218
						array(
219
							'woocommerce_term_id' => $new_term_id,
220
							'meta_key'            => $meta_data->meta_key,
221
							'meta_value'          => $meta_data->meta_value
222
						)
223
					);
224
				}
225
			}
226
		}
227
	}
228
}
229
add_action( 'split_shared_term', 'wc_taxonomy_metadata_update_content_for_split_terms', 10, 4 );
230
231
/**
232
 * Migrate data from WC term meta to WP term meta
233
 *
234
 * When the database is updated to support term meta, migrate WC term meta data across.
235
 * We do this when the new version is >= 34370, and the old version is < 34370 (34370 is when term meta table was added).
236
 *
237
 * @param  string $wp_db_version The new $wp_db_version.
238
 * @param  string $wp_current_db_version The old (current) $wp_db_version.
239
 */
240
function wc_taxonomy_metadata_migrate_data( $wp_db_version, $wp_current_db_version ) {
241 View Code Duplication
	if ( $wp_db_version >= 34370 && $wp_current_db_version < 34370 ) {
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...
242
		global $wpdb;
243
		if ( $wpdb->query( "INSERT INTO {$wpdb->termmeta} ( term_id, meta_key, meta_value ) SELECT woocommerce_term_id, meta_key, meta_value FROM {$wpdb->prefix}woocommerce_termmeta;" ) ) {
244
			$wpdb->query( "DROP TABLE IF EXISTS {$wpdb->prefix}woocommerce_termmeta" );
245
		}
246
	}
247
}
248
add_action( 'wp_upgrade', 'wc_taxonomy_metadata_migrate_data', 10, 2 );
249
250
/**
251
 * WooCommerce Term Meta API
252
 *
253
 * WC tables for storing term meta are @deprecated from WordPress 4.4 since 4.4 has its own table.
254
 * This function serves as a wrapper, using the new table if present, or falling back to the WC table.
255
 *
256
 * @todo These functions should be deprecated with notices in a future WC version, allowing users a chance to upgrade WordPress.
257
 *
258
 * @param mixed $term_id
259
 * @param string $meta_key
260
 * @param mixed $meta_value
261
 * @param string $prev_value (default: '')
262
 * @return bool
263
 */
264
function update_woocommerce_term_meta( $term_id, $meta_key, $meta_value, $prev_value = '' ) {
265
	return function_exists( 'update_term_meta' ) ? update_term_meta( $term_id, $meta_key, $meta_value, $prev_value ) : update_metadata( 'woocommerce_term', $term_id, $meta_key, $meta_value, $prev_value );
266
}
267
268
/**
269
 * WooCommerce Term Meta API
270
 *
271
 * WC tables for storing term meta are @deprecated from WordPress 4.4 since 4.4 has its own table.
272
 * This function serves as a wrapper, using the new table if present, or falling back to the WC table.
273
 *
274
 * @todo These functions should be deprecated with notices in a future WC version, allowing users a chance to upgrade WordPress.
275
 * @param mixed $term_id
276
 * @param mixed $meta_key
277
 * @param mixed $meta_value
278
 * @param bool $unique (default: false)
279
 * @return bool
280
 */
281
function add_woocommerce_term_meta( $term_id, $meta_key, $meta_value, $unique = false ){
282
	return function_exists( 'add_term_meta' ) ? add_term_meta( $term_id, $meta_key, $meta_value, $unique ) : add_metadata( 'woocommerce_term', $term_id, $meta_key, $meta_value, $unique );
283
}
284
285
/**
286
 * WooCommerce Term Meta API
287
 *
288
 * WC tables for storing term meta are @deprecated from WordPress 4.4 since 4.4 has its own table.
289
 * This function serves as a wrapper, using the new table if present, or falling back to the WC table.
290
 *
291
 * @todo These functions should be deprecated with notices in a future WC version, allowing users a chance to upgrade WordPress.
292
 * @param mixed $term_id
293
 * @param string $meta_key
294
 * @param string $meta_value (default: '')
295
 * @param bool $deprecated (default: false)
296
 * @return bool
297
 */
298
function delete_woocommerce_term_meta( $term_id, $meta_key, $meta_value = '', $deprecated = false ) {
0 ignored issues
show
Unused Code introduced by
The parameter $deprecated 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...
299
	return function_exists( 'delete_term_meta' ) ? delete_term_meta( $term_id, $meta_key, $meta_value ) : delete_metadata( 'woocommerce_term', $term_id, $meta_key, $meta_value );
300
}
301
302
/**
303
 * WooCommerce Term Meta API
304
 *
305
 * WC tables for storing term meta are @deprecated from WordPress 4.4 since 4.4 has its own table.
306
 * This function serves as a wrapper, using the new table if present, or falling back to the WC table.
307
 *
308
 * @todo These functions should be deprecated with notices in a future WC version, allowing users a chance to upgrade WordPress.
309
 * @param mixed $term_id
310
 * @param string $key
311
 * @param bool $single (default: true)
312
 * @return mixed
313
 */
314
function get_woocommerce_term_meta( $term_id, $key, $single = true ) {
315
	return function_exists( 'get_term_meta' ) ? get_term_meta( $term_id, $key, $single ) : get_metadata( 'woocommerce_term', $term_id, $key, $single );
316
}
317
318
/**
319
 * Move a term before the a	given element of its hierarchy level.
320
 *
321
 * @param int $the_term
322
 * @param int $next_id the id of the next sibling element in save hierarchy level
323
 * @param string $taxonomy
324
 * @param int $index (default: 0)
325
 * @param mixed $terms (default: null)
326
 * @return int
327
 */
328
function wc_reorder_terms( $the_term, $next_id, $taxonomy, $index = 0, $terms = null ) {
329
	if ( ! $terms ) $terms = get_terms( $taxonomy, 'menu_order=ASC&hide_empty=0&parent=0' );
330
	if ( empty( $terms ) ) return $index;
331
332
	$id	= $the_term->term_id;
333
334
	$term_in_level = false; // flag: is our term to order in this level of terms
335
336
	foreach ( $terms as $term ) {
337
338
		if ( $term->term_id == $id ) { // our term to order, we skip
339
			$term_in_level = true;
340
			continue; // our term to order, we skip
341
		}
342
		// the nextid of our term to order, lets move our term here
343
		if (null !== $next_id && $term->term_id == $next_id) {
344
			$index++;
345
			$index = wc_set_term_order($id, $index, $taxonomy, true);
346
		}
347
348
		// set order
349
		$index++;
350
		$index = wc_set_term_order($term->term_id, $index, $taxonomy);
351
352
		// if that term has children we walk through them
353
		$children = get_terms($taxonomy, "parent={$term->term_id}&menu_order=ASC&hide_empty=0");
354
		if ( ! empty( $children ) ) {
355
			$index = wc_reorder_terms( $the_term, $next_id, $taxonomy, $index, $children );
356
		}
357
	}
358
359
	// no nextid meaning our term is in last position
360
	if ( $term_in_level && null === $next_id ) {
361
		$index = wc_set_term_order( $id, $index + 1, $taxonomy, true );
362
	}
363
364
	return $index;
365
}
366
367
/**
368
 * Set the sort order of a term.
369
 *
370
 * @param int $term_id
371
 * @param int $index
372
 * @param string $taxonomy
373
 * @param bool $recursive (default: false)
374
 * @return int
375
 */
376
function wc_set_term_order( $term_id, $index, $taxonomy, $recursive = false ) {
377
378
	$term_id 	= (int) $term_id;
379
	$index 		= (int) $index;
380
381
	// Meta name
382
	if ( taxonomy_is_product_attribute( $taxonomy ) )
383
		$meta_name =  'order_' . esc_attr( $taxonomy );
384
	else
385
		$meta_name = 'order';
386
387
	update_woocommerce_term_meta( $term_id, $meta_name, $index );
388
389
	if( ! $recursive ) return $index;
390
391
	$children = get_terms($taxonomy, "parent=$term_id&menu_order=ASC&hide_empty=0");
392
393
	foreach ( $children as $term ) {
394
		$index++;
395
		$index = wc_set_term_order($term->term_id, $index, $taxonomy, true);
396
	}
397
398
	clean_term_cache( $term_id, $taxonomy );
399
400
	return $index;
401
}
402
403
/**
404
 * Add term ordering to get_terms.
405
 *
406
 * It enables the support a 'menu_order' parameter to get_terms for the product_cat taxonomy.
407
 * By default it is 'ASC'. It accepts 'DESC' too.
408
 *
409
 * To disable it, set it ot false (or 0).
410
 *
411
 * @param array $clauses
412
 * @param array $taxonomies
413
 * @param array $args
414
 * @return array
415
 */
416
function wc_terms_clauses( $clauses, $taxonomies, $args ) {
417
	global $wpdb;
418
419
	// No sorting when menu_order is false.
420
	if ( isset( $args['menu_order'] ) && $args['menu_order'] == false ) {
421
		return $clauses;
422
	}
423
424
	// No sorting when orderby is non default.
425
	if ( isset( $args['orderby'] ) && $args['orderby'] != 'name' ) {
426
		return $clauses;
427
	}
428
429
	// No sorting in admin when sorting by a column.
430
	if ( is_admin() && isset( $_GET['orderby'] ) ) {
431
		return $clauses;
432
	}
433
434
	// Wordpress should give us the taxonomies asked when calling the get_terms function. Only apply to categories and pa_ attributes.
435
	$found = false;
436
	foreach ( (array) $taxonomies as $taxonomy ) {
437
		if ( taxonomy_is_product_attribute( $taxonomy ) || in_array( $taxonomy, apply_filters( 'woocommerce_sortable_taxonomies', array( 'product_cat' ) ) ) ) {
438
			$found = true;
439
			break;
440
		}
441
	}
442
	if ( ! $found ) {
443
		return $clauses;
444
	}
445
446
	// Meta name.
447
	if ( ! empty( $taxonomies[0] ) && taxonomy_is_product_attribute( $taxonomies[0] ) ) {
448
		$meta_name = 'order_' . esc_attr( $taxonomies[0] );
449
	} else {
450
		$meta_name = 'order';
451
	}
452
453
	// Query fields.
454
	if ( strpos( 'COUNT(*)', $clauses['fields'] ) === false )  {
455
		$clauses['fields'] = 'tm.*, ' . $clauses['fields'];
456
	}
457
458
	// Query join.
459
	if ( get_option( 'db_version' ) < 34370 ) {
460
		$clauses['join'] .= " LEFT JOIN {$wpdb->woocommerce_termmeta} AS tm ON (t.term_id = tm.woocommerce_term_id AND tm.meta_key = '" . esc_sql( $meta_name ) . "') ";
461
	} else {
462
		$clauses['join'] .= " LEFT JOIN {$wpdb->termmeta} AS tm ON (t.term_id = tm.term_id AND tm.meta_key = '" . esc_sql( $meta_name ) . "') ";
463
	}
464
465
	// Default to ASC.
466
	if ( ! isset( $args['menu_order'] ) || ! in_array( strtoupper( $args['menu_order'] ), array( 'ASC', 'DESC' ) ) ) {
467
		$args['menu_order'] = 'ASC';
468
	}
469
470
	$order = "ORDER BY tm.meta_value+0 " . $args['menu_order'];
471
472
	if ( $clauses['orderby'] ) {
473
		$clauses['orderby'] = str_replace( 'ORDER BY', $order . ',', $clauses['orderby'] );
474
	} else {
475
		$clauses['orderby'] = $order;
476
	}
477
478
	return $clauses;
479
}
480
481
add_filter( 'terms_clauses', 'wc_terms_clauses', 10, 3 );
482
483
/**
484
 * Function for recounting product terms, ignoring hidden products.
485
 *
486
 * @param  array $terms
487
 * @param  string $taxonomy
488
 * @param  bool $callback
489
 * @param  bool $terms_are_term_taxonomy_ids
490
 */
491
function _wc_term_recount( $terms, $taxonomy, $callback = true, $terms_are_term_taxonomy_ids = true ) {
492
	global $wpdb;
493
494
	// Standard callback
495
	if ( $callback ) {
496
		_update_post_term_count( $terms, $taxonomy );
497
	}
498
499
	// Stock query
500
	if ( get_option( 'woocommerce_hide_out_of_stock_items' ) == 'yes' ) {
501
		$stock_join  = "LEFT JOIN {$wpdb->postmeta} AS meta_stock ON posts.ID = meta_stock.post_id";
502
		$stock_query = "
503
		AND meta_stock.meta_key = '_stock_status'
504
		AND meta_stock.meta_value = 'instock'
505
		";
506
	} else {
507
		$stock_query = $stock_join = '';
508
	}
509
510
	// Main query
511
	$count_query = "
512
		SELECT COUNT( DISTINCT posts.ID ) FROM {$wpdb->posts} as posts
513
		LEFT JOIN {$wpdb->postmeta} AS meta_visibility ON posts.ID = meta_visibility.post_id
514
		LEFT JOIN {$wpdb->term_relationships} AS rel ON posts.ID=rel.object_ID
515
		LEFT JOIN {$wpdb->term_taxonomy} AS tax USING( term_taxonomy_id )
516
		LEFT JOIN {$wpdb->terms} AS term USING( term_id )
517
		LEFT JOIN {$wpdb->postmeta} AS postmeta ON posts.ID = postmeta.post_id
518
		$stock_join
519
		WHERE 	post_status = 'publish'
520
		AND 	post_type 	= 'product'
521
		AND 	meta_visibility.meta_key = '_visibility'
522
		AND 	meta_visibility.meta_value IN ( 'visible', 'catalog' )
523
		$stock_query
524
	";
525
526
	// Pre-process term taxonomy ids
527
	if ( ! $terms_are_term_taxonomy_ids ) {
528
		// We passed in an array of TERMS in format id=>parent
529
		$terms = array_filter( (array) array_keys( $terms ) );
530
	} else {
531
		// If we have term taxonomy IDs we need to get the term ID
532
		$term_taxonomy_ids = $terms;
533
		$terms             = array();
534
		foreach ( $term_taxonomy_ids as $term_taxonomy_id ) {
535
			$term    = get_term_by( 'term_taxonomy_id', $term_taxonomy_id, $taxonomy->name );
536
			$terms[] = $term->term_id;
537
		}
538
	}
539
540
	// Exit if we have no terms to count
541
	if ( empty( $terms ) ) {
542
		return;
543
	}
544
545
	// Ancestors need counting
546
	if ( is_taxonomy_hierarchical( $taxonomy->name ) ) {
547
		foreach ( $terms as $term_id ) {
548
			$terms = array_merge( $terms, get_ancestors( $term_id, $taxonomy->name ) );
549
		}
550
	}
551
552
	// Unique terms only
553
	$terms = array_unique( $terms );
554
555
	// Count the terms
556
	foreach ( $terms as $term_id ) {
557
		$terms_to_count = array( absint( $term_id ) );
558
559
		if ( is_taxonomy_hierarchical( $taxonomy->name ) ) {
560
			// We need to get the $term's hierarchy so we can count its children too
561
			if ( ( $children = get_term_children( $term_id, $taxonomy->name ) ) && ! is_wp_error( $children ) ) {
562
				$terms_to_count = array_unique( array_map( 'absint', array_merge( $terms_to_count, $children ) ) );
563
			}
564
		}
565
566
		// Generate term query
567
		$term_query = 'AND term_id IN ( ' . implode( ',', $terms_to_count ) . ' )';
568
569
		// Get the count
570
		$count = $wpdb->get_var( $count_query . $term_query );
571
572
		// Update the count
573
		update_woocommerce_term_meta( $term_id, 'product_count_' . $taxonomy->name, absint( $count ) );
574
	}
575
576
	delete_transient( 'wc_term_counts' );
577
}
578
579
/**
580
 * Recount terms after the stock amount changes.
581
 *
582
 * @param int $product_id
583
 */
584
function wc_recount_after_stock_change( $product_id ) {
585
	if ( get_option( 'woocommerce_hide_out_of_stock_items' ) != 'yes' )
586
		return;
587
588
	$product_terms = get_the_terms( $product_id, 'product_cat' );
589
590 View Code Duplication
	if ( $product_terms ) {
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...
591
		$product_cats = array();
592
593
		foreach ( $product_terms as $term ) {
594
			$product_cats[ $term->term_id ] = $term->parent;
595
		}
596
597
		_wc_term_recount( $product_cats, get_taxonomy( 'product_cat' ), false, false );
598
	}
599
600
	$product_terms = get_the_terms( $product_id, 'product_tag' );
601
602 View Code Duplication
	if ( $product_terms ) {
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...
603
		$product_tags = array();
604
605
		foreach ( $product_terms as $term ) {
606
			$product_tags[ $term->term_id ] = $term->parent;
607
		}
608
609
		_wc_term_recount( $product_tags, get_taxonomy( 'product_tag' ), false, false );
610
	}
611
}
612
add_action( 'woocommerce_product_set_stock_status', 'wc_recount_after_stock_change' );
613
614
615
/**
616
 * Overrides the original term count for product categories and tags with the product count.
617
 * that takes catalog visibility into account.
618
 *
619
 * @param array $terms
620
 * @param string|array $taxonomies
621
 * @return array
622
 */
623
function wc_change_term_counts( $terms, $taxonomies ) {
624
	if ( is_admin() || is_ajax() ) {
625
		return $terms;
626
	}
627
628
	if ( ! isset( $taxonomies[0] ) || ! in_array( $taxonomies[0], apply_filters( 'woocommerce_change_term_counts', array( 'product_cat', 'product_tag' ) ) ) ) {
629
		return $terms;
630
	}
631
632
	$term_counts = $o_term_counts = get_transient( 'wc_term_counts' );
633
634
	foreach ( $terms as &$term ) {
635
		if ( is_object( $term ) ) {
636
			$term_counts[ $term->term_id ] = isset( $term_counts[ $term->term_id ] ) ? $term_counts[ $term->term_id ] : get_woocommerce_term_meta( $term->term_id, 'product_count_' . $taxonomies[0] , true );
637
638
			if ( $term_counts[ $term->term_id ] !== '' ) {
639
				$term->count = absint( $term_counts[ $term->term_id ] );
640
			}
641
		}
642
	}
643
644
	// Update transient
645
	if ( $term_counts != $o_term_counts ) {
646
		set_transient( 'wc_term_counts', $term_counts, DAY_IN_SECONDS * 30 );
647
	}
648
649
	return $terms;
650
}
651
add_filter( 'get_terms', 'wc_change_term_counts', 10, 2 );
652
653
/**
654
 * Return products in a given term, and cache value.
655
 *
656
 * To keep in sync, product_count will be cleared on "set_object_terms".
657
 *
658
 * @param int $term_id
659
 * @param string $taxonomy
660
 * @return array
661
 */
662
function wc_get_term_product_ids( $term_id, $taxonomy ) {
663
	$product_ids = get_woocommerce_term_meta( $term_id, 'product_ids', true );
664
665
	if ( false === $product_ids || ! is_array( $product_ids ) ) {
666
		$product_ids = get_objects_in_term( $term_id, $taxonomy );
667
		update_woocommerce_term_meta( $term_id, 'product_ids', $product_ids );
668
	}
669
670
	return $product_ids;
671
}
672
673
/**
674
 * When a post is updated and terms recounted (called by _update_post_term_count), clear the ids.
675
 * @param int    $object_id  Object ID.
676
 * @param array  $terms      An array of object terms.
677
 * @param array  $tt_ids     An array of term taxonomy IDs.
678
 * @param string $taxonomy   Taxonomy slug.
679
 * @param bool   $append     Whether to append new terms to the old terms.
680
 * @param array  $old_tt_ids Old array of term taxonomy IDs.
681
 */
682
function wc_clear_term_product_ids( $object_id, $terms, $tt_ids, $taxonomy, $append, $old_tt_ids ) {
0 ignored issues
show
Unused Code introduced by
The parameter $object_id 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...
Unused Code introduced by
The parameter $terms 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...
Unused Code introduced by
The parameter $taxonomy 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...
Unused Code introduced by
The parameter $append 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...
683
	foreach ( $old_tt_ids as $term_id ) {
684
		delete_woocommerce_term_meta( $term_id, 'product_ids' );
685
	}
686
	foreach ( $tt_ids as $term_id ) {
687
		delete_woocommerce_term_meta( $term_id, 'product_ids' );
688
	}
689
}
690
add_action( 'set_object_terms', 'wc_clear_term_product_ids', 10, 6 );
691