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 ) { |
|
|
|
|
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 ) { |
|
|
|
|
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 ) { |
|
|
|
|
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 ) { |
|
|
|
|
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 ) { |
|
|
|
|
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 ) { |
|
|
|
|
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 ) { |
|
|
|
|
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 ) { |
|
|
|
|
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
|
|
|
|
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.