定制器对象

自定义API是面向对象的,有四种主要的定制器对象类型:面板(Panels)、板块(Sections)、设置(Settings)和控件(Controls)。设置将UI元素(控件)与保存在数据库中的设置联系起来。板块是控件的UI容器,以改善其组织。面板是将多个板块组合在一起的容器。

每个定制器对象都由一个PHP类表示,所有的对象都由定制器管理(Customize Manager)对象(WP_Customize_Manager)管理。

Graphic showing the relationship between each type of customize object

要添加、删除或修改任何定制器对象,以及访问定制器管理,请使用customize_register钩子。

function themeslug_customize_register( $wp_customize ) {
  // Do stuff with $wp_customize, the WP_Customize_Manager object.
}
add_action( 'customize_register', 'themeslug_customize_register' );

定制器管理为每个定制器对象类型提供 add_, get_, 和 remove_ 方法,每个方法都与一个id一起工作。get_ 方法允许直接修改添加控件时指定的参数。

add_action('customize_register','my_customize_register');
function my_customize_register( $wp_customize ) {
  $wp_customize->add_panel();
  $wp_customize->get_panel();
  $wp_customize->remove_panel();

  $wp_customize->add_section();
  $wp_customize->get_section();
  $wp_customize->remove_section();

  $wp_customize->add_setting();
  $wp_customize->get_setting();
  $wp_customize->remove_setting();

  $wp_customize->add_control();
  $wp_customize->get_control();
  $wp_customize->remove_control();
}

注意:主题一般不应该用 get 方法修改核心板块和面板,因为主题不应该修改核心的、与主题无关的功能。我们鼓励插件在必要时使用这些功能。主题不应该"重新组织"不是由主题添加的定制器板块。

设置

设置(Settings)处理实时预览、保存和净化你的定制器对象。每个设置都由一个控件对象管理,在添加一个新的设置时,有几个参数可用。

$wp_customize->add_setting( 'setting_id', array(
  'type' => 'theme_mod', // or 'option'
  'capability' => 'edit_theme_options',
  'theme_supports' => '', // Rarely needed.
  'default' => '',
  'transport' => 'refresh', // or postMessage
  'sanitize_callback' => '',
  'sanitize_js_callback' => '', // Basically to_json.
) );

重要提示:不要使用类似 widget_*sidebars_widgets[*]nav_menu[*]nav_menu_item[*]的设置 ID。这些设置ID模式分别保留给小工具实例、侧边栏、导航菜单和导航菜单项。如果你需要在你的设置ID中使用 "widget",把它作为后缀而不是前缀,例如 "homepage_widget"

设置有两种主要类型:选项(option)和主题修改(theme_mod)。选项直接存储在WordPress数据库的wp_options表中,并应用于网站,而不考虑主题的启用。主题应该尽量少添加选项类型的设置。另一方面,主题修改是特定于某个主题的,大多数主题选项应该是theme_mod。例如,一个自定义的CSS插件可以将一个自定义的主题css设置注册为theme_mod,允许每个主题有一套独特的CSS规则,在切换主题时不会丢失CSS,然后再切换回来。

customize-theme-mods-options

Theme_mod 和 option 的设置类型例子

通常最重要的是设置的默认值以及它的数据净化回调,这将确保没有不安全的数据被存储在数据库中。典型的主题用法:

$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',
) );

请注意,定制器可以使用选项类型处理存储为键值数组的设置的选项。这允许多个设置被存储在一个不是theme_mod的选项中。要检索和使用你的定制器选项值,请根据设置ID使用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()的第二个参数是默认值,它应该与你在添加设置时设置的默认值一致。

 

控件

控件(Controls)是创建用户界面的主要定制器对象。 具体来说,每个控件都必须与一个设置相关联,该设置将把用户输入的数据从控件中保存到数据库中(除了在实时预览中显示它并对其进行净化)。控件可以由定制器管理添加,并以最小的努力提供一套强大的UI选项:

$wp_customize->add_control( 'setting_id', array(
  'type' => 'date',
  'priority' => 10, // Within the section.
  'section' => 'colors', // Required, core or custom.
  'label' => __( 'Date' ),
  'description' => __( 'This is a date control with a red border.' ),
  'input_attrs' => array(
    'class' => 'my-custom-class-for-js',
    'style' => 'border: 1px solid #900',
    'placeholder' => __( 'mm/dd/yyyy' ),
  ),
  'active_callback' => 'is_front_page',
) );

在添加控件时,最重要的参数是它的类型(type),这决定了定制器将显示什么类型的UI。核心提供了几种内置的控件类型:

  • <input> 任何允许类型的元素(见下文)
  • checkbox
  • textarea
  • radio (用choices参数传递一个数组,(键)选项值 => (值)标签)
  • select (用choices参数传递一个数组,(键)选项值 => (值)标签)
  • dropdown-pages (用allow_addition参数,允许用户从控件中添加新的页面)

对于html input元素支持任何input类型,只需在添加控件时将类型属性值传递给type参数。这样就可以支持text, hidden, number, range, url, tel, email, search, time, date, datetime, 和week等控件类型,但要等待浏览器支持。

控件在显示之前必须被添加到一个板块(板块必须包含要显示的控件)。这可以通过在添加控件时指定section参数来实现。下面是一个添加基本的textarea控件的例子。

$wp_customize->add_control( 'custom_theme_css', array(
  'label' => __( 'Custom Theme CSS' ),
  'type' => 'textarea',
  'section' => 'custom_css',
) );

