In almost every theme you see a check if dynamic_sidebar() did returned false then show HTML inside the if statement. See the sidebar.php of Twenty Eleven. Twenty Twelve and Twenty thirteen don’t do this anymore. This is ugly in two ways. The first one is that this kind of logic shouldn’t be in the template files. It can easily be in functions.php or a file included by it. The second is that you generate duplicated code. Probably by using the_widget() you can accomplish the same.

In this tutorial I show how you can have a function like my_register_default_widget() which allows you to pass the sidebar, widget class and the settings of the widget. The first thing is to understand this code base. The file /wp-includes/widgets.php includes code for functions like register_widget, register_sidebars, register_sidebar and dynamic_sidebar. It also has the following classes as WP_Widget, WP_Widget_Factory.

WP_Widget_Factory

WP_Widget is well know but what about WP_Widget_Factory. It’s a simple class that contains all the widgets. The code below is the whole class. So when you call register_widget you are calling WP_Widget_Factory::register(). WP_Widget_Factory is initiate in wp-settings.php as a global wp_widget_factory.

class WP_Widget_Factory {
	var $widgets = array();

	function WP_Widget_Factory() {
		add_action( 'widgets_init', array( $this, '_register_widgets' ), 100 );
	}

	function register($widget_class) {
		$this->widgets[$widget_class] = new $widget_class();
	}

	function unregister($widget_class) {
		if ( isset($this->widgets[$widget_class]) )
			unset($this->widgets[$widget_class]);
	}

	function _register_widgets() {
		global $wp_registered_widgets;
		$keys = array_keys($this->widgets);
		$registered = array_keys($wp_registered_widgets);
		$registered = array_map('_get_widget_id_base', $registered);

		foreach ( $keys as $key ) {
			// don't register new widget if old widget with the same id is already registered
			if ( in_array($this->widgets[$key]->id_base, $registered, true) ) {
				unset($this->widgets[$key]);
				continue;
			}

			$this->widgets[$key]->_register();
		}
	}
}

Register a widget to a sidebar

We talked about the widget factory and we are going to use that to check if the class does exists. But we still need to store the settings. And for that we need a private property called custom_settings that is an array.

The code is pretty self explained. The only ugly thing is the how the id is generated. We can do look up in the options for it and check it. Also we need to write a method to handle the option value of every widget class that gets registered.

public function register_default_widget( $sidebar, $class_name, $settings ) {
	global $_wp_sidebars_widgets, $wp_widget_factory;

	if( empty( $_wp_sidebars_widgets[ $sidebar ] ) ) {
		if( isset( $wp_widget_factory->widgets[ $class_name ] ) ) {
			$class = $wp_widget_factory->widgets[ $class_name ];

			if( ! isset( $this->custom_settings[ $class->option_name ] ) ) { 
				$this->custom_settings[ $class->option_name ] = array();
				add_filter( 'option_' . $class->option_name, array( $this, '_register_default_widgets_settings' ) );
			}
			
			$id = 900 + count( $this->custom_settings[ $class->option_name ] );
			$this->custom_settings[ $class->option_name ][ $id ] =  $settings;
			$_wp_sidebars_widgets[ $sidebar ][] = $class->id_base . '-' . $id;

			return true;
		}
	}

	return false;
}

Manipulating the option value

This function needs a bit of magic by calling current_filter() to know which filter is calling this method. From that we can retrieve the widget this function needs to deal with. The isset check isn’t really needed but it’s always better to be save and when the custom settings are there merge it with the default one.

function _register_default_widgets_settings( $value ) {
	$filter = substr( current_filter(), 7);

	if( isset( $this->custom_settings[ $filter ] ) )
		$value = $value + $this->custom_settings[ $filter ];

	return $value;
}

And by this you can registrate a widget to a sidebar with only code. The whole class can be found on GitHub as a gist: https://gist.github.com/markoheijnen/3110599.

Leave a Reply

Your email address will not be published. Required fields are marked *