让 WooCommerce 产品分类菜单同步,变得无比简单
你遇到过这个问题吗?
在 WordPress 后台编辑导航菜单时,当你从「产品分类」里批量添加多个分类到菜单中,你会发现——所有分类都变成了同级关系,没有任何父子层级。
即使你在「产品 → 产品分类」中已经正确设置了父子层级(如:珠宝 → 手链 → 卡地亚手链),添加到菜单后却变成:
text
JEWELRY & ACCESSORIES BRACELET Cartier Bracelet
而不是你想要的:
text
▼ JEWELRY & ACCESSORIES
▼ BRACELET
Cartier Bracelet
你需要手动把每个子分类拖拽到父分类下方,一个个缩进调整。
如果你的产品分类有几十上百个,这项工作将耗费大量时间,甚至让人崩溃。
FTP 手动安装
在 /wp-content/plugins/ 目录下新建文件夹:auto-category-menu-sync
将插件代码保存为 auto-category-menu-sync.php
上传到 /wp-content/plugins/auto-category-menu-sync/ 目录
进入 WordPress 后台 → 插件 → 找到「Auto Category Menu Sync」→ 点击「启用」
解决方案:Auto Category Menu Sync 由QQ8111216原创,转载请声明。
这是一个专为 WooCommerce 设计的轻量级插件,它能自动将产品分类按正确的父子层级关系同步到导航菜单中,彻底告别手动拖拽。
核心功能
1️⃣ 一键同步,保留完整层级
选择目标菜单,点击「开始同步」,插件会自动:
- 扫描所有 WooCommerce 产品分类
- 识别每个分类的父级/子级关系
- 按正确层级添加到菜单中(支持无限级嵌套)
2️⃣ 增量更新模式(防重复)
日常运营中,你可能会不断新增产品分类。再次同步时:
- 只添加新增的分类
- 已存在于菜单中的分类不会重复添加
- 原有菜单顺序和自定义设置保持不变
3️⃣ 清空重建模式
当菜单结构混乱,或者你想完全重置时:
- 勾选「清空现有菜单项」
- 插件会先删除该菜单中的所有内容
- 再重新添加全部分类(保持完整层级)
4️⃣ 完全兼容原生菜单编辑器
同步完成后,你依然可以在 外观 → 菜单 中:
- 手动调整菜单项的顺序
- 添加自定义链接、页面等
- 设置菜单显示位置(顶部菜单、移动端菜单等)
插件不会锁定或覆盖你的手动调整。
使用场景
| 场景 | 说明 |
|---|---|
| 新站搭建 | 产品分类设置好后,一键生成完整的多级导航菜单 |
| 分类频繁新增 | 每次新增子分类后,增量同步即可,无需重新搭建菜单 |
| 菜单结构重置 | 勾选清空模式,一键恢复最干净的层级结构 |
| 大型分类目录 | 几百个分类的场景下,手动拖拽几乎不可行,本插件是刚需 |
插件截图
| 界面 | 说明 |
|---|---|
| 设置页面 | 选择菜单、选择同步模式、点击按钮 |
| 同步前(菜单编辑界面) | 分类平铺、无层级 |
| 同步后(菜单编辑界面) | 父子层级完整显示、缩进正确 |
技术参数
| 项目 | 说明 |
|---|---|
| 兼容性 | WordPress 5.0+ / WooCommerce 4.0+ |
| 多语言 | 支持中文、英文(可扩展) |
| 性能 | 轻量级,无外部依赖,不拖慢后台 |
| 隐私 | 不收集任何用户数据 |
常见问题
Q1:插件会影响前台菜单的显示效果吗?
A: 不会。插件只负责菜单的结构(哪个菜单项是父级、哪个是子级),不改变菜单的样式。前台菜单的显示效果由你的主题决定。
Q2:同步后我可以手动调整顺序吗?
A: 完全可以。同步只是帮你建立正确的父子层级,之后你可以像操作普通菜单一样随意拖拽调整。
Q3:如果我删除了某个产品分类,菜单会自动同步吗?
A: 不会自动删除。这是 WooCommerce 的设计机制——删除分类后,菜单中对应的菜单项会变成「(已删除)」状态,需要你手动移除。插件暂不涉及删除逻辑,避免误操作风险。
Q4:支持多级分类吗(父→子→孙→曾孙)?
A: 支持。理论上无限级嵌套,只要你在产品分类中正确设置了父子关系,插件就会原样同步到菜单中。
Q5:支持多站点(Multisite)吗?
A: 支持。每个站点独立管理自己的菜单和分类。
<?php
/**
* Plugin Name: Auto Category Menu Sync Pro
* Description: 自动将 WooCommerce 产品分类按层级关系同步到导航菜单中,支持按层级深度筛选(仅一级/二级/三级/全部分类)
* Version: 2.0
* Author: qq8111216
*/
if (!defined('ABSPATH')) {
exit;
}
/**
* 检查 WooCommerce 是否激活
*/
function acms_check_woocommerce() {
if (!class_exists('WooCommerce')) {
add_action('admin_notices', function() {
echo '<div class="notice notice-error"><p>⚠️ Auto Category Menu Sync 插件需要安装并激活 <strong>WooCommerce</strong> 才能使用。</p></div>';
});
return false;
}
return true;
}
/**
* 添加管理后台菜单页面
*/
add_action('admin_menu', 'acms_add_admin_menu');
function acms_add_admin_menu() {
if (!class_exists('WooCommerce')) {
return;
}
add_submenu_page(
'themes.php',
'分类菜单同步',
'分类菜单同步',
'manage_options',
'auto-category-menu-sync',
'acms_render_admin_page'
);
}
/**
* 渲染管理页面
*/
function acms_render_admin_page() {
if (!class_exists('WooCommerce')) {
echo '<div class="wrap"><div class="notice notice-error"><p>请先安装并激活 WooCommerce 插件。</p></div></div>';
return;
}
// 处理表单提交
if (isset($_POST['acms_sync_nonce']) && wp_verify_nonce($_POST['acms_sync_nonce'], 'acms_sync_action')) {
$selected_menu_id = intval($_POST['menu_id']);
$clear_existing = isset($_POST['clear_existing']);
$depth = isset($_POST['depth']) ? intval($_POST['depth']) : 0; // 0=全部, 1=一级, 2=二级, 3=三级
$result = acms_sync_categories_to_menu($selected_menu_id, $clear_existing, $depth);
if ($result === true) {
$depth_text = acms_get_depth_text($depth);
echo '<div class="notice notice-success is-dismissible"><p>✅ 同步成功!已添加 <strong>' . $depth_text . '</strong> 到菜单中。</p></div>';
} elseif ($result === 'nothing_to_sync') {
echo '<div class="notice notice-warning is-dismissible"><p>⚠️ 所选层级的分类已在菜单中,无需重复添加。</p></div>';
} else {
echo '<div class="notice notice-error is-dismissible"><p>❌ 同步失败,请检查是否选择了有效的菜单。</p></div>';
}
}
$menus = wp_get_nav_menus();
?>
<div class="wrap">
<h1>自动分类菜单同步</h1>
<p>将 WooCommerce 产品分类按<strong>父子层级关系</strong>自动添加到选定的导航菜单中。</p>
<form method="post" action="">
<?php wp_nonce_field('acms_sync_action', 'acms_sync_nonce'); ?>
<table class="form-table">
<tr>
<th scope="row"><label for="menu_id">选择目标菜单</label></th>
<td>
<select name="menu_id" id="menu_id" required>
<option value="">— 请选择 —</option>
<?php foreach ($menus as $menu): ?>
<option value="<?php echo esc_attr($menu->term_id); ?>">
<?php echo esc_html($menu->name); ?>
</option>
<?php endforeach; ?>
</select>
<p class="description">选择要将产品分类添加到的菜单(例如:Main navigation)</p>
</td>
</tr>
<!-- 新增:层级深度选择 -->
<tr>
<th scope="row"><label for="depth">同步层级</label></th>
<td>
<select name="depth" id="depth">
<option value="0">📁 所有层级(全部)</option>
<option value="1">📂 仅一级分类(顶级分类)</option>
<option value="2">📂 仅二级分类(子分类)</option>
<option value="3">📂 仅三级分类(孙分类)</option>
</select>
<p class="description">
选择要同步到菜单中的分类层级。<br>
<strong>例如:</strong>「仅一级分类」只会添加顶级分类,不会添加其子分类;「仅二级分类」只会添加第二层级的子分类。
</p>
</td>
</tr>
<tr>
<th scope="row"><label for="clear_existing">清空现有菜单项</label></th>
<td>
<input type="checkbox" name="clear_existing" id="clear_existing" value="1">
<label for="clear_existing">同步前先清空此菜单中的所有现有项目</label>
<p class="description">
⚠️ 勾选后会删除该菜单中所有已有的菜单项,然后重新添加选层级的产品分类。<br>
<strong>不勾选时:</strong>只会添加菜单中尚不存在的分类,已有的会保留(不会重复添加)。
</p>
</td>
</tr>
</table>
<p class="submit">
<button type="submit" class="button button-primary" onclick="return confirm('确认要同步分类到菜单吗?');">
开始同步
</button>
</p>
</form>
<hr>
<h2>层级说明</h2>
<div style="background: #f5f5f5; padding: 15px; border-left: 4px solid #007cba;">
<p><strong>📁 一级分类(顶级分类)</strong><br>
例如:JEWELRY & ACCESSORIES</p>
<p><strong>📂 二级分类(子分类)</strong><br>
例如:BRACELET(隶属于 JEWELRY & ACCESSORIES)</p>
<p><strong>📄 三级分类(孙分类)</strong><br>
例如:Cartier Bracelet(隶属于 BRACELET)</p>
</div>
<h2>使用场景</h2>
<ul>
<li><strong>仅一级分类:</strong>适合简约导航,只显示大类,用户点击后进入分类页再筛选子类。</li>
<li><strong>仅二级分类:</strong>适合作为某个顶级分类下的快速导航。</li>
<li><strong>仅三级分类:</strong>适合精细化导航,直接展示最细粒度的产品分类。</li>
<li><strong>所有层级:</strong>完整的多级下拉菜单,适合品类丰富的电商网站。</li>
</ul>
<p><strong>注意:</strong>同步后,你可以继续在 <code>外观 → 菜单</code> 中手动调整顺序或样式。</p>
</div>
<?php
}
/**
* 获取层级文本描述
*/
function acms_get_depth_text($depth) {
switch ($depth) {
case 1: return '仅一级分类';
case 2: return '仅二级分类';
case 3: return '仅三级分类';
default: return '所有层级分类';
}
}
/**
* 获取菜单中已有的所有产品分类ID
*/
function acms_get_existing_category_ids_in_menu($menu_id) {
$existing_ids = [];
$menu_items = wp_get_nav_menu_items($menu_id);
if (!empty($menu_items)) {
foreach ($menu_items as $item) {
if ($item->type === 'taxonomy' && $item->object === 'product_cat') {
$existing_ids[] = (int) $item->object_id;
}
}
}
return $existing_ids;
}
/**
* 核心同步函数(支持层级筛选)
*
* @param int $menu_id 菜单ID
* @param bool $clear_existing 是否清空
* @param int $depth 层级深度:0=全部, 1=一级, 2=二级, 3=三级
*/
function acms_sync_categories_to_menu($menu_id, $clear_existing = false, $depth = 0) {
if (!$menu_id) {
return false;
}
if ($clear_existing) {
$existing_items = wp_get_nav_menu_items($menu_id);
if (!empty($existing_items)) {
foreach ($existing_items as $item) {
wp_delete_post($item->ID, true);
}
}
$existing_category_ids = [];
} else {
$existing_category_ids = acms_get_existing_category_ids_in_menu($menu_id);
}
// 获取顶级分类
$top_categories = get_terms([
'taxonomy' => 'product_cat',
'hide_empty' => false,
'parent' => 0,
]);
if (is_wp_error($top_categories)) {
return false;
}
$added_count = 0;
foreach ($top_categories as $category) {
// 根据层级深度决定是否处理
$current_depth = 1;
// 如果只需要一级分类
if ($depth === 1) {
$added_count += acms_add_single_category_only($category, $menu_id, 0, $existing_category_ids, $clear_existing);
}
// 如果需要更深层级(二级、三级、全部)
else {
$added_count += acms_add_category_and_children_to_menu(
$category,
$menu_id,
0,
$existing_category_ids,
$clear_existing,
$depth,
$current_depth
);
}
}
if ($added_count === 0 && !$clear_existing) {
return 'nothing_to_sync';
}
return true;
}
/**
* 只添加单个分类(不添加子分类)
* 用于"仅一级分类"模式
*/
function acms_add_single_category_only($category, $menu_id, $parent_menu_item_id = 0, &$existing_category_ids = [], $force_add = false) {
$added_count = 0;
$already_exists = in_array((int) $category->term_id, $existing_category_ids);
if (!$already_exists || $force_add) {
if (!($already_exists && !$force_add)) {
$menu_item_data = [
'menu-item-object-id' => $category->term_id,
'menu-item-object' => 'product_cat',
'menu-item-parent-id' => $parent_menu_item_id,
'menu-item-type' => 'taxonomy',
'menu-item-title' => $category->name,
'menu-item-url' => get_term_link($category),
'menu-item-status' => 'publish',
];
$menu_item_id = wp_update_nav_menu_item($menu_id, 0, $menu_item_data);
if (!is_wp_error($menu_item_id)) {
$added_count++;
$existing_category_ids[] = (int) $category->term_id;
}
}
}
return $added_count;
}
/**
* 递归添加分类及其子分类(支持层级限制)
*
* @param WP_Term $category 分类对象
* @param int $menu_id 目标菜单ID
* @param int $parent_menu_item_id 父级菜单项ID
* @param array &$existing_category_ids 已存在的分类ID列表
* @param bool $force_add 是否强制添加
* @param int $max_depth 最大深度:0=无限制, 1=仅当前层, 2=到二级, 3=到三级
* @param int $current_depth 当前深度(1=一级, 2=二级, 3=三级)
* @return int 实际新增的菜单项数量
*/
function acms_add_category_and_children_to_menu($category, $menu_id, $parent_menu_item_id = 0, &$existing_category_ids = [], $force_add = false, $max_depth = 0, $current_depth = 1) {
$added_count = 0;
$current_menu_item_id = 0;
$already_exists = in_array((int) $category->term_id, $existing_category_ids);
// 判断当前层级是否应该被添加
$should_add_this_level = true;
if ($max_depth > 0) {
// 如果指定了层级限制,只添加匹配层级的分类
// 例如:max_depth=2 时,只添加当前深度为 2 的分类(不添加深度1和深度3)
// 但为了保持菜单结构的语义,我们允许添加指定深度的分类,并正确设置其父级
$should_add_this_level = ($current_depth == $max_depth);
}
// 如果需要添加当前分类
if (($should_add_this_level || $max_depth == 0) && (!$already_exists || $force_add)) {
if (!($already_exists && !$force_add)) {
$menu_item_data = [
'menu-item-object-id' => $category->term_id,
'menu-item-object' => 'product_cat',
'menu-item-parent-id' => $parent_menu_item_id,
'menu-item-type' => 'taxonomy',
'menu-item-title' => $category->name,
'menu-item-url' => get_term_link($category),
'menu-item-status' => 'publish',
];
$menu_item_id = wp_update_nav_menu_item($menu_id, 0, $menu_item_data);
if (!is_wp_error($menu_item_id)) {
$current_menu_item_id = $menu_item_id;
$added_count++;
$existing_category_ids[] = (int) $category->term_id;
}
} else {
$current_menu_item_id = acms_get_menu_item_id_by_category($menu_id, $category->term_id);
}
} else {
// 即使不添加当前分类,也需要获取其菜单项ID(如果已存在),用于子分类的父级挂载
$current_menu_item_id = acms_get_menu_item_id_by_category($menu_id, $category->term_id);
}
// 处理子分类(如果有)
// 当 max_depth > 0 且 current_depth >= max_depth 时,不再深入处理子分类
$should_process_children = ($max_depth == 0) || ($current_depth < $max_depth);
if ($should_process_children && $current_menu_item_id) {
$children = get_terms([
'taxonomy' => 'product_cat',
'hide_empty' => false,
'parent' => $category->term_id,
]);
if (!is_wp_error($children) && !empty($children)) {
$next_depth = $current_depth + 1;
foreach ($children as $child) {
$added_count += acms_add_category_and_children_to_menu(
$child,
$menu_id,
$current_menu_item_id,
$existing_category_ids,
$force_add,
$max_depth,
$next_depth
);
}
}
}
return $added_count;
}
/**
* 根据分类ID获取其在菜单中对应的菜单项ID
*/
function acms_get_menu_item_id_by_category($menu_id, $category_id) {
$menu_items = wp_get_nav_menu_items($menu_id);
if (!empty($menu_items)) {
foreach ($menu_items as $item) {
if ($item->type === 'taxonomy' &&
$item->object === 'product_cat' &&
(int) $item->object_id === (int) $category_id) {
return $item->ID;
}
}
}
return false;
}
