WordPress has already become a very popular free blog engine mostly thanks to the extensibility with the help of plug-ins. Since version 2.1 famous WordPress Widgets were integrated. This mechanism allows to manage blocks in sidebar. In more difficult sites which are built with WP often there is a need of using different sidebars on different pages. Here come troubles with using widgets. The majority of widgets can be added only once. You can easily find articles on how to create widgets in the Internet. The main article is on WordPress codex site (read wordpress widgets API article).

The article ends on section “Widgets – One or many” with such words: “… WordPress is doing the work for you to instantiate your Widget multiple times if you follow some rules.”. Actually in this article we want to describe these rules, because we can’t find them somewhere in the internet and investigate this subject ourselves.

So the roots were taken from standard functionality for text widget, code has been separated, investigated and described very carefully.

At first we will look at the general rules of multi widget creation. WordPress site does not recommend to use “wp_register_widget_control ()” function. But it is impossible to create multiple widgets without this function. It can accept additional widget parameters, one of them is “id_base“. This parameter specifies that widget is intended for multiple adding.

But do not think that this is all, not so fast ;-) . Each time you add multi-widget it should be declared with other WP declarations. Thus, if you have added 3 widgets you should declare 3 widgets.

Widget Registration

Lets look at the example of such widget registration function.

add_action('init', 'widget_name_multi_register');
function widget_name_multi_register() {
 
	$prefix = 'name-multi'; // $id prefix
	$name = __('Multi Widget');
	$widget_ops = array('classname' => 'widget_name_multi', 'description' => __('This is an example of a widget which you can add many times'));
	$control_ops = array('width' => 200, 'height' => 200, 'id_base' => $prefix);
 
	$options = get_option('widget_name_multi');
	if(isset($options[0])) unset($options[0]);
 
	if(!empty($options)){
		foreach(array_keys($options) as $widget_number){
			wp_register_sidebar_widget($prefix.'-'.$widget_number, $name, 'widget_name_multi', $widget_ops, array( 'number' => $widget_number ));
			wp_register_widget_control($prefix.'-'.$widget_number, $name, 'widget_name_multi_control', $control_ops, array( 'number' => $widget_number ));
		}
	} else{
		$options = array();
		$widget_number = 1;
		wp_register_sidebar_widget($prefix.'-'.$widget_number, $name, 'widget_name_multi', $widget_ops, array( 'number' => $widget_number ));
		wp_register_widget_control($prefix.'-'.$widget_number, $name, 'widget_name_multi_control', $control_ops, array( 'number' => $widget_number ));
	}
}

Let’s do some explanations:

  • $prefix – unique ID of your widget
  • $name – any widget name
  • $widget_ops — widget registration options
  • $control_ops — widget control registration options. It keeps the “id_base” parameter!
  • $options = get_option(‘widget_name_multi’); – this line takes saved widgets from the database. They should have the format:
    	array(
    		'3563456' => array(... some params...),   // index is the unique widget number for current id_base. This index is autogenerated  by Wordpress.
    		'2563456' => array(... some params...),
    	)
  • So further there comes a registration of as many widgets as were added. If there are no such widgets a widget with number “-1″ is declared. Please pay attention that a unique number is given to each widget (last array in registration functions) while declaration.

Widget control functions

Now we will detailedly describe widget functions:

Widget function doesn’t differ much from the standard one. Standard function gets only one set of vars. We should add code to separate options for different widgets in different sidebars, so we parse the unique widget number from $widget_id variable:

function widget_name_multi($args, $vars = array()) {
	extract($args);
	// get widget saved options
	$widget_number = (int)str_replace('name-multi-', '', @$widget_id);
	$options = get_option('widget_name_multi');
	if(!empty($options[$widget_number])){
		$vars = $options[$widget_number];
	}
	// widget open tags
	echo $before_widget;
 
	// print title from admin 
	if(!empty($vars['title'])){
		echo $before_title . $vars['title'] . $after_title;
	} 
 
	// print content and widget end tags
	echo '<center>You can add this widget as many times as you want</center>';
	echo $after_widget;
}

The most important is widget control function. It presents the functions of saving and loading data to the widget:

