wc-term-functions.php ➔ get_woocommerce_term_meta()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 3
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
dl 0
loc 3
rs 10
c 0
b 0
f 0
eloc 2
nc 2
nop 3
1
<?php
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 ( ! $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
 * WooCommerce Term/Order item Meta API - set table name.
201
 */
202
function wc_taxonomy_metadata_wpdbfix() {
203
	global $wpdb;
204
	$termmeta_name = 'woocommerce_termmeta';
205
	$itemmeta_name = 'woocommerce_order_itemmeta';
206
207
	$wpdb->woocommerce_termmeta = $wpdb->prefix . $termmeta_name;
208
	$wpdb->order_itemmeta = $wpdb->prefix . $itemmeta_name;
209
210
	$wpdb->tables[] = 'woocommerce_termmeta';
211
	$wpdb->tables[] = 'woocommerce_order_itemmeta';
212
}
213
add_action( 'init', 'wc_taxonomy_metadata_wpdbfix', 0 );
214
add_action( 'switch_blog', 'wc_taxonomy_metadata_wpdbfix', 0 );
215
216
/**
217
 * When a term is split, ensure meta data maintained.
218
 * @param  int $old_term_id
219
 * @param  int $new_term_id
220
 * @param  string $term_taxonomy_id
221
 * @param  string $taxonomy
222
 */
223
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...
224
	global $wpdb;
225
226
	if ( get_option( 'db_version' ) < 34370 ) {
227
		if ( 'product_cat' === $taxonomy || taxonomy_is_product_attribute( $taxonomy ) ) {
228
			$old_meta_data = $wpdb->get_results( $wpdb->prepare( "SELECT * FROM {$wpdb->prefix}woocommerce_termmeta WHERE woocommerce_term_id = %d;", $old_term_id ) );
229
230
			// Copy across to split term
231
			if ( $old_meta_data ) {
232
				foreach ( $old_meta_data as $meta_data ) {
233
					$wpdb->insert(
234
						"{$wpdb->prefix}woocommerce_termmeta",
235
						array(
236
							'woocommerce_term_id' => $new_term_id,
237
							'meta_key'            => $meta_data->meta_key,
238
							'meta_value'          => $meta_data->meta_value
239
						)
240
					);
241
				}
242
			}
243
		}
244
	}
245
}
246
add_action( 'split_shared_term', 'wc_taxonomy_metadata_update_content_for_split_terms', 10, 4 );
247
248
/**
249
 * Migrate data from WC term meta to WP term meta
250
 *
251
 * When the database is updated to support term meta, migrate WC term meta data across.
252
 * We do this when the new version is >= 34370, and the old version is < 34370 (34370 is when term meta table was added).
253
 *
254
 * @param  string $wp_db_version The new $wp_db_version.
255
 * @param  string $wp_current_db_version The old (current) $wp_db_version.
256
 */
257
function wc_taxonomy_metadata_migrate_data( $wp_db_version, $wp_current_db_version ) {
258 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...
259
		global $wpdb;
260
		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;" ) ) {
261
			$wpdb->query( "DROP TABLE IF EXISTS {$wpdb->prefix}woocommerce_termmeta" );
262
		}
263
	}
264
}
265
add_action( 'wp_upgrade', 'wc_taxonomy_metadata_migrate_data', 10, 2 );
266
267
/**
268
 * WooCommerce Term Meta API
269
 *
270
 * WC tables for storing term meta are @deprecated from WordPress 4.4 since 4.4 has its own table.
271
 * This function serves as a wrapper, using the new table if present, or falling back to the WC table.
272
 *
273
 * @todo These functions should be deprecated with notices in a future WC version, allowing users a chance to upgrade WordPress.
274
 *
275
 * @param mixed $term_id
276
 * @param string $meta_key
277
 * @param mixed $meta_value
278
 * @param string $prev_value (default: '')
279
 * @return bool
280
 */
281
function update_woocommerce_term_meta( $term_id, $meta_key, $meta_value, $prev_value = '' ) {
282
	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 );
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 mixed $meta_key
294
 * @param mixed $meta_value
