WordPress 可以将一些常用的模块封装成一个个的小工具,并提供对应的配置选项,这样我们可以通过拖拽的方式自定义摆放各种小工具实现网页的布局。Widgets API 即是用于用户自定义小工具的一个类,该 API 定义在 wp-includes/widgets.php 文件下。常用的方法主要有:

  • register_sidebar():注册一个小工具侧栏。
  • unregister_sidebar():卸载一个小工具侧栏。
  • is_registered_sidebar():判断某个小工具是否已经注册。
  • dynamic_sidebar():前端显示指定的小工具。
  • is_dynamic_sidebar():判断主题是否启用了小工具功能。
  • is_active_sidebar():判断某个小工具侧栏是否被激活(是否添加了小工具)。
  • register_widget():注册一个小工具(注意不是侧栏位置)。
  • unregister_widget():卸载一个小工具。
  • is_active_widget():判断某个小工具是否被激活使用。

register_sidebar()

WordPress 默认是不显示小工具的,我们需要通过 register_sidebar() 注册一个侧栏位置,小工具功能才会显示。

register_sidebar( array|string $args = array() )

$args: 数组,用于配置小工具参数。

  • id:字符串,后台小工具侧栏位置的唯一识别 ID 。
  • name:字符串,后台小工具侧栏位置的标题名称。
  • description:字符串,后台小工具侧栏位置的描述信息,一般不用。
  • before_sidebar:字符串,HTML 标签内容,显示在 sidebar 输出的前面,默认为空,一般不用。
  • after_sidebar:字符串,HTML 标签内容,显示在 sidebar 输出的后面,默认为空,一般不用。
  • before_widget:字符串,HTML 标签内容,显示在 widget 输出的前面,自定义小工具时可通过 $before_widget 引用。
  • after_widget:字符串,HTML 标签内容,显示在 widget 输出的末尾,自定义小工具时可通过 $after_widget 引用。
  • before_title:字符串,HTML 标签内容,显示在 widget 输出的标题前面,自定义小工具时可通过 $before_title 引用。
  • after_title:字符串,HTML 标签内容,显示在 widget 输出的标题后面,自定义小工具时可通过 $after_title 引用。

简单地说,register_sidebar 定义一个位置,我们通过 dynamic_sidebar 控制其在网页的哪个位置显示,Sidebar 里可以放置多个 Widget ,before_sidebar、after_sidebar、before_widget、after_widget、before_title、after_title 可以预定义一段 HTML 代码,这些代码可以在我们自定义小工具的时候直接引用,这样在输出小工具内容时,预定义的 HTML 代码会自动渲染。一般情况下一个网站会定义多个侧栏位置,定义方式如下:

// 自定义侧栏位置
if ( !function_exists('diy_register_sidebar') ) {
	function diy_register_sidebar(){
		$sidebars = array(
			'diy_sidebar'           => '自定义侧栏位置01',
            'diy_sidebar_2'         => '自定义侧栏位置02',
		);
		foreach ( $sidebars as $key => $value ) {
			register_sidebar(array(
				'id'            => $key,
				'name'          => $value,
				'before_widget' => '<section id="%1$s" class="widget %2$s">',    // 显示在整个小工具前面
				'after_widget'  => '</section>',    // 显示在整个小工具后面
				'before_title'  => '<h3>',    // 显示在小工具标题前面
				'after_title'   => '</h3>'    // 显示在小工具标题后面
			));
		};
	}
	add_action( 'widgets_init', 'diy_register_sidebar' );
}

注册完成后显示如下:

unregister_sidebar()

注册的小工具如果不需要了,可以通过 unregister_sidebar 取消注册。

unregister_sidebar( string $id )

$id:字符串,后台小工具侧栏位置的唯一识别 ID 。

is_registered_sidebar()

判断小工具是否注册了,避免重复注册可能导致的错误,返回布尔值。

is_registered_sidebar( string $id )

$id:字符串,后台小工具侧栏位置的唯一识别 ID 。

dynamic_sidebar()

在前端页面显示指定小工具位置内的所有小工具。

dynamic_sidebar( int|string $index = 1 )

$index:整型或字符串,默认显示索引为 1 的侧栏位置下的小工具,如果注册侧栏位置时指定了 id 或者 name 属性,则可以传入指定的 id 或 name 值用来显示特定的侧栏。

is_dynamic_sidebar()

判断主题是否启用或者使用了小工具功能,不需要传递参数,主题扩展功能时判断一下避免错误,返回布尔值。

is_dynamic_sidebar()

