WordPress multi-widgets

Alexander Prokopenko
Alexander Prokopenko Chief Technical Officer
5 minutes to read

IMPORTANT: This article was written for WordPress sites using versions 2.1 to 2.7. It describes some fixes of the limitations of custom WordPress Widgets. Starting from WordPress 2.8 this issue has been resolved in WordPress core.

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

The article ends on section “Widgets – One or many” with the following: “… 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. As we can’t seem to find them anywhere on the internet, we decided to investigate this subject ourselves.

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

At first, we will look at the general rules of multi widget creation. WordPress does not recommend using the “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 the widget is intended for multiple adding.

But it doesn’t end here. 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

Let’s look at an 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 ));
	}
}

And now, let’s explain:

  • $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 ensure that a unique number is given to each widget (last array in registration functions) while declaration.

Widget control functions

Now we will describe the widget functions in detail:

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 the 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
    <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 indexed with a 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 numbers 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;
	}
}

The 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 of the above pieces. The result will be your first multi-widget! Good luck!

Book a call With our strategist

What we will talk about:
  • you and your business needs;
  • current plans, ideas, and strategy;
  • possible solution to your business challenge.

Describe your business requirements in enough details so we could understand your goal better.

*If an NDA should come first, please let us know.
Cookie notice

This website uses cookies. By using and further navigating this website you accept the use of cookies. Learn more about the information we collect at Privacy policy page.