Keine Beschreibung

wc-term-functions.php 21KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693
  1. <?php
  2. /**
  3. * WooCommerce Terms
  4. *
  5. * Functions for handling terms/term meta.
  6. *
  7. * @package WooCommerce\Functions
  8. * @version 2.1.0
  9. */
  10. defined( 'ABSPATH' ) || exit;
  11. /**
  12. * Change get terms defaults for attributes to order by the sorting setting, or default to menu_order for sortable taxonomies.
  13. *
  14. * @since 3.6.0 Sorting options are now set as the default automatically, so you no longer have to request to orderby menu_order.
  15. *
  16. * @param array $defaults An array of default get_terms() arguments.
  17. * @param array $taxonomies An array of taxonomies.
  18. * @return array
  19. */
  20. function wc_change_get_terms_defaults( $defaults, $taxonomies ) {
  21. if ( is_array( $taxonomies ) && 1 < count( $taxonomies ) ) {
  22. return $defaults;
  23. }
  24. $taxonomy = is_array( $taxonomies ) ? (string) current( $taxonomies ) : $taxonomies;
  25. $orderby = 'name';
  26. if ( taxonomy_is_product_attribute( $taxonomy ) ) {
  27. $orderby = wc_attribute_orderby( $taxonomy );
  28. } elseif ( in_array( $taxonomy, apply_filters( 'woocommerce_sortable_taxonomies', array( 'product_cat' ) ), true ) ) {
  29. $orderby = 'menu_order';
  30. }
  31. // Change defaults. Invalid values will be changed later @see wc_change_pre_get_terms.
  32. // These are in place so we know if a specific order was requested.
  33. switch ( $orderby ) {
  34. case 'menu_order':
  35. case 'name_num':
  36. case 'parent':
  37. $defaults['orderby'] = $orderby;
  38. break;
  39. }
  40. return $defaults;
  41. }
  42. add_filter( 'get_terms_defaults', 'wc_change_get_terms_defaults', 10, 2 );
  43. /**
  44. * Adds support to get_terms for menu_order argument.
  45. *
  46. * @since 3.6.0
  47. * @param WP_Term_Query $terms_query Instance of WP_Term_Query.
  48. */
  49. function wc_change_pre_get_terms( $terms_query ) {
  50. $args = &$terms_query->query_vars;
  51. // Put back valid orderby values.
  52. if ( 'menu_order' === $args['orderby'] ) {
  53. $args['orderby'] = 'name';
  54. $args['force_menu_order_sort'] = true;
  55. }
  56. if ( 'name_num' === $args['orderby'] ) {
  57. $args['orderby'] = 'name';
  58. $args['force_numeric_name'] = true;
  59. }
  60. // When COUNTING, disable custom sorting.
  61. if ( 'count' === $args['fields'] ) {
  62. return;
  63. }
  64. // Support menu_order arg used in previous versions.
  65. if ( ! empty( $args['menu_order'] ) ) {
  66. $args['order'] = 'DESC' === strtoupper( $args['menu_order'] ) ? 'DESC' : 'ASC';
  67. $args['force_menu_order_sort'] = true;
  68. }
  69. if ( ! empty( $args['force_menu_order_sort'] ) ) {
  70. $args['orderby'] = 'meta_value_num';
  71. $args['meta_key'] = 'order'; // phpcs:ignore
  72. $terms_query->meta_query->parse_query_vars( $args );
  73. }
  74. }
  75. add_action( 'pre_get_terms', 'wc_change_pre_get_terms', 10, 1 );
  76. /**
  77. * Adjust term query to handle custom sorting parameters.
  78. *
  79. * @param array $clauses Clauses.
  80. * @param array $taxonomies Taxonomies.
  81. * @param array $args Arguments.
  82. * @return array
  83. */
  84. function wc_terms_clauses( $clauses, $taxonomies, $args ) {
  85. global $wpdb;
  86. // No need to filter when counting.
  87. if ( strpos( $clauses['fields'], 'COUNT(*)' ) !== false ) {
  88. return $clauses;
  89. }
  90. // Force numeric sort if using name_num custom sorting param.
  91. if ( ! empty( $args['force_numeric_name'] ) ) {
  92. $clauses['orderby'] = str_replace( 'ORDER BY t.name', 'ORDER BY t.name+0', $clauses['orderby'] );
  93. }
  94. // For sorting, force left join in case order meta is missing.
  95. if ( ! empty( $args['force_menu_order_sort'] ) ) {
  96. $clauses['join'] = str_replace( "INNER JOIN {$wpdb->termmeta} ON ( t.term_id = {$wpdb->termmeta}.term_id )", "LEFT JOIN {$wpdb->termmeta} ON ( t.term_id = {$wpdb->termmeta}.term_id AND {$wpdb->termmeta}.meta_key='order')", $clauses['join'] );
  97. $clauses['where'] = str_replace( "{$wpdb->termmeta}.meta_key = 'order'", "( {$wpdb->termmeta}.meta_key = 'order' OR {$wpdb->termmeta}.meta_key IS NULL )", $clauses['where'] );
  98. $clauses['orderby'] = 'DESC' === $args['order'] ? str_replace( 'meta_value+0', 'meta_value+0 DESC, t.name', $clauses['orderby'] ) : str_replace( 'meta_value+0', 'meta_value+0 ASC, t.name', $clauses['orderby'] );
  99. }
  100. return $clauses;
  101. }
  102. add_filter( 'terms_clauses', 'wc_terms_clauses', 99, 3 );
  103. /**
  104. * Helper to get cached object terms and filter by field using wp_list_pluck().
  105. * Works as a cached alternative for wp_get_post_terms() and wp_get_object_terms().
  106. *
  107. * @since 3.0.0
  108. * @param int $object_id Object ID.
  109. * @param string $taxonomy Taxonomy slug.
  110. * @param string $field Field name.
  111. * @param string $index_key Index key name.
  112. * @return array
  113. */
  114. function wc_get_object_terms( $object_id, $taxonomy, $field = null, $index_key = null ) {
  115. // Test if terms exists. get_the_terms() return false when it finds no terms.
  116. $terms = get_the_terms( $object_id, $taxonomy );
  117. if ( ! $terms || is_wp_error( $terms ) ) {
  118. return array();
  119. }
  120. return is_null( $field ) ? $terms : wp_list_pluck( $terms, $field, $index_key );
  121. }
  122. /**
  123. * Cached version of wp_get_post_terms().
  124. * This is a private function (internal use ONLY).
  125. *
  126. * @since 3.0.0
  127. * @param int $product_id Product ID.
  128. * @param string $taxonomy Taxonomy slug.
  129. * @param array $args Query arguments.
  130. * @return array
  131. */
  132. function _wc_get_cached_product_terms( $product_id, $taxonomy, $args = array() ) {
  133. $cache_key = 'wc_' . $taxonomy . md5( wp_json_encode( $args ) );
  134. $cache_group = WC_Cache_Helper::get_cache_prefix( 'product_' . $product_id ) . $product_id;
  135. $terms = wp_cache_get( $cache_key, $cache_group );
  136. if ( false !== $terms ) {
  137. return $terms;
  138. }
  139. $terms = wp_get_post_terms( $product_id, $taxonomy, $args );
  140. wp_cache_add( $cache_key, $terms, $cache_group );
  141. return $terms;
  142. }
  143. /**
  144. * Wrapper used to get terms for a product.
  145. *
  146. * @param int $product_id Product ID.
  147. * @param string $taxonomy Taxonomy slug.
  148. * @param array $args Query arguments.
  149. * @return array
  150. */
  151. function wc_get_product_terms( $product_id, $taxonomy, $args = array() ) {
  152. if ( ! taxonomy_exists( $taxonomy ) ) {
  153. return array();
  154. }
  155. return apply_filters( 'woocommerce_get_product_terms', _wc_get_cached_product_terms( $product_id, $taxonomy, $args ), $product_id, $taxonomy, $args );
  156. }
  157. /**
  158. * Sort by name (numeric).
  159. *
  160. * @param WP_Post $a First item to compare.
  161. * @param WP_Post $b Second item to compare.
  162. * @return int
  163. */
  164. function _wc_get_product_terms_name_num_usort_callback( $a, $b ) {
  165. $a_name = (float) $a->name;
  166. $b_name = (float) $b->name;
  167. if ( abs( $a_name - $b_name ) < 0.001 ) {
  168. return 0;
  169. }
  170. return ( $a_name < $b_name ) ? -1 : 1;
  171. }
  172. /**
  173. * Sort by parent.
  174. *
  175. * @param WP_Post $a First item to compare.
  176. * @param WP_Post $b Second item to compare.
  177. * @return int
  178. */
  179. function _wc_get_product_terms_parent_usort_callback( $a, $b ) {
  180. if ( $a->parent === $b->parent ) {
  181. return 0;
  182. }
  183. return ( $a->parent < $b->parent ) ? 1 : -1;
  184. }
  185. /**
  186. * WooCommerce Dropdown categories.
  187. *
  188. * @param array $args Args to control display of dropdown.
  189. */
  190. function wc_product_dropdown_categories( $args = array() ) {
  191. global $wp_query;
  192. $args = wp_parse_args(
  193. $args,
  194. array(
  195. 'pad_counts' => 1,
  196. 'show_count' => 1,
  197. 'hierarchical' => 1,
  198. 'hide_empty' => 1,
  199. 'show_uncategorized' => 1,
  200. 'orderby' => 'name',
  201. 'selected' => isset( $wp_query->query_vars['product_cat'] ) ? $wp_query->query_vars['product_cat'] : '',
  202. 'show_option_none' => __( 'Select a category', 'woocommerce' ),
  203. 'option_none_value' => '',
  204. 'value_field' => 'slug',
  205. 'taxonomy' => 'product_cat',
  206. 'name' => 'product_cat',
  207. 'class' => 'dropdown_product_cat',
  208. )
  209. );
  210. if ( 'order' === $args['orderby'] ) {
  211. $args['orderby'] = 'meta_value_num';
  212. $args['meta_key'] = 'order'; // phpcs:ignore
  213. }
  214. wp_dropdown_categories( $args );
  215. }
  216. /**
  217. * Custom walker for Product Categories.
  218. *
  219. * Previously used by wc_product_dropdown_categories, but wp_dropdown_categories has been fixed in core.
  220. *
  221. * @param mixed ...$args Variable number of parameters to be passed to the walker.
  222. * @return mixed
  223. */
  224. function wc_walk_category_dropdown_tree( ...$args ) {
  225. if ( ! class_exists( 'WC_Product_Cat_Dropdown_Walker', false ) ) {
  226. include_once WC()->plugin_path() . '/includes/walkers/class-wc-product-cat-dropdown-walker.php';
  227. }
  228. // The user's options are the third parameter.
  229. if ( empty( $args[2]['walker'] ) || ! is_a( $args[2]['walker'], 'Walker' ) ) {
  230. $walker = new WC_Product_Cat_Dropdown_Walker();
  231. } else {
  232. $walker = $args[2]['walker'];
  233. }
  234. return $walker->walk( ...$args );
  235. }
  236. /**
  237. * Migrate data from WC term meta to WP term meta.
  238. *
  239. * When the database is updated to support term meta, migrate WC term meta data across.
  240. * We do this when the new version is >= 34370, and the old version is < 34370 (34370 is when term meta table was added).
  241. *
  242. * @param string $wp_db_version The new $wp_db_version.
  243. * @param string $wp_current_db_version The old (current) $wp_db_version.
  244. */
  245. function wc_taxonomy_metadata_migrate_data( $wp_db_version, $wp_current_db_version ) {
  246. if ( $wp_db_version >= 34370 && $wp_current_db_version < 34370 ) {
  247. global $wpdb;
  248. 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;" ) ) {
  249. $wpdb->query( "DROP TABLE IF EXISTS {$wpdb->prefix}woocommerce_termmeta" );
  250. }
  251. }
  252. }
  253. add_action( 'wp_upgrade', 'wc_taxonomy_metadata_migrate_data', 10, 2 );
  254. /**
  255. * Move a term before the a given element of its hierarchy level.
  256. *
  257. * @param int $the_term Term ID.
  258. * @param int $next_id The id of the next sibling element in save hierarchy level.
  259. * @param string $taxonomy Taxnomy.
  260. * @param int $index Term index (default: 0).
  261. * @param mixed $terms List of terms. (default: null).
  262. * @return int
  263. */
  264. function wc_reorder_terms( $the_term, $next_id, $taxonomy, $index = 0, $terms = null ) {
  265. if ( ! $terms ) {
  266. $terms = get_terms( $taxonomy, 'hide_empty=0&parent=0&menu_order=ASC' );
  267. }
  268. if ( empty( $terms ) ) {
  269. return $index;
  270. }
  271. $id = intval( $the_term->term_id );
  272. $term_in_level = false; // Flag: is our term to order in this level of terms.
  273. foreach ( $terms as $term ) {
  274. $term_id = intval( $term->term_id );
  275. if ( $term_id === $id ) { // Our term to order, we skip.
  276. $term_in_level = true;
  277. continue; // Our term to order, we skip.
  278. }
  279. // the nextid of our term to order, lets move our term here.
  280. if ( null !== $next_id && $term_id === $next_id ) {
  281. $index++;
  282. $index = wc_set_term_order( $id, $index, $taxonomy, true );
  283. }
  284. // Set order.
  285. $index++;
  286. $index = wc_set_term_order( $term_id, $index, $taxonomy );
  287. /**
  288. * After a term has had it's order set.
  289. */
  290. do_action( 'woocommerce_after_set_term_order', $term, $index, $taxonomy );
  291. // If that term has children we walk through them.
  292. $children = get_terms( $taxonomy, "parent={$term_id}&hide_empty=0&menu_order=ASC" );
  293. if ( ! empty( $children ) ) {
  294. $index = wc_reorder_terms( $the_term, $next_id, $taxonomy, $index, $children );
  295. }
  296. }
  297. // No nextid meaning our term is in last position.
  298. if ( $term_in_level && null === $next_id ) {
  299. $index = wc_set_term_order( $id, $index + 1, $taxonomy, true );
  300. }
  301. return $index;
  302. }
  303. /**
  304. * Set the sort order of a term.
  305. *
  306. * @param int $term_id Term ID.
  307. * @param int $index Index.
  308. * @param string $taxonomy Taxonomy.
  309. * @param bool $recursive Recursive (default: false).
  310. * @return int
  311. */
  312. function wc_set_term_order( $term_id, $index, $taxonomy, $recursive = false ) {
  313. $term_id = (int) $term_id;
  314. $index = (int) $index;
  315. update_term_meta( $term_id, 'order', $index );
  316. if ( ! $recursive ) {
  317. return $index;
  318. }
  319. $children = get_terms( $taxonomy, "parent=$term_id&hide_empty=0&menu_order=ASC" );
  320. foreach ( $children as $term ) {
  321. $index++;
  322. $index = wc_set_term_order( $term->term_id, $index, $taxonomy, true );
  323. }
  324. clean_term_cache( $term_id, $taxonomy );
  325. return $index;
  326. }
  327. /**
  328. * Function for recounting product terms, ignoring hidden products.
  329. *
  330. * @param array $terms List of terms.
  331. * @param object $taxonomy Taxonomy.
  332. * @param bool $callback Callback.
  333. * @param bool $terms_are_term_taxonomy_ids If terms are from term_taxonomy_id column.
  334. */
  335. function _wc_term_recount( $terms, $taxonomy, $callback = true, $terms_are_term_taxonomy_ids = true ) {
  336. global $wpdb;
  337. /**
  338. * Filter to allow/prevent recounting of terms as it could be expensive.
  339. * A likely scenario for this is when bulk importing products. We could
  340. * then prevent it from recounting per product but instead recount it once
  341. * when import is done. Of course this means the import logic has to support this.
  342. *
  343. * @since 5.2
  344. * @param bool
  345. */
  346. if ( ! apply_filters( 'woocommerce_product_recount_terms', '__return_true' ) ) {
  347. return;
  348. }
  349. // Standard callback.
  350. if ( $callback ) {
  351. _update_post_term_count( $terms, $taxonomy );
  352. }
  353. $exclude_term_ids = array();
  354. $product_visibility_term_ids = wc_get_product_visibility_term_ids();
  355. if ( $product_visibility_term_ids['exclude-from-catalog'] ) {
  356. $exclude_term_ids[] = $product_visibility_term_ids['exclude-from-catalog'];
  357. }
  358. if ( 'yes' === get_option( 'woocommerce_hide_out_of_stock_items' ) && $product_visibility_term_ids['outofstock'] ) {
  359. $exclude_term_ids[] = $product_visibility_term_ids['outofstock'];
  360. }
  361. $query = array(
  362. 'fields' => "
  363. SELECT COUNT( DISTINCT ID ) FROM {$wpdb->posts} p
  364. ",
  365. 'join' => '',
  366. 'where' => "
  367. WHERE 1=1
  368. AND p.post_status = 'publish'
  369. AND p.post_type = 'product'
  370. ",
  371. );
  372. if ( count( $exclude_term_ids ) ) {
  373. $query['join'] .= " LEFT JOIN ( SELECT object_id FROM {$wpdb->term_relationships} WHERE term_taxonomy_id IN ( " . implode( ',', array_map( 'absint', $exclude_term_ids ) ) . ' ) ) AS exclude_join ON exclude_join.object_id = p.ID';
  374. $query['where'] .= ' AND exclude_join.object_id IS NULL';
  375. }
  376. // Pre-process term taxonomy ids.
  377. if ( ! $terms_are_term_taxonomy_ids ) {
  378. // We passed in an array of TERMS in format id=>parent.
  379. $terms = array_filter( (array) array_keys( $terms ) );
  380. } else {
  381. // If we have term taxonomy IDs we need to get the term ID.
  382. $term_taxonomy_ids = $terms;
  383. $terms = array();
  384. foreach ( $term_taxonomy_ids as $term_taxonomy_id ) {
  385. $term = get_term_by( 'term_taxonomy_id', $term_taxonomy_id, $taxonomy->name );
  386. $terms[] = $term->term_id;
  387. }
  388. }
  389. // Exit if we have no terms to count.
  390. if ( empty( $terms ) ) {
  391. return;
  392. }
  393. // Ancestors need counting.
  394. if ( is_taxonomy_hierarchical( $taxonomy->name ) ) {
  395. foreach ( $terms as $term_id ) {
  396. $terms = array_merge( $terms, get_ancestors( $term_id, $taxonomy->name ) );
  397. }
  398. }
  399. // Unique terms only.
  400. $terms = array_unique( $terms );
  401. // Count the terms.
  402. foreach ( $terms as $term_id ) {
  403. $terms_to_count = array( absint( $term_id ) );
  404. if ( is_taxonomy_hierarchical( $taxonomy->name ) ) {
  405. // We need to get the $term's hierarchy so we can count its children too.
  406. $children = get_term_children( $term_id, $taxonomy->name );
  407. if ( $children && ! is_wp_error( $children ) ) {
  408. $terms_to_count = array_unique( array_map( 'absint', array_merge( $terms_to_count, $children ) ) );
  409. }
  410. }
  411. // Generate term query.
  412. $term_query = $query;
  413. $term_query['join'] .= " INNER JOIN ( SELECT object_id FROM {$wpdb->term_relationships} INNER JOIN {$wpdb->term_taxonomy} using( term_taxonomy_id ) WHERE term_id IN ( " . implode( ',', array_map( 'absint', $terms_to_count ) ) . ' ) ) AS include_join ON include_join.object_id = p.ID';
  414. // Get the count.
  415. // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
  416. $count = $wpdb->get_var( implode( ' ', $term_query ) );
  417. // Update the count.
  418. update_term_meta( $term_id, 'product_count_' . $taxonomy->name, absint( $count ) );
  419. }
  420. delete_transient( 'wc_term_counts' );
  421. }
  422. /**
  423. * Recount terms after the stock amount changes.
  424. *
  425. * @param int $product_id Product ID.
  426. */
  427. function wc_recount_after_stock_change( $product_id ) {
  428. if ( 'yes' !== get_option( 'woocommerce_hide_out_of_stock_items' ) ) {
  429. return;
  430. }
  431. _wc_recount_terms_by_product( $product_id );
  432. }
  433. add_action( 'woocommerce_product_set_stock_status', 'wc_recount_after_stock_change' );
  434. /**
  435. * Overrides the original term count for product categories and tags with the product count.
  436. * that takes catalog visibility into account.
  437. *
  438. * @param array $terms List of terms.
  439. * @param string|array $taxonomies Single taxonomy or list of taxonomies.
  440. * @return array
  441. */
  442. function wc_change_term_counts( $terms, $taxonomies ) {
  443. if ( is_admin() || is_ajax() ) {
  444. return $terms;
  445. }
  446. if ( ! isset( $taxonomies[0] ) || ! in_array( $taxonomies[0], apply_filters( 'woocommerce_change_term_counts', array( 'product_cat', 'product_tag' ) ), true ) ) {
  447. return $terms;
  448. }
  449. $o_term_counts = get_transient( 'wc_term_counts' );
  450. $term_counts = $o_term_counts;
  451. foreach ( $terms as &$term ) {
  452. if ( is_object( $term ) ) {
  453. $term_counts[ $term->term_id ] = isset( $term_counts[ $term->term_id ] ) ? $term_counts[ $term->term_id ] : get_term_meta( $term->term_id, 'product_count_' . $taxonomies[0], true );
  454. if ( '' !== $term_counts[ $term->term_id ] ) {
  455. $term->count = absint( $term_counts[ $term->term_id ] );
  456. }
  457. }
  458. }
  459. // Update transient.
  460. if ( $term_counts !== $o_term_counts ) {
  461. set_transient( 'wc_term_counts', $term_counts, DAY_IN_SECONDS * 30 );
  462. }
  463. return $terms;
  464. }
  465. add_filter( 'get_terms', 'wc_change_term_counts', 10, 2 );
  466. /**
  467. * Return products in a given term, and cache value.
  468. *
  469. * To keep in sync, product_count will be cleared on "set_object_terms".
  470. *
  471. * @param int $term_id Term ID.
  472. * @param string $taxonomy Taxonomy.
  473. * @return array
  474. */
  475. function wc_get_term_product_ids( $term_id, $taxonomy ) {
  476. $product_ids = get_term_meta( $term_id, 'product_ids', true );
  477. if ( false === $product_ids || ! is_array( $product_ids ) ) {
  478. $product_ids = get_objects_in_term( $term_id, $taxonomy );
  479. update_term_meta( $term_id, 'product_ids', $product_ids );
  480. }
  481. return $product_ids;
  482. }
  483. /**
  484. * When a post is updated and terms recounted (called by _update_post_term_count), clear the ids.
  485. *
  486. * @param int $object_id Object ID.
  487. * @param array $terms An array of object terms.
  488. * @param array $tt_ids An array of term taxonomy IDs.
  489. * @param string $taxonomy Taxonomy slug.
  490. * @param bool $append Whether to append new terms to the old terms.
  491. * @param array $old_tt_ids Old array of term taxonomy IDs.
  492. */
  493. function wc_clear_term_product_ids( $object_id, $terms, $tt_ids, $taxonomy, $append, $old_tt_ids ) {
  494. foreach ( $old_tt_ids as $term_id ) {
  495. delete_term_meta( $term_id, 'product_ids' );
  496. }
  497. foreach ( $tt_ids as $term_id ) {
  498. delete_term_meta( $term_id, 'product_ids' );
  499. }
  500. }
  501. add_action( 'set_object_terms', 'wc_clear_term_product_ids', 10, 6 );
  502. /**
  503. * Get full list of product visibilty term ids.
  504. *
  505. * @since 3.0.0
  506. * @return int[]
  507. */
  508. function wc_get_product_visibility_term_ids() {
  509. if ( ! taxonomy_exists( 'product_visibility' ) ) {
  510. wc_doing_it_wrong( __FUNCTION__, 'wc_get_product_visibility_term_ids should not be called before taxonomies are registered (woocommerce_after_register_post_type action).', '3.1' );
  511. return array();
  512. }
  513. return array_map(
  514. 'absint',
  515. wp_parse_args(
  516. wp_list_pluck(
  517. get_terms(
  518. array(
  519. 'taxonomy' => 'product_visibility',
  520. 'hide_empty' => false,
  521. )
  522. ),
  523. 'term_taxonomy_id',
  524. 'name'
  525. ),
  526. array(
  527. 'exclude-from-catalog' => 0,
  528. 'exclude-from-search' => 0,
  529. 'featured' => 0,
  530. 'outofstock' => 0,
  531. 'rated-1' => 0,
  532. 'rated-2' => 0,
  533. 'rated-3' => 0,
  534. 'rated-4' => 0,
  535. 'rated-5' => 0,
  536. )
  537. )
  538. );
  539. }
  540. /**
  541. * Recounts all terms.
  542. *
  543. * @since 5.2
  544. * @return void
  545. */
  546. function wc_recount_all_terms() {
  547. $product_cats = get_terms(
  548. 'product_cat',
  549. array(
  550. 'hide_empty' => false,
  551. 'fields' => 'id=>parent',
  552. )
  553. );
  554. _wc_term_recount( $product_cats, get_taxonomy( 'product_cat' ), true, false );
  555. $product_tags = get_terms(
  556. 'product_tag',
  557. array(
  558. 'hide_empty' => false,
  559. 'fields' => 'id=>parent',
  560. )
  561. );
  562. _wc_term_recount( $product_tags, get_taxonomy( 'product_tag' ), true, false );
  563. }
  564. /**
  565. * Recounts terms by product.
  566. *
  567. * @since 5.2
  568. * @param int $product_id The ID of the product.
  569. * @return void
  570. */
  571. function _wc_recount_terms_by_product( $product_id = '' ) {
  572. if ( empty( $product_id ) ) {
  573. return;
  574. }
  575. $product_terms = get_the_terms( $product_id, 'product_cat' );
  576. if ( $product_terms ) {
  577. $product_cats = array();
  578. foreach ( $product_terms as $term ) {
  579. $product_cats[ $term->term_id ] = $term->parent;
  580. }
  581. _wc_term_recount( $product_cats, get_taxonomy( 'product_cat' ), false, false );
  582. }
  583. $product_terms = get_the_terms( $product_id, 'product_tag' );
  584. if ( $product_terms ) {
  585. $product_tags = array();
  586. foreach ( $product_terms as $term ) {
  587. $product_tags[ $term->term_id ] = $term->parent;
  588. }
  589. _wc_term_recount( $product_tags, get_taxonomy( 'product_tag' ), false, false );
  590. }
  591. }