<?php
function widget_name_multi_control($args) {
 
	$prefix = 'name-multi'; // $id prefix
 
	$options = get_option('widget_name_multi');
	if(empty($options)) $options = array();
	if(isset($options[0])) unset($options[0]);
 
	// update options array
	if(!empty($_POST[$prefix]) && is_array($_POST)){
		foreach($_POST[$prefix] as $widget_number => $values){
			if(empty($values) && isset($options[$widget_number])) // user clicked cancel
				continue;
 
			if(!isset($options[$widget_number]) && $args['number'] == -1){
				$args['number'] = $widget_number;
				$options['last_number'] = $widget_number;
			}
			$options[$widget_number] = $values;
		}
 
		// update number
		if($args['number'] == -1 && !empty($options['last_number'])){
			$args['number'] = $options['last_number'];
		}
 
		// clear unused options and update options in DB. return actual options array
		$options = bf_smart_multiwidget_update($prefix, $options, $_POST[$prefix], $_POST['sidebar'], 'widget_name_multi');
	}
 
	// $number - is dynamic number for multi widget, gived by WP
	// by default $number = -1 (if no widgets activated). In this case we should use %i% for inputs
	//   to allow WP generate number automatically
	$number = ($args['number'] == -1)? '%i%' : $args['number'];
 
	// now we can output control
	$opts = @$options[$number];
 
	$title = @$opts['title'];
 
	?>
    Title<br />
		<input type="text" name="<?php echo $prefix; ?/>[< ?php echo $number; ?>][title]" value="< ?php echo $title; ?>" />
	<?php
}
?>

Explanations:

  • $prefix – the same as in the first function — widget ID base
  • $options – array of saved widgets from the database.
  • if (! empty ($ _POST [$prefix]) && is_array ($ _POST)) — checks that there was a “Save” button click and we should perform updates in the database.
  • Database updates take place with the help of universal function “bf_smart_multiwidget_update()” which we will describe later.
  • Further there is control output. We receive widget options stored before by its number and then display them on the screen. The field for input should be obligatory indexed with prefix and number, i.e. name=”prefix[number][option_name]“.
    Note: if it is a new widget, (number == -1) it is necessary to replace the number with “%i%” and the input name attribute will look like: name=”prefix[%i%][option_name]“
    * This trick is necessary. WP will replace this symbols set (%i%) with new autogenerated unique number for every new widget you add to the sidebar.

Universal “Update Helper”

And the last saving function:

// helper function can be defined in another plugin
if(!function_exists('bf_smart_multiwidget_update')){
	function bf_smart_multiwidget_update($id_prefix, $options, $post, $sidebar, $option_name = ''){
		global $wp_registered_widgets;
		static $updated = false;
 
		// get active sidebar
		$sidebars_widgets = wp_get_sidebars_widgets();
		if ( isset($sidebars_widgets[$sidebar]) )
			$this_sidebar =& $sidebars_widgets[$sidebar];
		else
			$this_sidebar = array();
 
		// search unused options
		foreach ( $this_sidebar as $_widget_id ) {
			if(preg_match('/'.$id_prefix.'-([0-9]+)/i', $_widget_id, $match)){
				$widget_number = $match[1];
 
				// $_POST['widget-id'] contain current widgets set for current sidebar
				// $this_sidebar is not updated yet, so we can determine which was deleted
				if(!in_array($match[0], $_POST['widget-id'])){
					unset($options[$widget_number]);
				}
			}
		}
 
		// update database
		if(!empty($option_name)){
			update_option($option_name, $options);
			$updated = true;
		}
 
		// return updated array
		return $options;
	}
}

Firstly, this function is universal and it is possible to write many plug-ins which would use it because the mechanism of saving in ALL plug-ins of this kind should be identical. WordPress dictates the rules here.

So this function does the following:

  • receives already registered sidebars and finds the current one among them.
  • goes through sidebar array (contains widget names) and $_POST[$prefix] array to define which widgets were added/updated, and which were removed.
  • finally, if “$option_name” attribute is not empty then update the database option.

That’s all. You can try to create a plug-in by collecting together all above pieces. As the result you get your first multi-widget! If you have troubles with repeating these steps you can download an example (archive contains the theme with 2 sidebars: default one and the second one at the page “About” plus the multiwidget plugin file). Good luck!

Comments (10)

Leave a Reply