而这里是一个基本的range(滑块)控件的例子。请注意,大多数浏览器不会显示这个控件的数字值,因为range input类型是为精确值不重要的设置而设计的。如果数字值很重要,请考虑使用number类型。input_attrs参数将把一个数组:属性 => 值 映射到input元素上的属性,可以用于从占位符文本到自定义脚本中的数据--JavaScript引用的数据等目的。对于number和range控件,它允许我们设置最小、最大和阶梯值。

$wp_customize->add_control( 'setting_id', array(
  'type' => 'range',
  'section' => 'title_tagline',
  'label' => __( 'Range' ),
  'description' => __( 'This is the range control 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' => __( 'Accent Color', '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' => __( 'Featured Home Page Image', 'theme_textdomain' ),
  'section' => 'media',
  'mime_type' => 'image',
) ) );
$wp_customize->add_control( new WP_Customize_Media_Control( $wp_customize, 'audio_control', array(
  'label' => _( 'Featured Home Page Recording', '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,它提供了一个在选择图片后裁剪的接口。这对于需要一个特定的长宽比的情况是很有用的。

 

板块

板块(Sections)是自定义控件的UI容器。虽然你可以在核心板块添加自定义控件,但如果你有超过几个选项,你可能想添加一个或多个自定义板块。使用WP_Customize_Manager对象的add_section方法来添加一个新的板块。

$wp_customize->add_section( 'custom_css', array(
  'title' => __( 'Custom CSS' ),
  'description' => __( 'Add custom CSS here' ),
  'panel' => '', // Not typically needed.
  'priority' => 160,
  'capability' => 'edit_theme_options',
  'theme_supports' => '', // Rarely needed.
) );

你只需要包含那些你想覆盖默认值的字段,例如,默认的优先级(排序)通常是可以接受的。如果你的选项一目了然,那么大多数板块不需要“描述”文本。如果你确实想改变自定义板块的位置,核心板块的优先级见下表:

Title ID Priority (Order)
Site Title & Tagline title_tagline 20
Colors colors 40
Header Image header_image 60
Background Image background_image 80
Menus (Panel) nav_menus 100
Widgets (Panel) widgets 110
Static Front Page static_front_page 120
default 160
Additional CSS custom_css 200

在大多数情况下,只需指定一个或两个参数就可以添加板块。下面是一个添加与主题页脚有关的选项板块的例子:

// Add a footer/copyright information section.
$wp_customize->add_section( 'footer' , array(
  'title' => __( 'Footer', 'themename' ),
  'priority' => 105, // Before Widgets.
) );

 

面板

定制器面板(Panels)API是在WordPress 4.0中引入的,它允许开发者在控件和板块之外创建一个额外的层次结构。面板不仅仅是简单地将控件分组,它被设计用来为定制器提供不同的上下文,比如自定义小工具、菜单,或者也许在未来,编辑文章。板块和面板对象之间有一个重要的技术区别。

在大多数情况下,主题不应该注册自己的面板。板块不需要嵌套在面板下,而且每个板块一般都应该包含多个控件。控件也应该被添加到核心提供的版块中,比如在颜色版块中添加颜色选项。还要确保你的选项尽可能地精简和有效,请看WordPress的哲学。面板被设计为整个功能的上下文,如Widgets、Menus或Posts,而不是作为通用版块的包装。如果你一定要使用面板,你会发现它的API和版块的API几乎是一样的。

$wp_customize->add_panel( 'menus', array(
  'title' => __( 'Menus' ),
  'description' => $description, // Include html tags such as <p>.
  'priority' => 160, // Mixed with top-level-section hierarchy.
) );
$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';
  /**
  * Render the control's content.
  */
  public function render_content() {
  ?>
    <button class="button button-primary" id="create-new-menu-submit" tabindex="0"><?php _e( 'Create Menu' ); ?></button>
  <?php
  }
}

通过对基础控制类的子类化,你可以用自定义功能覆盖任何功能,或者根据你的需要使用核心功能。最常见的覆盖功能是render_content(),因为它允许你用HTML从头开始创建自定义UI。然而,自定义控件应该谨慎使用,因为它们可能会引入与周围的核心用户界面不一致的用户界面,并给用户带来混乱。定制器对象的添加方式与默认控件、板块和面板的添加方式类似。

$wp_customize->add_control(
  new WP_Customize_Color_Control(
    $wp_customize, // WP_Customize_Manager
    'accent_color', // Setting id
    array( // Args, including any custom ones.
      'label' => __( 'Accent Color' ),
      '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标准

自定义控件、板块和面板应该尽可能地与核心的用户界面做法相匹配。这包括依靠wp-admin的标准,例如使用.button.button-primary类。也有一些专门针对定制器的标准(从WordPress 4.7开始)。

  • 白色背景颜色仅用于指示导航和可操作项目(如inputs)
  • 一般的#eee背景色提供了与白色元素的视觉对比
  • 1px #ddd边框将导航元素与背景边距彼此分开
  • 在需要视觉分离的元素之间提供15px的间距
  • 4px边框用于导航元素一侧以显示悬停或焦点,颜色为#0073aa
  • 定制器文本使用颜色#555d66,导航元素的悬停和焦点状态使用#0073aa

 

自定义设置类型

默认情况下,定制器支持将设置保存为选项或主题修改。但是这种行为可以很容易地被覆盖,以便在WordPress数据库的wp_options表之外手动保存和预览设置,或者应用其他自定义处理。要开始的话,在添加你的设置时指定一个选项或主题修改以外的类型(你可以使用几乎任何字符串)。

$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 is ordered array of item ids.
    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 );

而这里是同一个插件如何实现对导航菜单项的预览(注意这个例子需要PHP5.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 );