Create Dynamic Custom WordPress Sidebars

This article will take you through creating custom wordpress sidebars for you wordpress theme, allowing you to create and set different sidebars for individual posts and pages.

Custom WordPress Sidebar Code:

Add the following code snippet to your themes functions.php file. This will add a new sidebars menu item to your wordpress administrator interface allowing you to create new sidebars that will show up under Appearance > Widgets.

<?php
/**
 * Custom Sidebar Class
 *
 * Register unlimited custom sidebars with wordpress
 */
class JC_Custom_Sidebar{
 
	private $meta_id = 'jc-sidebar';
	private $meta_key = 'jc-sidebar-meta';
	private $keys = array('_page_sidebar');
 
 
	/**
	 * Initiate Wordpress Hooks
	 * @return void
	 */
	public function __construct(){
 
		add_action('init', array( $this, 'register_sidebar' ));
		add_action('init', array( $this, 'load_sidebars' ));
 
		// manage post meta 
		add_action( 'load-post.php', array($this, 'setup_metabox' ));
		add_action( 'load-post-new.php', array($this, 'setup_metabox' ));
		add_action( 'save_post', array($this, 'save_metabox'), 10, 2 );
 
	}
 
	/**
	 * Register Sidebar Poststype
	 * @return void
	 */
	public function register_sidebar(){
		register_post_type( 'sidebar', array(
			'labels' => array(
				'name' => 'Sidebars',
				'title' => 'Sidebars'
			),
			'public' => false,		// hide the post type from public
			'has_archive' => false,
			'show_ui' => true
 
		));
	}
 
	/**
	 * Register Sidebars with Wordpress
	 * @return void
	 */
	public function load_sidebars(){
		$sidebars = new WP_Query(array(
			'post_type' => 'sidebar'
		));
 
		if($sidebars->have_posts()){
			while($sidebars->have_posts()){
				$sidebars->the_post();
 
				global $post;
 
				register_sidebar( array(
				    'id'          => 'jc-sidebar-' . $post->post_name ,
				    'name'        => __( $post->post_title ),
				    'description' => __( $post->post_content ),
				) );
			}
		}
	}
 
	/**
	 * Register Sidebar Meta Box
	 * @return void
	 */
	public function setup_metabox(){
		add_action( 'add_meta_boxes', array($this, 'add_metabox' ));
	}
 
	/**
	 * Add Sidebar Meta box
	 * @return  void
	 */
	public function add_metabox(){
		add_meta_box($this->meta_id, esc_html__( 'Sidebar'), array($this, 'show_metabox'), null, 'normal', 'default');
	}
 
	/**
	 * Output Sidebar Select Menu
	 * @param  stdObj $object
	 * @return void
	 */
	function show_metabox($object){
		wp_nonce_field( basename( __FILE__ ), $this->meta_key.'_nonce' );
		$values = $this->get_meta( $object->ID);
		extract($values);
		?>
		<p>Choose Sidebar: <select id="_page_sidebar" name="<?php echo $this->meta_id; ?>[_page_sidebar]">
			<option value="0">Default</option>
			<?php
			$sidebars = new WP_Query(array(
				'post_type' => 'sidebar'
			));
			?>
			<?php while($sidebars->have_posts()): $sidebars->the_post(); ?>
			<?php
			$selected = '';
			if(intval($_page_sidebar) == get_the_ID())
				$selected = " selected=\"selected\"";
			 ?>
			<option value="<?php echo get_the_ID(); ?>"<?php echo $selected; ?>><?php echo get_the_title(); ?></option>
			<?php endwhile; ?>
			<?php wp_reset_postdata(); ?>
		</select></p>
		<?php
	}
 
	/**
	 * Retrieve Post Meta Values
	 * @param  int $post_ID
	 * @return array
	 */
	public function get_meta($post_ID){
		$values = array();
		$default = array(
			'_page_sidebar' => null,
		);
 
		foreach($this->keys as $key){
			$values[$key] = get_post_meta( $post_ID, $key, true );
		}
 
		return is_array($values) ? array_merge($default, $values) : $default;
	}
 
	/**
	 * Save Sidebar meta box
	 * @param  int $post_id
	 * @param  stdObj $post
	 * @return void
	 */
	public function save_metabox($post_id, $post){
		if ( !isset( $_POST[$this->meta_key.'_nonce'] ) || !wp_verify_nonce( $_POST[$this->meta_key.'_nonce'], basename( __FILE__ ) ) )
			return $post_id;
		// Get the post type object. 
 
		$post_type = get_post_type_object( $post->post_type );
 
		// Check if the current user has permission to edit the post. 
		if ( !current_user_can( $post_type->cap->edit_post, $post_id ) )
			return $post_id;
 
		foreach($this->keys as $key)
		{
			$name = isset( $_POST[$this->meta_id][$key] ) ?  $_POST[$this->meta_id][$key] : '' ;
			$value = get_post_meta( $post_id, $key, true );
 
			// If a new meta value was added and there was no previous value, add it. 
			if ( $name && '' == $value )
				add_post_meta( $post_id, $key, $name, true );
 
			// If the new meta value does not match the old value, update it. 
			elseif ( $name && $name != $value )
				update_post_meta( $post_id, $key, $name );
 
			// If there is no new meta value but an old value exists, delete it. 
			elseif ( '' == $name && $value )
				delete_post_meta( $post_id, $key, $value );
		}
	}
}
 
new JC_Custom_Sidebar();
?>

Displaying a dynamic sidebar within your wordpress theme

To output a sidebar within your wordpress theme, add the following code to your sidebar.php file or section of code that you wish to display a sidebar.

<?php
global $post;
 
