' ) { // Only check the category if the attribute is a category attribute. $primary_category = 'category' === $tax || 'product_cat' === $tax ? self::get_primary_cat( $product_id) : false; if ( $primary_category ) { $cats = $primary_category; } else { $args = array( 'taxonomy' => $tax, 'orderby' => 'parent', 'order' => 'DESC', ); // get the post-term ordered with the last child cat first $cats = wp_get_post_terms( $product_id, $tax, $args ); } $result = array(); if ( count( $cats ) === 0 ) { return ''; } // anonymous function to get the correct taxonomy string $cat_string = function ( $id ) use ( &$result, &$cat_string, $tax ) { // get the first term $term = get_term_by( 'id', $id, $tax, 'ARRAY_A' ); if ( ! $term ) { return; } // check if the term has a parent if ( $term['parent'] ) { // start the anonymous function again with the parent id $cat_string( $term['parent'] ); } // add the term name to the result $result[] = $term['name']; }; // activate the anonymous function with the term_id foreach( $cats as $cat ) { $cat_string( $cat->term_id ); } return implode( $separator, $result ); } /** * Single category path: Yoast/Rank Math primary breadcrumb, or the first assigned term's breadcrumb when no primary is set. * * Unlike {@see self::make_shop_taxonomies_string()}, this never merges paths from multiple categories. * * @param int|string $product_id Post ID. * @param string $tax Taxonomy slug. * @param string $separator Hierarchy separator. * * @since 3.22.0 * * @return string */ public static function make_main_product_category_string( $product_id, $tax = 'product_cat', $separator = ' > ' ) { $product_id = absint( $product_id ); if ( ! $product_id ) { return ''; } $term_id = 0; if ( 'category' === $tax || 'product_cat' === $tax ) { $primary = self::get_primary_cat( $product_id ); if ( $primary && isset( $primary[0]->term_id ) ) { $term_id = absint( $primary[0]->term_id ); } } if ( ! $term_id ) { $terms = wp_get_post_terms( $product_id, $tax, array( 'orderby' => 'term_id', 'order' => 'ASC', ) ); if ( is_wp_error( $terms ) || empty( $terms ) ) { return ''; } $term_id = absint( $terms[0]->term_id ); } return self::get_single_term_taxonomy_breadcrumb_string( $term_id, $tax, $separator ); } /** * Generates a string with all selected categories * * @param string $post_id * @param string $separator * * @return string */ public static function get_shop_categories( $post_id, $separator = ', ' ) { $return_string = ''; $args = array( 'taxonomy' => 'product_cat', 'orderby' => 'term_id', ); $cats = wp_get_post_terms( $post_id, 'product_cat', $args ); foreach ( $cats as $cat ) { $return_string .= $cat->name . $separator; } return rtrim( $return_string, $separator ); } /** * Returns the product category that is selected as primary (only when Yoast SEO or RankMath plugin is installed) * * @param string $product_id * * @return array|boolean */ public static function get_primary_cat( $product_id ) { $primary_cat_id = ''; if ( is_plugin_active( 'wordpress-seo/wp-seo.php' ) || is_plugin_active_for_network( 'wordpress-seo/wp-seo.php' ) || is_plugin_active( 'wordpress-seo-premium/wp-seo-premium.php' ) || is_plugin_active_for_network( 'wordpress-seo-premium/wp-seo-premium.php' ) ) { $primary_cat_id = get_post_meta( $product_id, '_yoast_wpseo_primary_product_cat', true ); } if ( is_plugin_active( 'seo-by-rank-math/rank-math.php' ) || is_plugin_active_for_network( 'seo-by-rank-math/rank-math.php' ) || is_plugin_active( 'seo-by-rank-math-pro/rank-math-pro.php' ) || is_plugin_active_for_network( 'seo-by-rank-math-pro/rank-math-pro.php' ) ) { $primary_cat_id = get_post_meta( $product_id, 'rank_math_primary_product_cat', true ); } if ( $primary_cat_id ) { $product_cat[0] = get_term( $primary_cat_id, 'product_cat' ); if ( isset( $product_cat[0]->term_id ) ) { return $product_cat; } } else { return false; } return false; } public static function get_shop_categories_list() { $args = array( 'hide_empty' => 0, 'taxonomy' => 'product_cat', 'hierarchical' => 1, 'orderby' => 'name', 'order' => 'ASC', // phpcs:ignore WordPressVIPMinimum.Performance.WPQueryParams.PostNotIn_exclude -- The exclude parameter is necessary to filter out specific categories from category mapping. This is a user-configurable feature and essential functionality. 'exclude' => apply_filters( 'wppfm_category_mapping_exclude', array() ), 'exclude_tree' => apply_filters( 'wppfm_category_mapping_exclude_tree', array() ), 'number' => absint( apply_filters( 'wppfm_category_mapping_max_categories', 0 ) ), 'child_of' => 0, ); // see https://developer.wordpress.org/reference/classes/wp_term_query/__construct/ for valid args $args = apply_filters( 'wppfm_category_mapping_args', $args ); return self::get_cat_hierarchy( 0, $args ); } /** * Builds root-to-leaf names for a single taxonomy term (same ordering as make_shop_taxonomies_string per term). * * @param int $term_id Term ID. * @param string $tax Taxonomy slug. * @param string $separator String used between segments. * * @return string */ private static function get_single_term_taxonomy_breadcrumb_string( $term_id, $tax, $separator ) { $result = array(); $term_id = absint( $term_id ); $cat_string = function ( $id ) use ( &$result, &$cat_string, $tax ) { $term = get_term_by( 'id', absint( $id ), $tax, 'ARRAY_A' ); if ( ! $term ) { return; } if ( ! empty( $term['parent'] ) ) { $cat_string( $term['parent'] ); } $result[] = $term['name']; }; $cat_string( $term_id ); return implode( $separator, $result ); } private static function get_cat_hierarchy( $parent, $args ) { $cats = get_categories( $args ); $ret = new stdClass; foreach ( $cats as $cat ) { if ( $cat->parent == $parent ) { $id = $cat->cat_ID; $ret->$id = $cat; $ret->$id->children = self::get_cat_hierarchy( $id, $args ); } } return $ret; } } // end of WPPFM_Taxonomies_Class endif;