295
 * @param bool $unique (default: false)
296
 * @return bool
297
 */
298
function add_woocommerce_term_meta( $term_id, $meta_key, $meta_value, $unique = false ){
299
	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 );
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 $meta_key
311
 * @param string $meta_value (default: '')
312
 * @param bool $deprecated (default: false)
313
 * @return bool
314
 */
315
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...
316
	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 );
317
}
318
319
/**
320
 * WooCommerce Term Meta API
321
 *
322
 * WC tables for storing term meta are @deprecated from WordPress 4.4 since 4.4 has its own table.
323
 * This function serves as a wrapper, using the new table if present, or falling back to the WC table.
324
 *
325
 * @todo These functions should be deprecated with notices in a future WC version, allowing users a chance to upgrade WordPress.
326
 * @param mixed $term_id
327
 * @param string $key
328
 * @param bool $single (default: true)
329
 * @return mixed
330
 */
331
function get_woocommerce_term_meta( $term_id, $key, $single = true ) {
332
	return function_exists( 'get_term_meta' ) ? get_term_meta( $term_id, $key, $single ) : get_metadata( 'woocommerce_term', $term_id, $key, $single );
333
}
334
335
/**
336
 * Move a term before the a	given element of its hierarchy level.
337
 *
338
 * @param int $the_term
339
 * @param int $next_id the id of the next sibling element in save hierarchy level
340
 * @param string $taxonomy
341
 * @param int $index (default: 0)
342
 * @param mixed $terms (default: null)
343
 * @return int
344
 */
345
function wc_reorder_terms( $the_term, $next_id, $taxonomy, $index = 0, $terms = null ) {
346
	if ( ! $terms ) $terms = get_terms( $taxonomy, 'menu_order=ASC&hide_empty=0&parent=0' );
347
	if ( empty( $terms ) ) return $index;
348
349
	$id	= $the_term->term_id;
350
351
	$term_in_level = false; // flag: is our term to order in this level of terms
352
353
	foreach ( $terms as $term ) {
354
355
		if ( $term->term_id == $id ) { // our term to order, we skip
356
			$term_in_level = true;
357
			continue; // our term to order, we skip
358
		}
359
		// the nextid of our term to order, lets move our term here
360
		if (null !== $next_id && $term->term_id == $next_id) {
361
			$index++;
362
			$index = wc_set_term_order($id, $index, $taxonomy, true);
363
		}
364
365
		// set order
366
		$index++;
367
		$index = wc_set_term_order($term->term_id, $index, $taxonomy);
368
369
		// if that term has children we walk through them
370
		$children = get_terms($taxonomy, "parent={$term->term_id}&menu_order=ASC&hide_empty=0");
371
		if ( ! empty( $children ) ) {
372
			$index = wc_reorder_terms( $the_term, $next_id, $taxonomy, $index, $children );
373
		}
374
	}
375
376
	// no nextid meaning our term is in last position
377
	if ( $term_in_level && null === $next_id ) {
378
		$index = wc_set_term_order( $id, $index + 1, $taxonomy, true );
379
	}
380
381
	return $index;
382
}
383
384
/**
385
 * Set the sort order of a term.
386
 *
387
 * @param int $term_id
388
 * @param int $index
389
 * @param string $taxonomy
390
 * @param bool $recursive (default: false)
391
 * @return int
392
 */
