如何在WooCommerce中关联类别与属性术语以创建头部菜单
关联WooCommerce产品分类与属性术语的解决方案
针对你需要构建的层级头部菜单需求,我整理了两种可行的方案,帮你把产品分类(父子层级)和对应的属性术语关联起来,直接就能用到你的项目里:
核心逻辑说明
WooCommerce里,产品分类(product_cat)和属性术语(pa_xxx类型的 taxonomy)都存在wp_terms表中,两者通过产品作为中间关联层:每个产品既属于某个分类,又关联了对应的属性术语,所以我们可以通过产品ID(object_id)把分类和属性术语连接起来。
方案一:直接SQL查询(适合快速获取数据的场景)
如果你需要直接从数据库拉取某个子分类下的所有属性术语,这个去重后的查询很实用,只会返回该分类下产品实际用到的属性术语:
SELECT DISTINCT t.term_id, t.name, t.slug, tt.taxonomy FROM wp_terms t JOIN wp_term_taxonomy tt ON t.term_id = tt.term_id JOIN wp_term_relationships tr ON tt.term_taxonomy_id = tr.term_taxonomy_id JOIN wp_term_relationships tr_cat ON tr.object_id = tr_cat.object_id JOIN wp_term_taxonomy tt_cat ON tr_cat.term_taxonomy_id = tt_cat.term_taxonomy_id WHERE tt.taxonomy LIKE 'pa_%' -- 只筛选属性类型的术语 AND tt_cat.taxonomy = 'product_cat' -- 锁定产品分类 AND tt_cat.term_id = 123 -- 替换成你的子分类ID ORDER BY tt.taxonomy, t.name; -- 按属性分组排序
查询解释
- 通过
wp_term_relationships关联产品ID,把分类和属性术语绑定到同一个产品上 DISTINCT确保同一个属性术语不会重复出现- 最后按属性类型(比如
pa_wattage)和术语名称排序,方便后续整理成菜单
方案二:WooCommerce内置PHP函数(适合主题/插件开发,更安全)
如果你是在WordPress主题里开发菜单,推荐用WooCommerce官方函数,避免直接操作数据库带来的兼容性问题。下面是完整的层级菜单构建代码:
第一步:封装获取子分类属性术语的函数
/** * 获取指定子分类下所有产品关联的属性术语 * @param int $child_cat_id 子分类ID * @return array 按属性分组的术语列表 */ function get_child_category_attribute_terms($child_cat_id) { // 获取子分类下的所有已发布产品(-1表示获取全部) $products = wc_get_products(array( 'category' => array(get_term($child_cat_id)->slug), 'limit' => -1, 'status' => 'publish' )); $attribute_terms = array(); foreach ($products as $product) { // 获取产品的所有属性 $attributes = $product->get_attributes(); foreach ($attributes as $attribute) { // 跳过自定义属性(如果需要包含自定义属性,可注释这一行) if (!$attribute->is_taxonomy()) continue; $taxonomy = $attribute->get_taxonomy(); // 获取产品关联的属性术语ID列表 $term_ids = $product->get_attribute($taxonomy); if (empty($term_ids)) continue; // 获取术语对象 $term_objects = get_terms(array( 'taxonomy' => $taxonomy, 'include' => explode(',', $term_ids), 'hide_empty' => false )); // 去重存储术语 foreach ($term_objects as $term) { $attribute_terms[$taxonomy][$term->term_id] = $term; } } } return $attribute_terms; }
第二步:构建完整的层级菜单
// 获取所有一级产品分类(parent为0的分类) $parent_categories = get_terms(array( 'taxonomy' => 'product_cat', 'parent' => 0, 'hide_empty' => true, // 只显示有产品的分类,可选 'orderby' => 'name' )); // 遍历一级分类构建菜单 foreach ($parent_categories as $parent_cat) { echo '<div class="menu-level-1">'; echo '<h2>' . esc_html($parent_cat->name) . '</h2>'; // 获取当前一级分类的子分类 $child_categories = get_terms(array( 'taxonomy' => 'product_cat', 'parent' => $parent_cat->term_id, 'hide_empty' => true, 'orderby' => 'name' )); // 遍历子分类 foreach ($child_categories as $child_cat) { echo '<div class="menu-level-2">'; echo '<h3>' . esc_html($child_cat->name) . '</h3>'; // 商城链接:跳转到子分类产品列表页 $shop_link = get_term_link($child_cat); echo '<a href="' . esc_url($shop_link) . '" class="shop-link">商城</a>'; // 获取该子分类下的属性术语 $attribute_terms = get_child_category_attribute_terms($child_cat->term_id); // 构建属性术语菜单 echo '<div class="menu-level-3">'; foreach ($attribute_terms as $taxonomy => $terms) { // 去掉属性taxonomy的pa_前缀,显示友好名称 $attr_name = str_replace('pa_', '', $taxonomy); echo '<div class="attribute-group">'; echo '<span class="attribute-name">' . esc_html($attr_name) . '</span>'; echo '<ul class="attribute-terms">'; foreach ($terms as $term) { // 构建筛选链接:子分类+属性术语的产品列表 $filtered_link = add_query_arg(array( 'product_cat' => $child_cat->slug, $taxonomy => $term->slug ), home_url('/shop/')); echo '<li><a href="' . esc_url($filtered_link) . '">' . esc_html($term->name) . '</a></li>'; } echo '</ul>'; echo '</div>'; } echo '</div>'; echo '</div>'; } echo '</div>'; }
代码说明
- 自动按「一级分类→子分类→属性术语」的层级构建菜单
- 每个属性术语的链接会自动筛选出该子分类下对应术语的产品
- 用
esc_html和esc_url确保输出内容安全,避免XSS漏洞 - 可以通过CSS类(
menu-level-1、menu-level-2等)自定义菜单样式
优化建议
- 缓存优化:如果分类下产品很多,每次加载都查询产品会影响性能,建议用WordPress的缓存函数缓存属性术语结果:
$cache_key = 'cat_attr_terms_' . $child_cat_id; $attribute_terms = wp_cache_get($cache_key); if (!$attribute_terms) { $attribute_terms = get_child_category_attribute_terms($child_cat_id); wp_cache_set($cache_key, $attribute_terms, '', 3600); // 缓存1小时 } - 自定义属性支持:如果需要包含自定义属性(非 taxonomy 类型),可以修改函数,用
$attribute->get_options()获取自定义属性的值。 - 性能优化:如果子分类下产品数量极大,建议用方案一的SQL查询代替
wc_get_products,减少内存占用。
内容的提问来源于stack exchange,提问作者Dario Lopes
相关产品推荐
相关产品推荐

