设置

设置功能负责自定义化对象的实时预览、保存以及数据净化处理。每个设置都由一个控制对象来管理。在添加新设置时,有若干参数可供使用:

$wp_customize->add_setting( 'setting_id', array(
  'type' => 'theme_mod', // 或 'option'
  'capability' => 'edit_theme_options',
  'theme_supports' => '', // 很少需要使用。
  'default' => '',
  'transport' => 'refresh', // 或 postMessage
  'sanitize_callback' => '',
  'sanitize_js_callback' => '', // 通常为 to_json。
) );
重要提示:绝对不要使用类似 widget_*、sidebars_widgets[*]、nav_menu[*] 或 nav_menu_item[*] 这样的设置编号。这些编号格式分别保留给插件实例、侧边栏、导航菜单及导航菜单项使用。如果需要在设置编号中包含“widget”字样,应将其作为后缀而非前缀,例如 “homepage_widget”。

设置主要分为两种类型:选项型与主题修改型。选项型设置会直接存储在 WordPress 数据库的 wp_options 表中,因此无论当前使用的是哪个主题,这些设置都会生效。主题应尽可能避免添加选项型的设置。而主题修改型设置则是特定于某个主题的。大多数主题选项都应属于主题修改型设置。例如,一个自定义 CSS 插件可以将自定义的 CSS 设置注册为主题修改型设置,这样每个主题就能拥有独特的 CSS 规则,且在切换主题后再切换回来时也不会丢失这些 CSS 规则。

customize-theme-mods-options
主题修改型设置与选项型设置的对比示例。

通常而言,为设置指定默认值以及净化回调函数最为重要,这能够确保没有不安全的数据被存储在数据库中。主题的典型用法如下:

$wp_customize->add_setting( 'accent_color', array(
  'default' => '#f72525',
  'sanitize_callback' => 'sanitize_hex_color',
) );

插件的典型用法如下:

$wp_customize->add_setting( 'myplugin_options[color]', array(
  'type' => 'option',
  'capability' => 'manage_options',
  'default' => '#ff2525',
  'sanitize_callback' => 'sanitize_hex_color',
) );

需要注意的是,对于使用选项型设置的那些以键值对数组形式存储的选项,Customizer 也可以进行处理。这样一来,非主题修改型的单个选项中就可以存储多个设置。若要获取并使用这些自定义设置的值,可利用设置编号,通过 get_theme_mod() 和 get_option() 函数来实现:

function my_custom_css_output() {
  echo '<style type="text/css" id="custom-theme-css">' .
  get_theme_mod( 'custom_theme_css', '' ) . '</style>';
  echo '<style type="text/css" id="custom-plugin-css">' .
  get_option( 'custom_plugin_css', '' ) . '</style>';
}
add_action( 'wp_head', 'my_custom_css_output');

请注意,get_theme_mod() 和 get_option() 函数的第二个参数是默认值,该值应与添加设置时设定的默认值保持一致。

控制元素

控制元素是用于构建用户界面的核心自定义化对象。具体而言,每个控制元素都必须与某个设置相关联,该设置会将用户通过控制元素输入的数据保存到数据库中(同时还会在实时预览中显示这些数据并对数据进行净化处理)。Customizer 管理器可以轻松地添加各种控制元素,从而为开发者提供丰富的界面选项:

$wp_customize->add_control( 'setting_id', array(
  'type' => 'date',
  'priority' => 10, // 在该部分内的优先级。
  'section' => 'colors', // 必填,可以是核心内置的或自定义的。
  'label' => __( '日期' ),
  'description' => __( '这是一个带有红色边框的日期输入控件。' ),
  'input_attrs' => array(
    'class' => 'my-custom-class-for-js',
    'style' => 'border: 1px solid #900',
    'placeholder' => __( 'mm/dd/yyyy' ),
  ),
  'active_callback' => 'is_front_page',
) );

