WordPress Tutorial : A unique dynamic sidebar for each page - or not!

The last few websites I've built have had sidebars that the client *may* or *may not* need to customize on every page. Some pages don't require a unique sidebar, some do. The client should have the option of a cascading sidebar.

Let's say you have a Blog, an "About" section, and your homepage. In the "About" section, you have the obligatory "About" and "Management Team" pages, and under that you'd like a bio for contacting each of your team members. You would like one sidebar throughout that section with your social widgets and Twitter feeds, but on the "Management Team" page you'd like a contact form instead. On the bio pages, you'd like to include direct contact information for certain members and their personal social links, instead of the generic contact form. The members who do not wish to provide contact information would like to default to the generic contact form.

Your page hierarchy might look something like this:

To create a dynamic sidebar for each of these pages with content that cascades 3 levels deep into the page hierarchy, we will simply loop through the pages in a query and register a sidebar for each page or content type that we pull. This is a simple example, set up for pages that have been published (not drafts) but it can be customized for custom post types or any other content that you can loop through. Remember that there will be a sidebar box added to your Widget Admin for every content that you pull; if you have 200 unique pages this might not be the right approach.

Add this to your functions.php file:

  if (function_exists('register_sidebar')){
    register_sidebar(array('name'=>'Single Post Sidebar',
      'id' => 'sidebar-single',
      'before_widget' => '<div id="%1$s" class="widget-area %2$s">',
      'after_widget' => '</div>',
      'before_title' => '<h3>',
      'after_title' => '</h3>'
    ));
 
    // Dynamic Widgets
    $myPages = $wpdb->get_results("SELECT * FROM $wpdb->posts WHERE post_type = 'page' AND post_status = 'publish'"); 
 
    foreach ($myPages as $q ){
      $id = 'sidebar-'.$q->ID;
      $namePref = "Parent - ";
      if ($q->post_parent != 0){ 
        $namePref = "Child - ";
      } 
      $sbtitle = $namePref . $q->post_title;
      if ($sbtitle && function_exists('register_sidebar')){
        register_sidebar(array(
          'id' => $id, 
          'name'          => $sbtitle ,
          'before_widget' => '<div id="%1$s" class="widget-area %2$s">', 
          'after_widget' => '</div>', 
          'before_title' => '<h3>', 
          'after_title' => '</h3>'
        ));
      }
    }
  }

Ta-da, all your sidebars should be appearing in the Admin > Appearance > Widgets view, with titles that make it obvious which sidebars are for Parent pages, and which are for Child pages and can safely be ignored, as they inherit content.

To make them appear on the website we need to do some work to sidebar.php. This code has conditionals for the blog single, the main blog page and the page view.

  1. If it's a single post it sets the $sid var for the static sidebar we configured.
  2. If it's a page it checks for a sidebar for that page specifically, then for the parent page, and then for the parent or the parent's parent page if the previous are not found.
  3. If it's the blog page it sets the $sid var for the blog sidebar id. Since the $post array is full of the blog posts and no longer referencing the Blog page, we will get the ID for this page by calling get_option('page_for_posts').

Then it displays the sidebar using the $sid var.

<?php 
  $sid = "";
  $pref = "sidebar-";
 
  if (is_single()){
    // sidebar explicitly defined for single blog post
    $sid = "sidebar-single"; 
  } elseif (is_page()){
    if ($post->post_parent == 0 || is_active_sidebar('sidebar-'.$post->ID)){
      // no parent to inherit, or not necessary because the sidebar has been set
      $sid = $pref.$post->ID;
    } else {
      if (is_active_sidebar('sidebar-'.$post->post_parent)){
        // parent exists and has a sidebar
        $sid = $pref.$post->post_parent;
      } else {
        // parent doesn't exist, check all for sidebars
        foreach (get_post_ancestors($post) as $ancestor => $id){
          if (is_active_sidebar($pref.$id)){
            $sid = $pref.$id;
            break;
          }
        }
      }
    }
  } elseif (is_home()){
     // sidebar for blog list page
    $sid = $pref.get_option('page_for_posts');
  }
?>
 
  <?php if (!empty($sid) && function_exists('is_dynamic_sidebar') && is_active_sidebar($sid)){ ?>
    <div id="sidebar">  
      <?php if (!function_exists('dynamic_sidebar') || !dynamic_sidebar($sid)): ?><?php endif; ?>
    </div>
  <?php } ?>

Calling get_sidebar() from your template files will display whichever sidebar content is available, 3 levels deep. New sidebars are created whenever new pages are created and inherit their parent page's content unless they are otherwise overridden. You might also define a generic sidebar to use in case the topmost page's sidebar hasn't been customized, for completely foolproof publishing.

Ready to get started?