is_active_sidebar()

判断主题中某个注册了的侧栏是否已经激活使用(是否添加了小工具),返回布尔值。比如:我们可以用它判断用户是否使用了某个侧栏,进而调整网页布局。

is_active_sidebar( string|int $index )

$index:整型或字符串,注册时用到的侧栏 id 或者 name 属性。

unregister_widget()

默认的小工具无论是样式还是功能上往往不能满足我们的使用,所以一般情况下需要将默认小工具卸载掉,然后自定义一下主题中用到的小工具。

unregister_widget( string|WP_Widget $widget )

$widget:一个 WP_Widget 实例,或者 WP_Widget 的类名。

// 卸载所有默认小工具
if ( !function_exists('diy_unregister_widget')) {
	function diy_unregister_widget(){
		unregister_widget('WP_Widget_Pages');
		unregister_widget('WP_Widget_Calendar');
		unregister_widget('WP_Widget_Archives');
		unregister_widget('WP_Widget_Links');
		unregister_widget('WP_Widget_Media_Audio');
		unregister_widget('WP_Widget_Media_Image');
		unregister_widget('WP_Widget_Media_Video');
		unregister_widget('WP_Widget_Media_Gallery');
		unregister_widget('WP_Widget_Meta');
		unregister_widget('WP_Widget_Search');
		unregister_widget('WP_Widget_Text');
		unregister_widget('WP_Widget_Categories');
		unregister_widget('WP_Widget_Recent_Posts');
		unregister_widget('WP_Widget_Recent_Comments');
		unregister_widget('WP_Widget_RSS');
		unregister_widget('WP_Widget_Tag_Cloud');
		unregister_widget('WP_Nav_Menu_Widget');
		unregister_widget('WP_Widget_Custom_HTML');
	}
	add_action('widgets_init', 'diy_unregister_widget');
}

register_widget()

使用该方法可以注册一个小工具,显示在 WordPress 小工具面板下,但是在注册前需要先定义一个小工具类,关于这部分的内容放在最后,这里只展示下注册方法及注册后的结果。

register_widget( string|WP_Widget $widget )

$widget:一个 WP_Widget 实例,或者 WP_Widget 的类名。

is_active_widget()

判断某个小工具是否已经在前端显示,避免重复显示引起的排版错误,返回布尔值或指定小工具的 ID 。

is_active_widget( callable|false $callback = false, int|false $widget_id = false, string|false $id_base = false, bool $skip_inactive = true )
  • $callback:回调或 false ,要检查的小工具回调函数,默认:false。
  • widget_id:整型或 false ,要检查的小工具 ID ,默认:false。
  • id_base:字符串或 false,通过 WP_Widget 类扩展创建的小工具的基本 ID ,默认:false。
  • skip_inactive:布尔值,是否在 ‘wp_inactive_widgets’ 中检查,默认:true。

定义一个小工具

好吧,这才是重中之重。创建小工具实则是创建一个对 WP_Widget 类库的集成,并配置相应的内容。WP_Widget 类定义在 wp-includes/class-wp-widget.php 文件中。一个常见的自定义小工具包含 __construct()、form()、update()、widget() 四部分。

  • __construct():小工具的基本属性配置。
  • form():小工具后台配置选项。
  • update():更新小工具设置。
  • widget():小工具前端输出内容。

__construct()

PHP 5 新增的构造方法,用于在创建一个实例之前传入相应的参数配置实例。这里传入小工具的基本配置信息,这个方法必须配置,不配置会报错。

WP_Widget::__construct( string $id_base, string $name, array $widget_options = array(), array $control_options = array() )
  • $id:字符串,可选,小工具的唯一 ID 值,需为小写,为空则自动使用小工具类名。
  • $name:字符串,可选,小工具的名称,就是后台配置页的标题名称。
  • $widget_options:数组,可选小工具的配置项,与 wp_register_sidebar_widget 方法中的 options 选项一致。
    • classname:字符串,小工具输出外层 HTML 标签的类名,默认值为输出回调名称的缩写。
    • description:字符串,小工具后台配置面板的描述信息。
  • $control_options:数组,可选,小工具的控制项,与 wp_register_widget_control 方法中的 options 选项一致,一般不用。
    • height:整型,后台小工具的高度,一般不用,默认 200 。
    • width:整型,后台小工具的宽度,默认 250 。
    • id_base:整型或字符串,小工具的基准 ID ,多窗口小工具必须使用,使用后小工具 ID 值变为 {ParseError: KaTeX parse error: Expected ‘EOF’, got ‘}’ at position 8: id_base}̲-{unique_number} 。