393
function wc_set_term_order( $term_id, $index, $taxonomy, $recursive = false ) {
394
395
	$term_id 	= (int) $term_id;
396
	$index 		= (int) $index;
397
398
	// Meta name
399
	if ( taxonomy_is_product_attribute( $taxonomy ) )
400
		$meta_name =  'order_' . esc_attr( $taxonomy );
401
	else
402
		$meta_name = 'order';
403
404
	update_woocommerce_term_meta( $term_id, $meta_name, $index );
405
406
	if( ! $recursive ) return $index;
407
408
	$children = get_terms($taxonomy, "parent=$term_id&menu_order=ASC&hide_empty=0");
409
410
	foreach ( $children as $term ) {
411
		$index++;
412
		$index = wc_set_term_order($term->term_id, $index, $taxonomy, true);
413
	}
414
415
	clean_term_cache( $term_id, $taxonomy );
416
417
	return $index;
418
}
419
420
/**
421
 * Add term ordering to get_terms.
422
 *
423
 * It enables the support a 'menu_order' parameter to get_terms for the product_cat taxonomy.
424
 * By default it is 'ASC'. It accepts 'DESC' too.
425
 *
426
 * To disable it, set it ot false (or 0).
427
 *
428
 * @param array $clauses
429
 * @param array $taxonomies
430
 * @param array $args
431
 * @return array
432
 */
433
function wc_terms_clauses( $clauses, $taxonomies, $args ) {
434
	global $wpdb;
435
436
	// No sorting when menu_order is false.
437
	if ( isset( $args['menu_order'] ) && $args['menu_order'] == false ) {
438
		return $clauses;
439
	}
440
441
	// No sorting when orderby is non default.
442
	if ( isset( $args['orderby'] ) && $args['orderby'] != 'name' ) {
443
		return $clauses;
444
	}
445
446
	// No sorting in admin when sorting by a column.
447
	if ( is_admin() && isset( $_GET['orderby'] ) ) {
448
		return $clauses;
449
	}
450
451
	// Wordpress should give us the taxonomies asked when calling the get_terms function. Only apply to categories and pa_ attributes.
452
	$found = false;
453
	foreach ( (array) $taxonomies as $taxonomy ) {
454
		if ( taxonomy_is_product_attribute( $taxonomy ) || in_array( $taxonomy, apply_filters( 'woocommerce_sortable_taxonomies', array( 'product_cat' ) ) ) ) {
455
			$found = true;
456
			break;
457
		}
458
	}
459
	if ( ! $found ) {
460
		return $clauses;
461
	}
462
463
	// Meta name.
464
	if ( ! empty( $taxonomies[0] ) && taxonomy_is_product_attribute( $taxonomies[0] ) ) {
465
		$meta_name = 'order_' . esc_attr( $taxonomies[0] );
466
	} else {
467
		$meta_name = 'order';
468
	}
469
470
	// Query fields.
471
	if ( strpos( 'COUNT(*)', $clauses['fields'] ) === false )  {
472
		$clauses['fields'] = 'tm.*, ' . $clauses['fields'];
473
	}
474
475
	// Query join.
476
	if ( get_option( 'db_version' ) < 34370 ) {
477
		$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 ) . "') ";
478
	} else {
479
		$clauses['join'] .= " LEFT JOIN {$wpdb->termmeta} AS tm ON (t.term_id = tm.term_id AND tm.meta_key = '" . esc_sql( $meta_name ) . "') ";
480
	}
481
482
	// Default to ASC.
483
	if ( ! isset( $args['menu_order'] ) || ! in_array( strtoupper( $args['menu_order'] ), array( 'ASC', 'DESC' ) ) ) {
484
		$args['menu_order'] = 'ASC';
485
	}
486
487
	$order = "ORDER BY tm.meta_value+0 " . $args['menu_order'];
488
489
	if ( $clauses['orderby'] ) {
490
		$clauses['orderby'] = str_replace( 'ORDER BY', $order . ',', $clauses['orderby'] );
491
	} else {
492
		$clauses['orderby'] = $order;
493
	}
494
495
	return $clauses;
496
}
497
498
add_filter( 'terms_clauses', 'wc_terms_clauses', 10, 3 );
499
500
/**
501
 * Function for recounting product terms, ignoring hidden products.
502
 *
503
 * @param  array $terms
504
 * @param  string $taxonomy
505
 * @param  bool $callback
506
 * @param  bool $terms_are_term_taxonomy_ids
507
 */