$sidebar = get_post_meta( $post->ID, '_page_sidebar', true );
if($sidebar){
	$sidebar_post = get_post($sidebar);
	dynamic_sidebar( 'jc-sidebar-' . $sidebar_post->post_name );
}
?>

Thanks to Roshan for pointing out my mistake when outputting the sidebars.

Custom WordPress Sidebar Code Explanation

Register our Sidebars Post Type

The following snippet is similar when registering any custom post type with wordpress, it uses wordpress’s register_post_type function to register our custom post type with the passed parameters.

public function register_sidebar(){
	register_post_type( 'sidebar', array(
		'labels' => array(
			'name' => 'Sidebars',
			'title' => 'Sidebars'
		),
		'public' => false,		// hide the post type from public
		'has_archive' => false,
		'show_ui' => true
	));
}

Registering our Sidebars with WordPress

The following snippet uses the WP_Query class to get all our created sidebars and register them with wordpress.

public function load_sidebars(){
	$sidebars = new WP_Query(array(
		'post_type' => 'sidebar'
	));
 
	if($sidebars->have_posts()){
		while($sidebars->have_posts()){
			$sidebars->the_post();
 
			global $post;
 
			register_sidebar( array(
			    'id'          => 'jc-sidebar-' . $post->post_name ,
			    'name'        => __( $post->post_title ),
			    'description' => __( $post->post_content ),
			) );
		}
	}
}

Creating the sidebar dropdown menu

Now that we have registered our custom sidebars from the post type, we have to be able to choose which sidebar to display on each page / post. We do this by adding a custom meta field for all post types by hooking into WordPress load-post.php hooks.

To make this easier for ourselves we create 3 class variables at the top of our class that can be accessed within all our functions.

private $meta_id = 'jc-sidebar';
private $meta_key = 'jc-sidebar-meta';
private $keys = array('_page_sidebar');

$meta_id – This is the id used when saving our sidebar select menu
$meta_key – This is the id used for the WordPress nonce field
$keys – This array contains the a list of all fields that should be saved

The following snippet is used to return an array of our saved post meta values which loops through all registered meta values from our classes $keys variable, if any value is set that will be returned, otherwise null will be returned.

public function get_meta($post_ID)
{
	$values = array();
	$default = array(
		'_page_sidebar' => null,
	);
 
	foreach($this->keys as $key){
		$values[$key] = get_post_meta( $post_ID, $key, true );
	}
 
	return is_array($values) ? array_merge($default, $values) : $default;
}

Register our sidebar dropdown

The following snippet of code creates the select menu that will be used to set which sidebar is displayed for the current post_type.

<?php
public function setup_metabox(){
	add_action( 'add_meta_boxes', array($this, 'add_metabox' ));
}
 
public function add_metabox(){
	add_meta_box($this->meta_id, esc_html__( 'Sidebar'), array($this, 'show_metabox'), null, 'normal', 'default');
}
 
function show_metabox($object){
	wp_nonce_field( basename( __FILE__ ), $this->meta_key.'_nonce' );
	$values = $this->get_meta( $object->ID);
	extract($values);
	?>
	<p>Choose Sidebar: <select id="_page_sidebar" name="<?php echo $this->meta_id; ?>[_page_sidebar]">
		<option value="0">Default</option>
		<?php
		$sidebars = new WP_Query(array(
			'post_type' => 'sidebar'
		));
		?>
		<?php while($sidebars->have_posts()): $sidebars->the_post(); ?>
		<?php
		$selected = '';
		if(intval($_page_sidebar) == get_the_ID())
			$selected = " selected="selected"";
		 ?>
		<option value="<?php echo get_the_ID(); ?>"<?php echo $selected; ?>><?php echo get_the_title(); ?></option>
		<?php endwhile; ?>
		<?php wp_reset_postdata(); ?>
	</select></p>
	<?php
}
?>

Saving the Sidebar dropdown

The following snippet will process and save the passed data from our dropdown.

public function save_metabox($post_id, $post){
	if ( !isset( $_POST[$this->meta_key.'_nonce'] ) || !wp_verify_nonce( $_POST[$this->meta_key.'_nonce'], basename( __FILE__ ) ) )
		return $post_id;
	// Get the post type object. 
 
	$post_type = get_post_type_object( $post->post_type );
 
	// Check if the current user has permission to edit the post. 
	if ( !current_user_can( $post_type->cap->edit_post, $post_id ) )
		return $post_id;
 
	foreach($this->keys as $key){
		$name = isset( $_POST[$this->meta_id][$key] ) ?  $_POST[$this->meta_id][$key] : '' ;
		$value = get_post_meta( $post_id, $key, true );
 
		// If a new meta value was added and there was no previous value, add it. 
		if ( $name && '' == $value )
			add_post_meta( $post_id, $key, $name, true );
 
		// If the new meta value does not match the old value, update it. 
		elseif ( $name && $name != $value )
			update_post_meta( $post_id, $key, $name );
 
		// If there is no new meta value but an old value exists, delete it. 
		elseif ( '' == $name && $value )
			delete_post_meta( $post_id, $key, $value );
	}
 
}

Hooking our functions into wordpress

Now that we have the code written for our custom sidebars all that is left to do is add all our hooks, this is done via our classes constructor function.

public function __construct(){
 
	add_action('init', array( $this, 'register_sidebar' ));
	add_action('init', array( $this, 'load_sidebars' ));
 
	// manage post meta 
	add_action( 'load-post.php', array($this, 'setup_metabox' ));
	add_action( 'load-post-new.php', array($this, 'setup_metabox' ));
	add_action( 'save_post', array($this, 'save_metabox'), 10, 2 );
 
}

Conclusion

Our custom sidebar class is not a complete solution, you can extend it however you like such as adding a custom admin interface, or disabling the sidebar dropdown menu on certain post types, or any other feature you think would help improve this class.



Liked this article? help spread the word.