您遇到了在Customizer中修改导航菜单的不完整实现。尤其是phpdoc内部WP_Customize_Nav_Menu_Item_Setting::preview()
您可以看到以下评论:
// @todo Add get_post_metadata filters for plugins to add their data.
尽管如此,尽管我们没有实现对导航菜单项Posteta的预览更改的核心支持,但仍然可以做到。但首先,让我们讨论一下JavaScript。
扩展Nav菜单项控件首先需要确定需要扩展的Nav菜单项控件,这是使用JS API实现此目的的方法:
wp.customize.control.bind( \'add\', ( control ) => {
if ( control.extended( wp.customize.Menus.MenuItemControl ) ) {
control.deferred.embedded.done( () => {
extendControl( control );
} );
}
} );
这个
extendControl()
函数需要使用您通过添加的新字段的行为来增强控件
kia_customizer_custom_fields()
:
/**
* Extend the control with roles information.
*
* @param {wp.customize.Menus.MenuItemControl} control
*/
function extendControl( control ) {
control.authFieldset = control.container.find( \'.nav_menu_role_authentication\' );
control.rolesFieldset = control.container.find( \'.nav_menu_roles\' );
// Set the initial UI state.
updateControlFields( control );
// Update the UI state when the setting changes programmatically.
control.setting.bind( () => {
updateControlFields( control );
} );
// Update the setting when the inputs are modified.
control.authFieldset.find( \'input\' ).on( \'click\', function () {
setSettingRoles( control.setting, this.value );
} );
control.rolesFieldset.find( \'input\' ).on( \'click\', function () {
const checkedRoles = [];
control.rolesFieldset.find( \':checked\' ).each( function () {
checkedRoles.push( this.value );
} );
setSettingRoles( control.setting, checkedRoles.length === 0 ? \'in\' : checkedRoles );
} );
}
在这里,您可以看到它根据控件的设置值设置初始UI状态,然后在使用双向数据绑定进行更改时进行更新。
下面是一个函数,负责更改roles
在自定义程序中Setting
导航菜单项的对象:
/**
* Extend the setting with roles information.
*
* @param {wp.customize.Setting} setting
* @param {string|Array} roles
*/
function setSettingRoles( setting, roles ) {
setting.set(
Object.assign(
{},
_.clone( setting() ),
{ roles }
)
);
}
下面是如何将设置的值应用于控件的字段:
/**
* Apply the control\'s setting value to the control\'s fields.
*
* @param {wp.customize.Menus.MenuItemControl} control
*/
function updateControlFields( control ) {
const roles = control.setting().roles || \'\';
const radioValue = _.isArray( roles ) ? \'in\' : roles;
const checkedRoles = _.isArray( roles ) ? roles : [];
control.rolesFieldset.toggle( \'in\' === radioValue );
const authRadio = control.authFieldset.find( `input[type=radio][value="${ radioValue }"]` );
authRadio.prop( \'checked\', true );
control.rolesFieldset.find( \'input[type=checkbox]\' ).each( function () {
this.checked = checkedRoles.includes( this.value );
} );
}
这就是所有必需的JS代码,可以通过以下方式在PHP中排队:
add_action(
\'customize_controls_enqueue_scripts\',
static function () {
wp_enqueue_script(
\'customize-nav-menu-roles\',
plugin_dir_url( __FILE__ ) . \'/customize-nav-menu-roles.js\',
[ \'customize-nav-menus\' ],
filemtime( __DIR__ . \'/customize-nav-menu-roles.js\' ),
true
);
}
);
现在来看看所需的PHP代码。。。
自WP_Customize_Nav_Menu_Item_Setting
类不知道自定义字段,它只会将它们去掉。所以我们需要实现我们自己的预览方法。下面是如何将其连接起来:
add_action(
\'customize_register\',
static function( WP_Customize_Manager $wp_customize ) {
if ( $wp_customize->settings_previewed() ) {
foreach ( $wp_customize->settings() as $setting ) {
if ( $setting instanceof WP_Customize_Nav_Menu_Item_Setting ) {
preview_nav_menu_setting_postmeta( $setting );
}
}
}
},
1000
);
这将在每个注册的nav菜单项设置上循环并调用
preview_nav_menu_setting_postmeta()
:
/**
* Preview changes to the nav menu item roles.
*
* Note the unimplemented to-do in the doc block for the setting\'s preview method.
*
* @see WP_Customize_Nav_Menu_Item_Setting::preview()
*
* @param WP_Customize_Nav_Menu_Item_Setting $setting Setting.
*/
function preview_nav_menu_setting_postmeta( WP_Customize_Nav_Menu_Item_Setting $setting ) {
$roles = get_sanitized_roles_post_data( $setting );
if ( null === $roles ) {
return;
}
add_filter(
\'get_post_metadata\',
static function ( $value, $object_id, $meta_key ) use ( $setting, $roles ) {
if ( $object_id === $setting->post_id && \'_nav_menu_role\' === $meta_key ) {
return [ $roles ];
}
return $value;
},
10,
3
);
}
您可以在这里看到,它向底层添加了一个过滤器
get_post_meta()
呼叫正在预览的角色值是通过以下方式获得的
get_sanitized_roles_post_data()
:
/**
* Save changes to the nav menu item roles.
*
* Note the unimplemented to-do in the doc block for the setting\'s preview method.
*
* @see WP_Customize_Nav_Menu_Item_Setting::update()
*
* @param WP_Customize_Nav_Menu_Item_Setting $setting Setting.
*/
function save_nav_menu_setting_postmeta( WP_Customize_Nav_Menu_Item_Setting $setting ) {
$roles = get_sanitized_roles_post_data( $setting );
if ( null !== $roles ) {
update_post_meta( $setting->post_id, \'_nav_menu_role\', $roles );
}
}
现在,为了节约,我们做了类似的事情。首先,我们在以下位置循环所有导航菜单项设置:
customize_save_after
:
add_action(
\'customize_save_after\',
function ( WP_Customize_Manager $wp_customize ) {
foreach ( $wp_customize->settings() as $setting ) {
if ( $setting instanceof WP_Customize_Nav_Menu_Item_Setting && $setting->check_capabilities() ) {
save_nav_menu_setting_postmeta( $setting );
}
}
}
);
在哪里
save_nav_menu_setting_postmeta()
获取预览的设置值,然后将其保存到Posteta中:
/**
* Save changes to the nav menu item roles.
*
* Note the unimplemented to-do in the doc block for the setting\'s preview method.
*
* @see WP_Customize_Nav_Menu_Item_Setting::update()
*
* @param WP_Customize_Nav_Menu_Item_Setting $setting Setting.
*/
function save_nav_menu_setting_postmeta( WP_Customize_Nav_Menu_Item_Setting $setting ) {
$roles = get_sanitized_roles_post_data( $setting );
if ( null !== $roles ) {
update_post_meta( $setting->post_id, \'_nav_menu_role\', $roles );
}
}
The
get_sanitized_roles_post_data()
函数如下所示:
/**
* Sanitize roles value.
*
* @param string|array $value Roles.
* @return array|string Sanitized roles.
*/
function sanitize_roles_value( $value ) {
global $wp_roles;
if ( is_array( $value ) ) {
return array_intersect( $value, array_keys( $wp_roles->role_names ) );
} elseif ( in_array( $value, [ \'\', \'in\', \'out\' ], true ) ) {
return $value;
}
return \'\';
}
就是这样。
这里有一个完整的工作插件,一旦与导航菜单角色插件一起激活,就可以将所有这些部分放在一起:https://gist.github.com/westonruter/7f2b9c18113f0576a72e0aca3ce3dbcb
用于在定制器设置和控件字段之间进行数据绑定的JS逻辑可以变得更加优雅。例如,它可以使用React。或者它可以使用wp.customize.Element
其他控件使用的。但这可以通过良好的ol\'jQuery完成工作。
关于此实现的一个警告是:由于自定义程序如何预览尚未保存的导航菜单项,您将无法预览对此类导航菜单项的角色值的更改。(在引擎盖下,自定义程序创建一个否定的post ID,以表示尚未保存到DB的nav菜单项。)