示例:

// 小工具初始化
public function __construct(){
	parent::__construct( 'diy_widget', __('自定义小工具', 'textdomain'), array(
		'classname'  => 'diy-widget-class',
		'description'=> '自定义小工具描述'
	));
}

form()

一般的小工具都会提供后台配置选项,这些配置选项在 form() 方法中配置。常用的控件类型包括:文本、单选按钮、复选框、下拉选择、下拉菜单、链接、数字等。

WP_Widget::form( array $instance )

$instanse:数组,小工具当前的配置。
form() 方法的返回值为字符串类型,默认为 noform 。

示例:

// 后台设置表单
public function form($instance){
	$defaults = array(
		'text'        => '默认单行文本控件的值',
		'textarea'    => '默认多行文本控件的值',
		'radio'       => '默认选中的按钮值',
		'checkbox'    => 'on',
		'select'      => 'selectOpt2',
		'nav_menu'    => 0,
		'number'      => 5,
		'link'        => "https://blog.quietguoguo.com"
	);
	
	
	$instance = wp_parse_args( (array) $instance, $defaults );   // 将用户定义的参数合并到默认数组中去
	extract($instance);    // 从数组中将变量导入到当前的符号表
	?>
	<!-- 单行文本 -->
	<p>
		<label for="<?php echo $this->get_field_id( 'text' ); ?>">
			<?php _e( '单行文本', 'textdomain'); ?>
		</label>
		<input type="text" id="<?php echo $this->get_field_id('text'); ?>" name="<?php echo $this->get_field_name('text'); ?>" value="<?php echo esc_attr( $instance['text'] ); ?>" class="text">
		
	</p>
	<!-- 多行文本 -->
	<p>
		<label for="<?php echo $this->get_field_id( 'textarea' ); ?>">
			<?php _e( '多行文本', 'textdomain'); ?>
		</label>
		<textarea  id="<?php echo $this->get_field_id('textarea'); ?>" name="<?php echo $this->get_field_name('textarea'); ?>" rows="3" cols="20"><?php echo esc_textarea( $instance['textarea'] ); ?></textarea>
	</p>
	<!-- 单选按钮 -->
	<p>
		<input type="radio" id="radio-1" name="<?php echo $this->get_field_name( 'radio' ); ?>" <?php checked( $radio, '默认选中的按钮值' ); ?> value="默认选中的按钮值" class="radio" />
		<label for="radio-1">
			<?php _e( '单选按钮,默认选中', 'textdomain'); ?>
		</label>
		<br/>
		<input type="radio" id="radio-2" name="<?php echo $this->get_field_name( 'radio' ); ?>" <?php checked( $radio, '默认未选中的按钮值' ); ?> value="默认未选中的按钮值" class="radio" />
		<label  for="radio-2">
			<?php _e( '单选按钮,默认未选中', 'textdomain'); ?>
		</label>
	</p>
	<!-- 复选框 -->
	<p>
		<input type="checkbox" id="<?php echo $this->get_field_id( 'checkbox' ); ?>" name="<?php echo $this->get_field_name( 'checkbox' ); ?>" <?php checked( $checkbox, 'on' ); ?> class="checkbox" />
		<label  for="<?php echo $this->get_field_id( 'checkbox' ); ?>">
			<?php _e( '复选框,默认选中', 'textdomain'); ?>
		</label>
	</p>
	<!-- 下拉选择 -->
	<p>
		<label for="<?php echo $this->get_field_id( 'select' ); ?>">
			<?php _e( '下拉选择', 'textdomain'); ?>
		</label>
		<select id="<?php echo $this->get_field_id( 'select' ); ?>" name="<?php echo $this->get_field_name( 'select' ); ?>">
			<option value ="selectOpt1" <?php selected( $select, 'selectOpt1'); ?>>下拉选择值1</option>
			<option value ="selectOpt2" <?php selected( $select, 'selectOpt2'); ?>>下拉选择值2</option>
			<option value ="selectOpt3" <?php selected( $select, 'selectOpt3'); ?>>下拉选择值3</option>
		</select>
	</p>
	<!-- 菜单选择 -->
	<?php $menus = wp_get_nav_menus(); ?>
	<p>
		<label for="<?php echo $this->get_field_id( 'nav_menu' ); ?>">
			<?php _e( '选择菜单' ); ?>
		</label>
		<select id="<?php echo $this->get_field_id( 'nav_menu' ); ?>" name="<?php echo $this->get_field_name( 'nav_menu' ); ?>">
			<option value="0"><?php _e( '&mdash; Select &mdash;' ); ?></option>
			<?php foreach ( $menus as $menu ) : ?>
				<option value="<?php echo esc_attr( $menu->term_id ); ?>" <?php selected( $nav_menu, $menu->term_id ); ?>>
					<?php echo esc_html( $menu->name ); ?>
				</option>
			<?php endforeach; ?>
		</select>
	</p>
	<!-- 数量调整 -->
	<p>
		<label for="<?php echo $this->get_field_id( 'number' ); ?>"><?php _e( '数量调整:' ); ?></label>
		<input type="number" id="<?php echo $this->get_field_id( 'number' ); ?>" name="<?php echo $this->get_field_name( 'number' ); ?>" step="1" min="1" value="<?php echo $number; ?>" size="3" class="tiny-text" />
	</p>
	<!-- 自定义链接 -->
	<p>
		<label for="<?php echo $this->get_field_id( 'link' ); ?>">
			<?php _e( '链接文本', 'textdomain'); ?>
		</label>
		<input type="url" id="<?php echo $this->get_field_id('link'); ?>" name="<?php echo $this->get_field_name('link'); ?>" value="<?php echo esc_attr( $instance['link'] ); ?>" class="url">
	</p>
	<?php
}