508
function _wc_term_recount( $terms, $taxonomy, $callback = true, $terms_are_term_taxonomy_ids = true ) {
509
	global $wpdb;
510
511
	// Standard callback
512
	if ( $callback ) {
513
		_update_post_term_count( $terms, $taxonomy );
514
	}
515
516
	// Stock query
517
	if ( get_option( 'woocommerce_hide_out_of_stock_items' ) == 'yes' ) {
518
		$stock_join  = "LEFT JOIN {$wpdb->postmeta} AS meta_stock ON posts.ID = meta_stock.post_id";
519
		$stock_query = "
520
		AND meta_stock.meta_key = '_stock_status'
521
		AND meta_stock.meta_value = 'instock'
522
		";
523
	} else {
524
		$stock_query = $stock_join = '';
525
	}
526
527
	// Main query
528
	$count_query = "
529
		SELECT COUNT( DISTINCT posts.ID ) FROM {$wpdb->posts} as posts
530
		LEFT JOIN {$wpdb->postmeta} AS meta_visibility ON posts.ID = meta_visibility.post_id
531
		LEFT JOIN {$wpdb->term_relationships} AS rel ON posts.ID=rel.object_ID
532
		LEFT JOIN {$wpdb->term_taxonomy} AS tax USING( term_taxonomy_id )
533
		LEFT JOIN {$wpdb->terms} AS term USING( term_id )
534
		LEFT JOIN {$wpdb->postmeta} AS postmeta ON posts.ID = postmeta.post_id
535
		$stock_join
536
		WHERE 	post_status = 'publish'
537
		AND 	post_type 	= 'product'
538
		AND 	meta_visibility.meta_key = '_visibility'
539
		AND 	meta_visibility.meta_value IN ( 'visible', 'catalog' )
540
		$stock_query
541
	";
542
543
	// Pre-process term taxonomy ids
544
	if ( ! $terms_are_term_taxonomy_ids ) {
545
		// We passed in an array of TERMS in format id=>parent
546
		$terms = array_filter( (array) array_keys( $terms ) );
547
	} else {
548
		// If we have term taxonomy IDs we need to get the term ID
549
		$term_taxonomy_ids = $terms;
550
		$terms             = array();
551
		foreach ( $term_taxonomy_ids as $term_taxonomy_id ) {
552
			$term    = get_term_by( 'term_taxonomy_id', $term_taxonomy_id, $taxonomy->name );
553
			$terms[] = $term->term_id;
554
		}
555
	}
556
557
	// Exit if we have no terms to count
558
	if ( ! $terms ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $terms of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
559
		return;
560
	}
561
562
	// Ancestors need counting
563
	if ( is_taxonomy_hierarchical( $taxonomy->name ) ) {
564
		foreach ( $terms as $term_id ) {
565
			$terms = array_merge( $terms, get_ancestors( $term_id, $taxonomy->name ) );
566
		}
567
	}
568
569
	// Unique terms only
570
	$terms = array_unique( $terms );
571
572
	// Count the terms
573
	foreach ( $terms as $term_id ) {
574
		$terms_to_count = array( absint( $term_id ) );
575
576
		if ( is_taxonomy_hierarchical( $taxonomy->name ) ) {
577
			// We need to get the $term's hierarchy so we can count its children too
578
			if ( ( $children = get_term_children( $term_id, $taxonomy->name ) ) && ! is_wp_error( $children ) ) {
579
				$terms_to_count = array_unique( array_map( 'absint', array_merge( $terms_to_count, $children ) ) );
580
			}
581
		}
582
583
		// Generate term query
584
		$term_query = 'AND term_id IN ( ' . implode( ',', $terms_to_count ) . ' )';
585
586
		// Get the count
587
		$count = $wpdb->get_var( $count_query . $term_query );
588
589
		// Update the count
590
		update_woocommerce_term_meta( $term_id, 'product_count_' . $taxonomy->name, absint( $count ) );
591
	}
592
593
	delete_transient( 'wc_term_counts' );
594
}
595
596
/**
597
 * Recount terms after the stock amount changes.
598
 *
599
 * @param int $product_id
600
 */