添加控制元素时最重要的参数是其类型——它决定了 Customizer 将呈现何种类型的用户界面。WordPress 核心提供了几种内置的控制元素类型:

  • 任何允许的类型的 <input> 元素(详见下文)
  • checkbox
  • textarea
  • radio(需将值与标签的键值对数组传递给 choices 参数)
  • select(需将值与标签的键值对数组传递给 choices 参数)
  • dropdown-pages(可使用 allow_addition 参数允许用户通过该控件添加新页面)

对于 html input 元素所支持的任何输入类型,只需在添加控制元素时将对应的类型属性值传递给 type 参数即可。这样一来,在浏览器支持的前提下,就可以使用 text、hidden、number、range、url、tel、email、search、time、date、datetime 以及 week 这些控制元素类型。

控制元素必须先添加到某个部分中才会被显示出来(而部分中必须包含控制元素才能被显示)。实现这一点的方法就是在添加控制元素时指定 section 参数。以下是一个添加基本文本区域控制元素的示例:

$wp_customize->add_control( 'custom_theme_css', array(
  'label' => __( '自定义主题 CSS' ),
  'type' => 'textarea',
  'section' => 'custom_css',
) );

下面是一个基本范围(滑块)控制元素的示例。需要注意的是,由于范围输入类型的设计初衷是用于那些精确数值并不重要的设置,因此大多数浏览器都不会显示该控制元素的数值。如果数值非常重要,建议使用 number 类型。input_attrs 参数可以将属性与值的键值对数组映射到输入元素上的相应属性,其用途十分广泛,从设置占位符文本到在自定义脚本中引用 data- 格式的数据均可使用。对于数字和范围控制元素,该参数还可用于设置最小值、最大值以及步长值。

$wp_customize->add_control( 'setting_id', array(
  'type' => 'range',
  'section' => 'title_tagline',
  'label' => __( '范围值' ),
  'description' => __( '这是该范围控制元素的描述。' ),
  'input_attrs' => array(
    'min' => 0,
    'max' => 10,
    'step' => 2,
  ),
) );

核心内置控制元素

如果现有的基本控制类型无法满足您的需求,您可以轻松创建并添加自定义控制。本文后面会更详细地介绍自定义控制,其实它们本质上是基础 WP_Customize_Control 对象的子类,能够实现您所需的任何任意 HTML 标记与功能。核心功能中还包含了几种内置的自定义控制,让开发者能够轻松实现由 JavaScript 驱动的丰富功能。如下所示即可添加颜色选择器控制:

$wp_customize->add_control( new WP_Customize_Color_Control( $wp_customize, 'color_control', array(
  'label' => __( '强调色', 'theme_textdomain' ),
  'section' => 'media',
) ) );

WordPress 4.1 和 4.2 还通过媒体控制功能增加了对各类多媒体内容的支持。该媒体控制实现了 WordPress 原生的媒体管理器,允许用户从自己的媒体库中选择文件或上传新文件。在添加控制时指定 mime_type 参数,即可指示媒体库仅显示特定类型的文件,比如图片或音频:

$wp_customize->add_control( new WP_Customize_Media_Control( $wp_customize, 'image_control', array(
  'label' => __( '首页特色图片', 'theme_textdomain' ),
  'section' => 'media',
  'mime_type' => 'image',
) ) );
$wp_customize->add_control( new WP_Customize_Media_Control( $wp_customize, 'audio_control', array(
  'label' => __( '首页特色音频', 'theme_textdomain' ),
  'section' => 'media',
  'mime_type' => 'audio',
) ) );

需要注意的是,与 WP_Customize_Media_Control 相关联的设置会保存对应的附件 ID,而所有其他与媒体相关的控制(即 WP_Customize_Upload_Control 的子类)则会在设置中保存媒体文件的 URL。更多相关信息可在 Make WordPress Core 文档中找到。

此外,WordPress 4.3 还引入了 WP_Customize_Cropped_Image_Control,它提供了在选中图片后对其进行裁剪的接口,对于需要特定宽高比的场景非常有用。

板块

板块是自定义化控制器的 UI 容器。虽然您可以将自定义控制添加到核心板块中,但如果选项较多,您可能希望添加一个或多个自定义板块。可以使用 WP_Customize_Manager 对象的 add_section 方法来添加新板块:

$wp_customize->add_section( 'custom_css', array(
  'title' => __( '自定义 CSS', 'themename' ),
  'description' => __( '在此处添加自定义 CSS' ),
  'panel' => '', // 通常不需要。
  'priority' => 160,
  'capability' => 'edit_theme_options',
  'theme_supports' => '', // 很少需要。
) );

您只需包含那些需要覆盖默认值的字段即可。例如,默认的优先级(显示顺序)通常已经足够,而且如果选项本身就很直观,大多数板块也不需要额外的描述性文字。如果您确实想更改自定义板块的位置,以下是核心板块的优先级顺序:

标题ID优先级(顺序)
站点标题与标语title_tagline20
颜色colors40
页头图片header_image60
背景图片background_image80
菜单(面板)nav_menus100
小部件(面板)widgets110
静态首页static_front_page120
默认160
附加 CSScustom_css200

在大多数情况下,只需指定一两个参数即可添加板块。以下是一个用于添加与主题页脚相关的选项板块的示例:

// 添加页脚/版权信息板块。
$wp_customize->add_section( 'footer' , array(
  'title' => __( '页脚', 'themename' ),
  'priority' => 105, // 位于小部件之前。
) );

面板

自定义化面板 API 是在 WordPress 4.0 中引入的,它允许开发者在控制与板块之外再创建一层层次结构。面板不仅仅用于对控制板块进行分组,其设计目的是为自定义化界面提供不同的使用场景,比如自定义小部件、菜单,未来甚至可能用于编辑文章。板块对象与面板对象之间存在重要的技术区别。

在大多数情况下,主题不应注册自己的面板。板块无需嵌套在面板之下,而且每个板块通常应包含多个控制。控制也应添加到核心提供的板块中,比如在“颜色”板块中添加颜色选项。同时,请确保您的选项尽可能简洁高效;详情可参阅 WordPress 的设计理念。面板是为整个功能模块(如小部件、菜单或文章)设计的,而非用于包裹普通板块的容器。如果您确实必须使用面板,会发现其 API 与板块的 API 几乎完全相同:

$wp_customize->add_panel( 'menus', array(
  'title' => __( '菜单', 'themename' ),
  'description' => $description, // 可以包含 <p> 等 HTML 标签。
  'priority' => 160, // 与顶级板块的层次结构混合。
) );
$wp_customize->add_section( $section_id , array(
  'title' => $menu->name,
  'panel' => 'menus',
) );

面板必须至少包含一个板块,而该板块又必须至少包含一个控制,才能被显示出来。从上面的示例可以看出,向面板中添加板块的方式与向板块中添加控制的方式类似。不过,与控制不同,如果在注册板块时面板参数为空,该板块将会显示在主要的顶级自定义化界面中,因为大多数板块本就不应被包含在面板之中。

自定义控制、板块与面板

通过继承与各个自定义化对象相关的 PHP 对象,即可轻松创建自定义控制、板块和面板:WP_Customize_Control、WP_Customize_Section 以及 WP_Customize_Panel(其实 WP_Customize_Setting 也可以这样处理,但如下一节所述,通常使用自定义设置类型来实现自定义设置会更为合适)。以下是一个基本自定义控制的示例:

class WP_New_Menu_Customize_Control extends WP_Customize_Control {
  public $type = 'new_menu';
  /**
  * 显示控制的内容。
  */
  public function render_content() {
  ?>
    <button class="button button-primary" id="create-new-menu-submit" tabindex="0"><?php _e( '创建菜单' ); ?></button>
  <?php
  }
}

通过继承基础控制类,您可以根据需求用自定义功能覆盖原有功能,或者直接使用核心功能。最常需要覆盖的函数是 render_content(),因为它允许您使用 HTML 从头开始创建自定义 UI。不过,使用自定义控制时需谨慎,因为它们可能会引入与周围核心 UI 不一致的用户界面,从而给用户带来困惑。自定义自定义化对象的添加方式与默认控制、板块和面板的添加方式类似:

$wp_customize->add_control(
  new WP_Customize_Color_Control(
    $wp_customize, // WP_Customize_Manager
    'accent_color', // 设置 ID
    array( // 参数,包括任何自定义参数。
      'label' => __( '强调色' ),
      'section' => 'colors',
    )
  )
);

在添加控制时传递的参数会映射到控制类中的类变量,因此当您的自定义对象的某些部分在不同实例中有所不同时,就可以添加并使用自定义参数。

在创建自定义控件、板块或面板时,强烈建议参考核心代码,这样才能充分了解哪些功能可以被覆盖。核心代码中还包含了每种类型自定义对象的示例,可分别在 wp-includes/class-wp-customize-control.php、wp-includes/class-wp-customize-section.php 以及 wp-includes/class-wp-customize-panel.php 中找到。每种自定义器对象类型还配有 JavaScript API,可用于扩展自定义对象;更多详情请参阅自定义器 JavaScript API 部分。

自定义器 UI 标准

自定义的自定义器控件、板块和面板应尽可能遵循核心 UI 惯例。这包括采用 wp-admin 中的标准,例如使用 .button 和 .button-primary 类。此外,从 WordPress 4.7 开始,还有一些针对自定义器的特定标准:

  • 仅使用白色背景色来标识导航项及可操作项目(如输入框)
  • 通用的 #eee 背景色可与白色元素形成视觉对比
  • 使用 1px #ddd 的边框将导航元素与背景边缘以及彼此分隔开
  • 在需要视觉分隔的元素之间设置 15px 的间距
  • 导航元素的一侧会使用 4px 的边框来显示悬停或聚焦状态,颜色为 #0073aa
  • 自定义器文本的颜色为 color: #555d66,而导航元素的悬停和聚焦状态则使用 #0073aa

自定义设置类型

默认情况下,自定义器支持将设置保存为选项或主题修改。但这一行为可以被轻松覆盖,从而实现将设置手动保存并预览到 WordPress 数据库的 wp_options 表之外,或应用其他自定义处理方式。要开始使用,可在添加设置时指定除 option 或 theme_mod 之外的类型(几乎可以使用任何字符串):

$wp_customize->add_setting( $nav_menu_setting_id, array(
  'type' => 'nav_menu',
  'default' => $item_ids,
) );

当关联控件中的该设置值发生变化时,它将不再被保存或预览。此时,可以使用 customize_update_$setting->type 和 customize_preview_$setting->type 函数来实现自定义的保存与预览功能。以下是来自 Menu Customizer 项目的示例,用于保存菜单项的顺序属性(该设置的值是一个按顺序排列的菜单 ID 数组):

function menu_customizer_update_nav_menu( $value, $setting ) {
  $menu_id = str_replace( 'nav_menu_', '', $setting->id );
  // ...
  $i = 0;
  foreach( $value as $item_id ) { // $value 是按顺序排列的元素 ID 数组。
    menu_customizer_update_menu_item_order( $menu_id, $item_id, $i );
  $i++;
  }
}
add_action( 'customize_update_nav_menu', 'menu_customizer_update_nav_menu', 10, 2 );

以下是同一个插件实现导航菜单项预览的方式(请注意,此示例需要 PHP 5.3 或更高版本):

function menu_customizer_preview_nav_menu( $setting ) {
  $menu_id = str_replace( 'nav_menu_', '', $setting->id );
  add_filter( 'wp_get_nav_menu_items', function( $items, $menu, $args ) use ( $menu_id, $setting ) {
    $preview_menu_id = $menu->term_id;
    if ( $menu_id == $preview_menu_id ) {
      $new_ids = $setting->post_value();
      foreach ( $new_ids as $item_id ) {
        $item = wp_setup_nav_menu_item( $item );
        $item->menu_order = $i;
        $new_items[] = $item;
        $i++;
      }
      return $new_items;
    } else {
      return $items;
    }
  }, 10, 3 );
}
add_action( 'customize_preview_nav_menu', 'menu_customizer_preview_nav_menu', 10, 2 );