后台设置选项如下图所示,你可以自定义下样式。

update()

update 方法用于更新小工具配置项。

WP_Widget::update( array $new_instance, array $old_instance )
  • $new_instance:小工具新的配置项(新设置的配置项)。
  • $old_instance:小工具原有配置项(数据库中的配置项)。

示例:

// 更新设置选项
public function update( $new_instance, $old_instance ) {
	$instance = $old_instance;
	$instance['text']       = $new_instance['text'];
	$instance['textarea']   = $new_instance['textarea'];
	$instance['radio']      = $new_instance['radio'];
	$instance['checkbox']   = $new_instance['checkbox'];
	$instance['select']     = $new_instance['select'];
	$instance['nav_menu']   = $new_instance['nav_menu'];
	$instance['number']     = $new_instance['number'];
	$instance['link']     = $new_instance['link'];
	return $instance;
}

更新后的小工具数据存储在数据库 wp_options 表下对应 id 字段下,同一个小工具被引用多次,每个小工具的配置项都会独立保存在这里。

widget()

widget 方法输出前端显示内容,你可以在这里设计小工具的前端显示结构及样式。

WP_Widget::widget( array $args, array $instance )
  • args:默认参数,就是使用 register_sidebar 注册小工具位置时的配置项,比如:before_sidebar、after_sidebar、before_widget、after_widget、before_title、$after_title 等。
  • $instance:小工具配置项的值。

示例:

// 前端生成显示
public function widget( $args, $instance ) {
	extract( $args );
	extract( $instance );
	
	$text = apply_filters('widget_name', $instance['text']);
	$textarea = apply_filters('widget_name', $instance['textarea']);
	$radio = apply_filters('widget_name', $instance['radio']);
	$checkbox = apply_filters('widget_name', $instance['checkbox']);
	$select = apply_filters('widget_name', $instance['select']);
	$nav_menu = apply_filters('widget_name', $instance['nav_menu']);
	$number = apply_filters('widget_name', $instance['number']);
	$link = apply_filters('widget_name', $instance['link']);
	
	echo $before_widget;

	echo '<div class="item"><h3>单行文本输出值:&nbsp;</h3><div>' . $text . '</div></div>';
	
	echo '<div class="item"><h3>多行文本输出值:&nbsp;</h3><div>' . $textarea . '</div></div>';

	echo '<div class="item"><h3>单选按钮输出值:&nbsp;</h3><div>' . $radio . '</div></div>';

	echo '<div class="item"><h3>复选框输出值:&nbsp;</h3><div>' . $checkbox . '</div></div>';

	echo '<div class="item"><h3>下拉选择输出值:&nbsp;</h3><div>' . $select . '</div></div>';

	echo '<div class="item"><h3>菜单 ID 输出值:&nbsp;</h3><div>' . $nav_menu . '</div></div>';

	echo '<div class="item"><h3>数量调整输出值:&nbsp;</h3><div>' . $number . '</div></div>';

	echo '<div class="item"><h3>自定义链接输出值:&nbsp;</h3><div>' . $link . '</div></div>';

	echo $after_widget;
}

前端显示结果如下:


如此,一个普通的前端小工具即构建完成。