601
function wc_recount_after_stock_change( $product_id ) {
602
	if ( get_option( 'woocommerce_hide_out_of_stock_items' ) != 'yes' )
603
		return;
604
605
	$product_terms = get_the_terms( $product_id, 'product_cat' );
606
607 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...
608
		$product_cats = array();
609
610
		foreach ( $product_terms as $term ) {
611
			$product_cats[ $term->term_id ] = $term->parent;
612
		}
613
614
		_wc_term_recount( $product_cats, get_taxonomy( 'product_cat' ), false, false );
615
	}
616
617
	$product_terms = get_the_terms( $product_id, 'product_tag' );
618
619 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...
620
		$product_tags = array();
621
622
		foreach ( $product_terms as $term ) {
623
			$product_tags[ $term->term_id ] = $term->parent;
624
		}
625
626
		_wc_term_recount( $product_tags, get_taxonomy( 'product_tag' ), false, false );
627
	}
628
}
629
add_action( 'woocommerce_product_set_stock_status', 'wc_recount_after_stock_change' );
630
631
632
/**
633
 * Overrides the original term count for product categories and tags with the product count.
634
 * that takes catalog visibility into account.
635
 *
636
 * @param array $terms
637
 * @param string|array $taxonomies
638
 * @return array
639
 */
640
function wc_change_term_counts( $terms, $taxonomies ) {
641
	if ( is_admin() || is_ajax() ) {
642
		return $terms;
643
	}
644
645
	if ( ! isset( $taxonomies[0] ) || ! in_array( $taxonomies[0], apply_filters( 'woocommerce_change_term_counts', array( 'product_cat', 'product_tag' ) ) ) ) {
646
		return $terms;
647
	}
648
649
	$term_counts = $o_term_counts = get_transient( 'wc_term_counts' );
650
651
	foreach ( $terms as &$term ) {
652
		if ( is_object( $term ) ) {
653
			$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 );
654
655
			if ( $term_counts[ $term->term_id ] !== '' ) {
656
				$term->count = absint( $term_counts[ $term->term_id ] );
657
			}
658
		}
659
	}
660
661
	// Update transient
662
	if ( $term_counts != $o_term_counts ) {
663
		set_transient( 'wc_term_counts', $term_counts, DAY_IN_SECONDS * 30 );
664
	}
665
666
	return $terms;
667
}
668
add_filter( 'get_terms', 'wc_change_term_counts', 10, 2 );
669
670
/**
671
 * Return products in a given term, and cache value.
672
 *
673
 * To keep in sync, product_count will be cleared on "set_object_terms".
674
 *
675
 * @param int $term_id
676
 * @param string $taxonomy
677
 * @return array
678
 */
679
function wc_get_term_product_ids( $term_id, $taxonomy ) {
680
	$product_ids = get_woocommerce_term_meta( $term_id, 'product_ids', true );
681
682
	if ( false === $product_ids || ! is_array( $product_ids ) ) {
683
		$product_ids = get_objects_in_term( $term_id, $taxonomy );
684
		update_woocommerce_term_meta( $term_id, 'product_ids', $product_ids );
685
	}
686
687
	return $product_ids;
688
}
689
690
/**
691
 * When a post is updated and terms recounted (called by _update_post_term_count), clear the ids.
692
 * @param int    $object_id  Object ID.
693
 * @param array  $terms      An array of object terms.
694
 * @param array  $tt_ids     An array of term taxonomy IDs.
695
 * @param string $taxonomy   Taxonomy slug.
696
 * @param bool   $append     Whether to append new terms to the old terms.
697
 * @param array  $old_tt_ids Old array of term taxonomy IDs.
698
 */
699
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...
700
	foreach ( $old_tt_ids as $term_id ) {
701
		delete_woocommerce_term_meta( $term_id, 'product_ids' );
702
	}
703
	foreach ( $tt_ids as $term_id ) {
704
		delete_woocommerce_term_meta( $term_id, 'product_ids' );
705
	}
706
}
707
add_action( 'set_object_terms', 'wc_clear_term_product_ids', 10, 6 );
708