1 <?php
2 3 4 5 6 7 8 9 10 11
12
13 if ( ! defined( 'ABSPATH' ) ) exit;
14
15 16 17 18 19 20 21 22 23 24 25
26 function wc_get_product_terms( $product_id, $taxonomy, $args = array() ) {
27 if ( ! taxonomy_exists( $taxonomy ) ) {
28 return array();
29 }
30
31 if ( empty( $args['orderby'] ) && taxonomy_is_product_attribute( $taxonomy ) ) {
32 $args['orderby'] = wc_attribute_orderby( $taxonomy );
33 }
34
35
36 if ( ! empty( $args['orderby'] ) && $args['orderby'] === 'parent' ) {
37 $fields = isset( $args['fields'] ) ? $args['fields'] : 'all';
38
39
40 unset( $args['orderby'] );
41 unset( $args['fields'] );
42
43 $terms = wp_get_post_terms( $product_id, $taxonomy, $args );
44
45 usort( $terms, '_wc_get_product_terms_parent_usort_callback' );
46
47 switch ( $fields ) {
48 case 'names' :
49 $terms = wp_list_pluck( $terms, 'name' );
50 break;
51 case 'ids' :
52 $terms = wp_list_pluck( $terms, 'term_id' );
53 break;
54 case 'slugs' :
55 $terms = wp_list_pluck( $terms, 'slug' );
56 break;
57 }
58 } elseif ( ! empty( $args['orderby'] ) && $args['orderby'] === 'menu_order' ) {
59
60 $args['include'] = wp_get_post_terms( $product_id, $taxonomy, array( 'fields' => 'ids' ) );
61
62 if ( empty( $args['include'] ) ) {
63 $terms = array();
64 } else {
65
66 unset( $args['orderby'] );
67
68
69 $args['menu_order'] = isset( $args['order'] ) ? $args['order'] : 'ASC';
70 $args['hide_empty'] = isset( $args['hide_empty'] ) ? $args['hide_empty'] : 0;
71 $args['fields'] = isset( $args['fields'] ) ? $args['fields'] : 'names';
72
73
74 $args['fields'] = $args['fields'] === 'slugs' ? 'id=>slug' : $args['fields'];
75 $terms = get_terms( $taxonomy, $args );
76 }
77 } else {
78 $terms = wp_get_post_terms( $product_id, $taxonomy, $args );
79 }
80
81 return $terms;
82 }
83
84 85 86 87 88 89
90 function _wc_get_product_terms_parent_usort_callback( $a, $b ) {
91 if ( $a->parent === $b->parent ) {
92 return 0;
93 }
94 return ( $a->parent < $b->parent ) ? 1 : -1;
95 }
96
97 98 99 100 101 102 103 104 105 106 107
108 function wc_product_dropdown_categories( $args = array(), $deprecated_hierarchical = 1, $deprecated_show_uncategorized = 1, $deprecated_orderby = '' ) {
109 global $wp_query, $woocommerce;
110
111 if ( ! is_array( $args ) ) {
112 _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.' );
113
114 $args['show_counts'] = $args;
115 $args['hierarchical'] = $deprecated_hierarchical;
116 $args['show_uncategorized'] = $deprecated_show_uncategorized;
117 $args['orderby'] = $deprecated_orderby;
118 }
119
120 $defaults = array(
121 'pad_counts' => 1,
122 'show_counts' => 1,
123 'hierarchical' => 1,
124 'hide_empty' => 1,
125 'show_uncategorized' => 1,
126 'orderby' => 'name',
127 'selected' => isset( $wp_query->query['product_cat'] ) ? $wp_query->query['product_cat'] : '',
128 'menu_order' => false
129 );
130
131 $args = wp_parse_args( $args, $defaults );
132
133 if ( $args['orderby'] == 'order' ) {
134 $args['menu_order'] = 'asc';
135 $args['orderby'] = 'name';
136 }
137
138 $terms = get_terms( 'product_cat', $args );
139
140 if ( ! $terms )
141 return;
142
143 $output = "<select name='product_cat' id='dropdown_product_cat'>";
144 $output .= '<option value="" ' . selected( isset( $_GET['product_cat'] ) ? $_GET['product_cat'] : '', '', false ) . '>' . __( 'Select a category', 'woocommerce' ) . '</option>';
145 $output .= wc_walk_category_dropdown_tree( $terms, 0, $args );
146
147 if ( $args['show_uncategorized'] )
148 $output .= '<option value="0" ' . selected( isset( $_GET['product_cat'] ) ? $_GET['product_cat'] : '', '0', false ) . '>' . __( 'Uncategorized', 'woocommerce' ) . '</option>';
149
150 $output .="</select>";
151
152 echo $output;
153 }
154
155 156 157 158 159
160 function wc_walk_category_dropdown_tree() {
161 global $woocommerce;
162
163 if ( ! class_exists( 'WC_Product_Cat_Dropdown_Walker' ) )
164 include_once( WC()->plugin_path() . '/includes/walkers/class-product-cat-dropdown-walker.php' );
165
166 $args = func_get_args();
167
168
169 if ( empty( $args[2]['walker']) || !is_a($args[2]['walker'], 'Walker' ) )
170 $walker = new WC_Product_Cat_Dropdown_Walker;
171 else
172 $walker = $args[2]['walker'];
173
174 return call_user_func_array(array( &$walker, 'walk' ), $args );
175 }
176
177 178 179 180 181
182 function wc_taxonomy_metadata_wpdbfix() {
183 global $wpdb;
184 $termmeta_name = 'woocommerce_termmeta';
185 $itemmeta_name = 'woocommerce_order_itemmeta';
186
187 $wpdb->woocommerce_termmeta = $wpdb->prefix . $termmeta_name;
188 $wpdb->order_itemmeta = $wpdb->prefix . $itemmeta_name;
189
190 $wpdb->tables[] = 'woocommerce_termmeta';
191 $wpdb->tables[] = 'woocommerce_order_itemmeta';
192 }
193 add_action( 'init', 'wc_taxonomy_metadata_wpdbfix', 0 );
194 add_action( 'switch_blog', 'wc_taxonomy_metadata_wpdbfix', 0 );
195
196 197 198 199 200 201 202 203 204
205 function update_woocommerce_term_meta( $term_id, $meta_key, $meta_value, $prev_value = '' ) {
206 return update_metadata( 'woocommerce_term', $term_id, $meta_key, $meta_value, $prev_value );
207 }
208
209 210 211 212 213 214 215 216 217
218 function add_woocommerce_term_meta( $term_id, $meta_key, $meta_value, $unique = false ){
219 return add_metadata( 'woocommerce_term', $term_id, $meta_key, $meta_value, $unique );
220 }
221
222 223 224 225 226 227 228 229 230
231 function delete_woocommerce_term_meta( $term_id, $meta_key, $meta_value = '', $delete_all = false ) {
232 return delete_metadata( 'woocommerce_term', $term_id, $meta_key, $meta_value, $delete_all );
233 }
234
235 236 237 238 239 240 241 242
243 function get_woocommerce_term_meta( $term_id, $key, $single = true ) {
244 return get_metadata( 'woocommerce_term', $term_id, $key, $single );
245 }
246
247 248 249 250 251 252 253 254 255 256
257 function wc_reorder_terms( $the_term, $next_id, $taxonomy, $index = 0, $terms = null ) {
258
259 if( ! $terms ) $terms = get_terms($taxonomy, 'menu_order=ASC&hide_empty=0&parent=0' );
260 if( empty( $terms ) ) return $index;
261
262 $id = $the_term->term_id;
263
264 $term_in_level = false;
265
266 foreach ($terms as $term) {
267
268 if( $term->term_id == $id ) {
269 $term_in_level = true;
270 continue;
271 }
272
273 if(null !== $next_id && $term->term_id == $next_id) {
274 $index++;
275 $index = wc_set_term_order($id, $index, $taxonomy, true);
276 }
277
278
279 $index++;
280 $index = wc_set_term_order($term->term_id, $index, $taxonomy);
281
282
283 $children = get_terms($taxonomy, "parent={$term->term_id}&menu_order=ASC&hide_empty=0");
284 if( !empty($children) ) {
285 $index = wc_reorder_terms( $the_term, $next_id, $taxonomy, $index, $children );
286 }
287 }
288
289
290 if( $term_in_level && null === $next_id )
291 $index = wc_set_term_order($id, $index+1, $taxonomy, true);
292
293 return $index;
294 }
295
296 297 298 299 300 301 302 303 304
305 function wc_set_term_order( $term_id, $index, $taxonomy, $recursive = false ) {
306
307 $term_id = (int) $term_id;
308 $index = (int) $index;
309
310
311 if ( taxonomy_is_product_attribute( $taxonomy ) )
312 $meta_name = 'order_' . esc_attr( $taxonomy );
313 else
314 $meta_name = 'order';
315
316 update_woocommerce_term_meta( $term_id, $meta_name, $index );
317
318 if( ! $recursive ) return $index;
319
320 $children = get_terms($taxonomy, "parent=$term_id&menu_order=ASC&hide_empty=0");
321
322 foreach ( $children as $term ) {
323 $index ++;
324 $index = wc_set_term_order($term->term_id, $index, $taxonomy, true);
325 }
326
327 clean_term_cache( $term_id, $taxonomy );
328
329 return $index;
330 }
331
332 333 334 335 336 337 338 339 340 341 342 343 344
345 function wc_terms_clauses( $clauses, $taxonomies, $args ) {
346 global $wpdb, $woocommerce;
347
348
349 if ( isset( $args['menu_order'] ) && $args['menu_order'] == false ) {
350 return $clauses;
351 }
352
353
354 if ( isset( $args['orderby'] ) && $args['orderby'] != 'name' ) {
355 return $clauses;
356 }
357
358
359 if ( is_admin() && isset( $_GET['orderby'] ) ) {
360 return $clauses;
361 }
362
363
364 $found = false;
365 foreach ( (array) $taxonomies as $taxonomy ) {
366 if ( taxonomy_is_product_attribute( $taxonomy ) || in_array( $taxonomy, apply_filters( 'woocommerce_sortable_taxonomies', array( 'product_cat' ) ) ) ) {
367 $found = true;
368 break;
369 }
370 }
371 if ( ! $found ) {
372 return $clauses;
373 }
374
375
376 if ( ! empty( $taxonomies[0] ) && taxonomy_is_product_attribute( $taxonomies[0] ) ) {
377 $meta_name = 'order_' . esc_attr( $taxonomies[0] );
378 } else {
379 $meta_name = 'order';
380 }
381
382
383 if ( strpos( 'COUNT(*)', $clauses['fields'] ) === false ) {
384 $clauses['fields'] .= ', tm.* ';
385 }
386
387
388 $clauses['join'] .= " LEFT JOIN {$wpdb->woocommerce_termmeta} AS tm ON (t.term_id = tm.woocommerce_term_id AND tm.meta_key = '". $meta_name ."') ";
389
390
391 if ( ! isset( $args['menu_order'] ) || ! in_array( strtoupper($args['menu_order']), array('ASC', 'DESC')) ) {
392 $args['menu_order'] = 'ASC';
393 }
394
395 $order = "ORDER BY tm.meta_value+0 " . $args['menu_order'];
396
397 if ( $clauses['orderby'] ):
398 $clauses['orderby'] = str_replace('ORDER BY', $order . ',', $clauses['orderby'] );
399 else:
400 $clauses['orderby'] = $order;
401 endif;
402
403 return $clauses;
404 }
405 add_filter( 'terms_clauses', 'wc_terms_clauses', 10, 3 );
406
407 408 409 410 411 412 413 414
415 function _wc_term_recount( $terms, $taxonomy, $callback = true, $terms_are_term_taxonomy_ids = true ) {
416 global $wpdb;
417
418
419 if ( $callback ) {
420 _update_post_term_count( $terms, $taxonomy );
421 }
422
423
424 if ( get_option( 'woocommerce_hide_out_of_stock_items' ) == 'yes' ) {
425 $stock_join = "LEFT JOIN {$wpdb->postmeta} AS meta_stock ON posts.ID = meta_stock.post_id";
426 $stock_query = "
427 AND meta_stock.meta_key = '_stock_status'
428 AND meta_stock.meta_value = 'instock'
429 ";
430 } else {
431 $stock_query = $stock_join = '';
432 }
433
434
435 $count_query = "
436 SELECT COUNT( DISTINCT posts.ID ) FROM {$wpdb->posts} as posts
437 LEFT JOIN {$wpdb->postmeta} AS meta_visibility ON posts.ID = meta_visibility.post_id
438 LEFT JOIN {$wpdb->term_relationships} AS rel ON posts.ID=rel.object_ID
439 LEFT JOIN {$wpdb->term_taxonomy} AS tax USING( term_taxonomy_id )
440 LEFT JOIN {$wpdb->terms} AS term USING( term_id )
441 LEFT JOIN {$wpdb->postmeta} AS postmeta ON posts.ID = postmeta.post_id
442 $stock_join
443 WHERE post_status = 'publish'
444 AND post_type = 'product'
445 AND meta_visibility.meta_key = '_visibility'
446 AND meta_visibility.meta_value IN ( 'visible', 'catalog' )
447 $stock_query
448 ";
449
450
451 if ( ! $terms_are_term_taxonomy_ids ) {
452
453 $terms = array_filter( (array) array_keys( $terms ) );
454 } else {
455
456 $term_taxonomy_ids = $terms;
457 $terms = array();
458 foreach ( $term_taxonomy_ids as $term_taxonomy_id ) {
459 $term = get_term_by( 'term_taxonomy_id', $term_taxonomy_id, $taxonomy->name );
460 $terms[] = $term->term_id;
461 }
462 }
463
464
465 if ( ! $terms ) {
466 return;
467 }
468
469
470 if ( is_taxonomy_hierarchical( $taxonomy->name ) ) {
471 foreach ( $terms as $term_id ) {
472 $terms = array_merge( $terms, get_ancestors( $term_id, $taxonomy->name ) );
473 }
474 }
475
476
477 $terms = array_unique( $terms );
478
479
480 foreach ( $terms as $term_id ) {
481 $terms_to_count = array( absint( $term_id ) );
482
483 if ( is_taxonomy_hierarchical( $taxonomy->name ) ) {
484
485 if ( ( $children = get_term_children( $term_id, $taxonomy->name ) ) && ! is_wp_error( $children ) ) {
486 $terms_to_count = array_unique( array_map( 'absint', array_merge( $terms_to_count, $children ) ) );
487 }
488 }
489
490
491 $term_query = 'AND term_id IN ( ' . implode( ',', $terms_to_count ) . ' )';
492
493
494 $count = $wpdb->get_var( $count_query . $term_query );
495
496
497 update_woocommerce_term_meta( $term_id, 'product_count_' . $taxonomy->name, absint( $count ) );
498 }
499 }
500
501 502 503 504 505
506 function wc_recount_after_stock_change( $product_id ) {
507 if ( get_option( 'woocommerce_hide_out_of_stock_items' ) != 'yes' )
508 return;
509
510 $product_terms = get_the_terms( $product_id, 'product_cat' );
511
512 if ( $product_terms ) {
513 foreach ( $product_terms as $term )
514 $product_cats[ $term->term_id ] = $term->parent;
515
516 _wc_term_recount( $product_cats, get_taxonomy( 'product_cat' ), false, false );
517 }
518
519 $product_terms = get_the_terms( $product_id, 'product_tag' );
520
521 if ( $product_terms ) {
522 foreach ( $product_terms as $term )
523 $product_tags[ $term->term_id ] = $term->parent;
524
525 _wc_term_recount( $product_tags, get_taxonomy( 'product_tag' ), false, false );
526 }
527
528 delete_transient( 'wc_term_counts' );
529 }
530 add_action( 'woocommerce_product_set_stock_status', 'wc_recount_after_stock_change' );
531
532
533 534 535 536 537 538 539 540 541
542 function wc_change_term_counts( $terms, $taxonomies, $args ) {
543 if ( is_admin() || is_ajax() ) {
544 return $terms;
545 }
546
547 if ( ! isset( $taxonomies[0] ) || ! in_array( $taxonomies[0], apply_filters( 'woocommerce_change_term_counts', array( 'product_cat', 'product_tag' ) ) ) ) {
548 return $terms;
549 }
550
551 $term_counts = $o_term_counts = get_transient( 'wc_term_counts' );
552
553 foreach ( $terms as &$term ) {
554 if ( is_object( $term ) ) {
555 $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 );
556
557 if ( $term_counts[ $term->term_id ] !== '' ) {
558 $term->count = absint( $term_counts[ $term->term_id ] );
559 }
560 }
561 }
562
563
564 if ( $term_counts != $o_term_counts ) {
565 set_transient( 'wc_term_counts', $term_counts, YEAR_IN_SECONDS );
566 }
567
568 return $terms;
569 }
570 add_filter( 'get_terms', 'wc_change_term_counts', 10, 3 );
571