WordPress菜单数据存于wp_options表的nav_menu和nav_menu_items两条记录中,非独立表;需通过wp_posts与wp_postmeta重建序列化结构,并确保nav_menu使用term_id而非post_id作为键。
nav_menu 选项在 wp_options 表里,不是独立表
wordpress 的菜单结构(包括菜单名称、位置映射、项顺序)全存在 wp_options 表的 nav_menu 和 nav_menu_items 这两条 option 记录里,不是单独一张表。很多人搜“nav_menu 表”却查不到,就是卡在这儿——它藏在 option_name 字段值为 nav_menu 和 nav_menu_items 的两行里。
恢复前提是:这两条记录没被彻底 DELETE,只是被误更新为空、或被插件/迁移脚本覆盖成空数组。
- 用 phpMyAdmin 或
mysql命令连上数据库,查SELECT * FROM wp_options WHERE option_name IN ('nav_menu', 'nav_menu_items'); - 如果
option_value是a:0:{}或空字符串,说明数据丢了但记录还在;如果是NULL或根本查不到,就得从备份或缓存里捞 -
nav_menu存的是菜单 ID → 名称映射(比如123→主菜单),nav_menu_items存的是每个菜单下所有项的完整序列化结构
从 wp_postmeta + wp_posts 拼出 nav_menu_items 内容
菜单项本质是自定义文章类型 nav_menu_item,每条菜单项对应一条 wp_posts 记录,其元数据(目标链接、标题、父级、排序)存在 wp_postmeta 里。只要这些 posts 没删,就能重建 nav_menu_items。
关键字段组合:wp_posts.post_type = 'nav_menu_item',且 wp_postmeta.meta_key 包含 _menu_item_object_id、_menu_item_url、_menu_item_menu_item_parent 等。
- 先确认菜单项是否存在:
SELECT ID, post_title, menu_order FROM wp_posts WHERE post_type = 'nav_menu_item' ORDER BY menu_order; - 对每个 ID,查它的元数据:
SELECT meta_key, meta_value FROM wp_postmeta WHERE post_id = 123; - 拼出来的 PHP 数组结构要严格匹配 WordPress 序列化格式,比如
a:2:{i:0;a:10:{s:13:"menu-item-url";s:12:"https://a.com";...}}—— 手写极易出错,建议用 PHPserialize()生成
wp_options 里 nav_menu 的修复不能只填名字
nav_menu 这条 option 不只是存菜单名,它还包含菜单 ID 与 term_id 的绑定关系。如果只塞个 a:1:{i:123;s:12:"主菜单";},后台可能显示菜单但无法分配到 theme_location。
真实结构里每个菜单对应一个 taxonomy term,ID 存在 wp_terms 和 wp_term_taxonomy 中,type 是 nav_menu。WordPress 通过 term_id 关联菜单项。
- 查真实菜单 term:
SELECT t.term_id, t.name, tt.taxonomy FROM wp_terms t JOIN wp_term_taxonomy tt ON t.term_id = tt.term_id WHERE tt.taxonomy = 'nav_menu'; -
nav_menu的 value 必须用 term_id 当 key,比如a:1:{i:456;s:12:"主菜单";},其中456是上面查出的 term_id,不是 posts 表里的 ID - 如果 term 被删了,得先用
INSERT INTO wp_terms和INSERT INTO wp_term_taxonomy补上,再更新nav_menuoption
wp_get_nav_menu_items() 返回空?检查 serialized 数据是否损坏
即使 nav_menu_items 记录存在,PHP 反序列化失败也会让 wp_get_nav_menu_items() 返回空数组。常见原因是:手动编辑过 option_value 字符串,导致引号、长度计数错位(比如把 s:5:"Hello" 改成 s:6:"Hello")。
这种损坏不会报错,只会静默失败。别信 phpMyAdmin 显示的“成功保存”,它不校验序列化有效性。
- 用 WordPress 自带函数验证:
var_dump( @unserialize( $broken_value ) );—— 如果返回bool(false)就是坏了 - 用在线工具如 unserialize.me(粘贴 raw value)快速诊断
- 修复方法只有两个:从最近一次正常导出的
wp_options备份中复制该字段,或从wp_posts+wp_postmeta重新生成并 serialize
最麻烦的不是找不到数据,而是菜单项的 _menu_item_object(page/post/category)对应的原始 post 或 term 已被删,这时候就算拼出结构,前台渲染也会跳空链接或 404 —— 得先补源内容,再修菜单。


















