_wc_term_recount() │ WC 1.0 Function for recounting product terms, ignoring hidden products.
This is used as the update_count_callback for the Product Category, Product Tag, and Product Brand taxonomies. By default, it actually calculates two (possibly different) counts for each term, which it stores in two different places. The first count is the one done by WordPress itself, and is based on the status of the objects that are assigned the terms. In this case, only products with the publish status are counted. This count is stored in the wp_term_taxonomy table in the count field.
The second count is based on WooCommerce-specific characteristics. In addition to the publish status requirement, products are only counted if they are considered visible in the catalog. This count is stored in the wp_termmeta table. The wc_change_term_counts function is used to override the first count with the second count in some circumstances.
Since the first count only needs to be recalculated when a product status is changed in some way, it can sometimes be skipped (thus avoiding some potentially expensive queries). Setting the $callback parameter to false skips the first count.
Internal function — this function is designed to be used by the kernel itself. It is not recommended to use this function in your code.
Returns
null. Nothing (null).
Usage
_wc_term_recount( $terms, $taxonomy, $callback, $terms_are_term_taxonomy_ids );
$terms(array) (required)
List of terms. For legacy reasons, this can either be a list of taxonomy term IDs or an associative array in the format of term ID > parent term ID.
$taxonomy(WP_Taxonomy ) (required)
The relevant taxonomy.
$callback(true|false)
Whether to also recalculate the term counts using the WP Core callback.
Default: true
$terms_are_term_taxonomy_ids(true|false)
Flag to indicate which format the list of terms is in.
Default: true, which indicates that it is a list of taxonomy term IDs
_wc_term_recount() wc term recount code
WC 10.3.3
function _wc_term_recount( $terms, $taxonomy, $callback = true, $terms_are_term_taxonomy_ids = true ) {
global $wpdb;
/**
* Filter to allow/prevent recounting of terms as it could be expensive.
* A likely scenario for this is when bulk importing products. We could
* then prevent it from recounting per product but instead recount it once
* when import is done. Of course this means the import logic has to support this.
*
* @since 5.2
* @param bool
*/
if ( ! apply_filters( 'woocommerce_product_recount_terms', true ) ) {
return;
}
if ( true === $terms_are_term_taxonomy_ids ) {
$taxonomy_term_ids = $terms;
$term_ids = array_map(
function ( $term_taxonomy_id ) use ( $taxonomy ) {
$term = get_term_by( 'term_taxonomy_id', $term_taxonomy_id, $taxonomy->name );
return $term instanceof WP_Term ? $term->term_id : null;
},
$terms
);
} else {
$taxonomy_term_ids = array(); // Defer querying these until the callback check.
$term_ids = array_keys( $terms );
}
$term_ids = array_unique( array_filter( $term_ids ) );
$taxonomy_term_ids = array_unique( array_filter( $taxonomy_term_ids ) );
// Exit if we have no terms to count.
if ( empty( $term_ids ) ) {
return;
}
// Standard WP callback for calculating post term counts.
if ( $callback ) {
if ( count( $taxonomy_term_ids ) < 1 ) {
$taxonomy_term_ids = array_map(
function ( $term_id ) use ( $taxonomy ) {
$term = get_term_by( 'term_id', $term_id, $taxonomy->name );
return $term instanceof WP_Term ? $term->term_taxonomy_id : null;
},
$term_ids
);
}
_update_post_term_count( $taxonomy_term_ids, $taxonomy );
}
$exclude_term_ids = array();
$product_visibility_term_ids = wc_get_product_visibility_term_ids();
if ( $product_visibility_term_ids['exclude-from-catalog'] ) {
$exclude_term_ids[] = $product_visibility_term_ids['exclude-from-catalog'];
}
if (
'yes' === get_option( 'woocommerce_hide_out_of_stock_items' )
&& $product_visibility_term_ids[ ProductStockStatus::OUT_OF_STOCK ]
) {
$exclude_term_ids[] = $product_visibility_term_ids[ ProductStockStatus::OUT_OF_STOCK ];
}
$query = array(
'fields' => "
SELECT COUNT( DISTINCT ID ) FROM {$wpdb->posts} p
",
'join' => '',
'where' => "
WHERE 1=1
AND p.post_status = 'publish'
AND p.post_type = 'product'
",
);
if ( count( $exclude_term_ids ) ) {
$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';
$query['where'] .= ' AND exclude_join.object_id IS NULL';
}
// Ancestors need counting.
if ( is_taxonomy_hierarchical( $taxonomy->name ) ) {
foreach ( $term_ids as $term_id ) {
$term_ids = array_merge( $term_ids, get_ancestors( $term_id, $taxonomy->name ) );
}
$term_ids = array_unique( $term_ids );
}
// Count the terms.
foreach ( $term_ids as $term_id ) {
$terms_to_count = array( absint( $term_id ) );
if ( is_taxonomy_hierarchical( $taxonomy->name ) ) {
// We need to get the $term's hierarchy so we can count its children too.
$children = get_term_children( $term_id, $taxonomy->name );
if ( $children && ! is_wp_error( $children ) ) {
$terms_to_count = array_unique( array_map( 'absint', array_merge( $terms_to_count, $children ) ) );
}
}
// Generate term query.
$term_query = $query;
$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';
// Get the count.
// phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
$count = $wpdb->get_var( implode( ' ', $term_query ) );
// Update the count.
update_term_meta( $term_id, 'product_count_' . $taxonomy->name, absint( $count ) );
}
delete_transient( 'wc_term_counts' );
}