71

I would like to programmatically add widgets to my two sidebars that I've got. I couldn't find any official way of doing it?

I started looking in the database. I've found that it's the 'sidebars_widgets' option which puts widgets on sidebars. When looking at the options the widget names has a number added to the end like: widget_name-6. Where does that number come from?

Any idea on how to fix this?

5

4 Answers 4

102

When I started this answer it should be just a small note. Well, I failed. Sorry! Stay with me, there is a goody hidden deep down …

How WordPress widgets are stored

The list of widget is stored in an option named 'sidebars_widgets'. A var_export() may give something like the following:

array (
  'wp_inactive_widgets' => 
  array (
  ),
  'top-widget' => 
  array (
  ),
  'bottom-widget' => 
  array (
  ),
  'array_version' => 3,
)

Ignore 'wp_inactive_widgets' and 'array_version'. We don't have to care about those.
The other keys are identifier for registered sidebars. In this case the sidebars may have been registered with this code:

// Register two sidebars.
$sidebars = array ( 'a' => 'top-widget', 'b' => 'bottom-widget' );
foreach ( $sidebars as $sidebar )
{
    register_sidebar(
        array (
            'name'          => $sidebar,
            'id'            => $sidebar,
            'before_widget' => '',
            'after_widget'  => ''
        )
    );
}

By default the sidebars are empty after registration. Of course.

For each registered widget class a separate option is created, containing all necessary options. The option is prefixed by the string widget_. To get the options for all active RSS widgets we have to look into …

get_option( 'widget_rss' );

Possible output:

array (
  2 => 
  array (
    'title' => 'WordPress Stack Exchange',
    'url' => 'http://wordpress.stackexchange.com/feeds',
    'link' => 'http://wordpress.stackexchange.com/questions',
    'items' => 5,
    'show_summary' => 1,
    'show_author' => 0,
    'show_date' => 0,
  ),
)

Note the number 2. The arguments for multiple instances are all stored in this one option sorted by numbers.

To see which widget classes are already known to WordPress go to wp-admin/options.php and scroll down until you see something like this:

screen shot of serialized widget options

Yes, serialized data. No, you can't read those here. Don't worry, you don't have to.

A demo widget

To illustrate the inner workings better I have written a very simple demo widget:

/**
 * Super simple widget.
 */
class T5_Demo_Widget extends WP_Widget
{
    public function __construct()
    {                      // id_base        ,  visible name
        parent::__construct( 't5_demo_widget', 'T5 Demo Widget' );
    }

    public function widget( $args, $instance )
    {
        echo $args['before_widget'], wpautop( $instance['text'] ), $args['after_widget'];
    }

    public function form( $instance )
    {
        $text = isset ( $instance['text'] )
            ? esc_textarea( $instance['text'] ) : '';
        printf(
            '<textarea class="widefat" rows="7" cols="20" id="%1$s" name="%2$s">%3$s</textarea>',
            $this->get_field_id( 'text' ),
            $this->get_field_name( 'text' ),
            $text
        );
    }
}

Note the constructor: 't5_demo_widget' is the $id_base, the identifier for this widget. As you can see in the screen shot its arguments are stored in the option widget_t5_demo_widget. All your custom widgets will be treated like this. You don't have to guess the name. And since you have written your widgets (probably) you know all the arguments from your class' $instance parameters.

Theme basics

First you have to register some sidebars and the custom widget. The proper action for this is easy to remember: 'widgets_init'. Put everything into a container – a class or a function. For simplicity I'll use a function named t5_default_widget_demo().

All of the following code goes into the functions.php. The class T5_Demo_Widget should be loaded already. I just put it into the same file …

add_action( 'widgets_init', 't5_default_widget_demo' );

function t5_default_widget_demo()
{
    // Register our own widget.
    register_widget( 'T5_Demo_Widget' );

    // Register two sidebars.
    $sidebars = array ( 'a' => 'top-widget', 'b' => 'bottom-widget' );
    foreach ( $sidebars as $sidebar )
    {
        register_sidebar(
            array (
                'name'          => $sidebar,
                'id'            => $sidebar,
                'before_widget' => '',
                'after_widget'  => ''
            )
        );
    }

So far, so simple. Our theme is now widget ready, the demo widget is known. Now the fun.

$active_widgets = get_option( 'sidebars_widgets' );

if ( ! empty ( $active_widgets[ $sidebars['a'] ] )
    or ! empty ( $active_widgets[ $sidebars['b'] ] )
)
{   // Okay, no fun anymore. There is already some content.
    return;
}

You really don't want to destroy the user settings. If there is already some content in the sidebars your code should not run over it. That's why we stop in this case.

Okay, assumed the sidebars are empty … we need a counter:

$counter = 1;

Widgets are numbered. These numbers are second identifiers for WordPress.

Let's get the array to change it:

$active_widgets = get_option( 'sidebars_widgets' );

We need a counter too (more on that later):

$counter = 1;

And here is how we use the counter, the sidebar names and the the widget arguments (well, we have just one argument: text).

// Add a 'demo' widget to the top sidebar …
$active_widgets[ $sidebars['a'] ][0] = 't5_demo_widget-' . $counter;
// … and write some text into it:
$demo_widget_content[ $counter ] = array ( 'text' => "This works!\n\nAmazing!" );

$counter++;

Note how the widget identifier is created: the id_base, a minus - and the counter. The content of the widget is stored in another variable $demo_widget_content. Here is the counter the key and the widget arguments are stored in an array.

We increment the counter by one when we are done to avoid collisions.

That was easy. Now a RSS widget. More fields, more fun!

$active_widgets[ $sidebars['a'] ][] = 'rss-' . $counter;
// The latest 15 questions from WordPress Stack Exchange.
$rss_content[ $counter ] = array (
    'title'        => 'WordPress Stack Exchange',
    'url'          => 'http://wordpress.stackexchange.com/feeds',
    'link'         => 'http://wordpress.stackexchange.com/questions',
    'items'        => 15,
    'show_summary' => 0,
    'show_author'  => 1,
    'show_date'    => 1,
);
update_option( 'widget_rss', $rss_content );

$counter++;

Here is something new: update_option() this will store the RSS widget argument in a separate option. WordPress will find these automatically later.
We didn't save the demo widget arguments because we add a second instance to our second sidebar now …

// Okay, now to our second sidebar. We make it short.
$active_widgets[ $sidebars['b'] ][] = 't5_demo_widget-' . $counter;
#$demo_widget_content = get_option( 'widget_t5_demo_widget', array() );
$demo_widget_content[ $counter ] = array ( 'text' => 'The second instance of our amazing demo widget.' );
update_option( 'widget_t5_demo_widget', $demo_widget_content );

… and save all arguments for the t5_demo_widget in one rush. No need to update the same option two times.

Well, enough widgets for today, let's save the sidebars_widgets too:

update_option( 'sidebars_widgets', $active_widgets );

Now WordPress will know that there are some registered widgets and where the arguments for each widget are stored. A var_export() on the sidebar_widgets will look like this:

array (
  'wp_inactive_widgets' => 
  array (
  ),
  'top-widget' => 
  array (
    0 => 't5_demo_widget-1',
    1 => 'rss-2',
  ),
  'bottom-widget' => 
  array (
    0 => 't5_demo_widget-3',
  ),
  'array_version' => 3,
)

The complete code again:

add_action( 'widgets_init', 't5_default_widget_demo' );

function t5_default_widget_demo()
{
    // Register our own widget.
    register_widget( 'T5_Demo_Widget' );

    // Register two sidebars.
    $sidebars = array ( 'a' => 'top-widget', 'b' => 'bottom-widget' );
    foreach ( $sidebars as $sidebar )
    {
        register_sidebar(
            array (
                'name'          => $sidebar,
                'id'            => $sidebar,
                'before_widget' => '',
                'after_widget'  => ''
            )
        );
    }

    // Okay, now the funny part.

    // We don't want to undo user changes, so we look for changes first.
    $active_widgets = get_option( 'sidebars_widgets' );

    if ( ! empty ( $active_widgets[ $sidebars['a'] ] )
        or ! empty ( $active_widgets[ $sidebars['b'] ] )
    )
    {   // Okay, no fun anymore. There is already some content.
        return;
    }

    // The sidebars are empty, let's put something into them.
    // How about a RSS widget and two instances of our demo widget?

    // Note that widgets are numbered. We need a counter:
    $counter = 1;

    // Add a 'demo' widget to the top sidebar …
    $active_widgets[ $sidebars['a'] ][0] = 't5_demo_widget-' . $counter;
    // … and write some text into it:
    $demo_widget_content[ $counter ] = array ( 'text' => "This works!\n\nAmazing!" );
    #update_option( 'widget_t5_demo_widget', $demo_widget_content );

    $counter++;

    // That was easy. Now a RSS widget. More fields, more fun!
    $active_widgets[ $sidebars['a'] ][] = 'rss-' . $counter;
    // The latest 15 questions from WordPress Stack Exchange.
    $rss_content[ $counter ] = array (
        'title'        => 'WordPress Stack Exchange',
        'url'          => 'http://wordpress.stackexchange.com/feeds',
        'link'         => 'http://wordpress.stackexchange.com/questions',
        'items'        => 15,
        'show_summary' => 0,
        'show_author'  => 1,
        'show_date'    => 1,
    );
    update_option( 'widget_rss', $rss_content );

    $counter++;

    // Okay, now to our second sidebar. We make it short.
    $active_widgets[ $sidebars['b'] ][] = 't5_demo_widget-' . $counter;
    #$demo_widget_content = get_option( 'widget_t5_demo_widget', array() );
    $demo_widget_content[ $counter ] = array ( 'text' => 'The second instance of our amazing demo widget.' );
    update_option( 'widget_t5_demo_widget', $demo_widget_content );

    // Now save the $active_widgets array.
    update_option( 'sidebars_widgets', $active_widgets );
}

If you go to wp-admin/widgets.php now you will see three pre-set widgets:

screen shot of active widgets

And that's it. Use …

dynamic_sidebar( 'top-widget' );
dynamic_sidebar( 'bottom-widget' );

… to print the widgets out.

There is a small glitch: You have to load the front-end two times for the initial registration. If anyone can help out here I'll be very grateful.

5
  • This is really interesting .. but wouldn´t this code add a "new" widget in every page load `? also, another interesting issue is how can one control those widgets, including their content from within a plugin as opposed to theme ( loads earlier ? )
    – krembo99
    Commented Aug 3, 2013 at 8:36
  • 1
    @krembo99 The widgets are not added when the sidebars are not empty. The code works in a plugin exactly the same way.
    – fuxia
    Commented Aug 3, 2013 at 11:46
  • What does widget_t5_demo_widget refer to here: update_option( 'widget_t5_demo_widget', $demo_widget_content );?
    – Snowcrash
    Commented Oct 16, 2015 at 15:29
  • @SnowCrash That's just an option name, no reference to anything else.
    – fuxia
    Commented Oct 16, 2015 at 15:37
  • I found this answer informative to understand how the data is stored/used. Then I like this other answer for practical code example: wordpress.stackexchange.com/a/138248/27896 Commented Sep 28, 2020 at 21:13
3

Thanks for sharing your solution. I have used what has been described in this question to create a piece of code that can be used to initialize sidebars very easily. It is very flexible, you can create as many widgets as you want without having to modify the code at all. Just make use of the filter hooks and pass in arguments in an array. Here is the commented code:

function initialize_sidebars(){

  $sidebars = array();
  // Supply the sidebars you want to initialize in a filter
  $sidebars = apply_filters( 'alter_initialization_sidebars', $sidebars );

  $active_widgets = get_option('sidebars_widgets');

  $args = array(
    'sidebars' => $sidebars,
    'active_widgets' => $active_widgets,
    'update_widget_content' => array(),
  );

  foreach ( $sidebars as $current_sidebar_short_name => $current_sidebar_id ) {

    $args['current_sidebar_short_name'] = $current_sidebar_short_name;
    // we are passing our arguments as a reference, so we can modify their contents
    do_action( 'your_plugin_sidebar_init', array( &$args ) );

  }
  // we only need to update sidebars, if the sidebars are not initialized yet
  // and we also have data to initialize the sidebars with
  if ( ! empty( $args['update_widget_content'] ) ) {

    foreach ( $args['update_widget_content'] as $widget => $widget_occurence ) {

      // the update_widget_content array stores all widget instances of each widget
      update_option( 'widget_' . $widget, $args['update_widget_content'][ $widget ] );

    }
    // after we have updated all the widgets, we update the active_widgets array
    update_option( 'sidebars_widgets', $args['active_widgets'] );

  }

}

This is a helper function which checks if the sidebar already has content in it:

function check_sidebar_content( $active_widgets, $sidebars, $sidebar_name ) {

  $sidebar_contents = $active_widgets[ $sidebars[ $sidebar_name ] ];

  if ( ! empty( $sidebar_contents ) ) {

    return $sidebar_contents;

  }

  return false;

}

Now we need to create a function that is hooked to the 'sidebar_init' action.

add_action( 'your_plugin_sidebar_init', 'add_widgets_to_sidebar' );

function add_widgets_to_sidebar( $args ) {

  extract( $args[0] );

  // We check if the current sidebar already has content and if it does we exit
  $sidebar_element = check_sidebar_content( $active_widgets, $sidebars, $current_sidebar_short_name );

  if ( $sidebar_element !== false  ) {

    return;

  }

  do_action( 'your_plugin_widget_init', array( &$args ) );

}

And now the widget initialization:

add_action( 'your_plugin_widget_init', 'your_plugin_initialize_widgets' );

function your_plugin_initialize_widgets( $args ) {

  extract( $args[0][0] );

  $widgets = array();

  // Here the widgets previously defined in filter functions are initialized,
  // but only those corresponding to the current sidebar 
  $widgets = apply_filters( 'alter_initialization_widgets_' . $current_sidebar_short_name, $widgets );

  if ( ! empty( $widgets ) ) {

    do_action( 'create_widgets_for_sidebar', array( &$args ), $widgets );

  }

}

The last action is to create the widgets in each sidebar:

add_action( 'create_widgets_for_sidebar', 'your_plugin_create_widgets', 10, 2 );

function your_plugin_create_widgets( $args, $widgets ) {

  extract( $args[0][0][0] );

  foreach ( $widgets as $widget => $widget_content ) {

    // The counter is increased on a widget basis. For instance, if you had three widgets,
    // two of them being the archives widget and one of the being a custom widget, then the
    // correct counter appended to each one of them would be archive-1, archive-2 and custom-1.
    // So the widget counter is not a global counter but one which counts the instances (the
    // widget_occurrence as I have called it) of each widget.
    $counter = count_widget_occurence( $widget, $args[0][0][0]['update_widget_content'] );

    // We add each instance to the active widgets...
    $args[0][0][0]['active_widgets'][ $sidebars[ $current_sidebar_short_name ] ][] = $widget . '-' . $counter;

    // ...and also save the content in another associative array.
    $args[0][0][0]['update_widget_content'][ $widget ][ $counter ] = $widget_content;

  }

}

This function is used to keep track of how many instances of a specific widget have already been defined:

function count_widget_occurence( $widget, $update_widget_content ) {

  $widget_occurrence = 0;

  // We look at the update_widget_content array which stores each
  // instance of the current widget with the current counter in an 
  // associative array. The key of this array is the name of the 
  // current widget.
      // Having three archives widgets for instance would look like this:
      // 'update_widget_content'['archives'] => [1][2][3] 
  if ( array_key_exists( $widget, $update_widget_content ) ) {

    $widget_counters = array_keys( $update_widget_content[ $widget ] );

    $widget_occurrence = end( $widget_counters );

  }

  $widget_occurrence++;

  return $widget_occurrence;

}

The last thing we need to do is to actually assign values. Make use of these filter functions:

add_filter( 'alter_initialization_sidebars', 'current_initialization_sidebars' ) ;
// Use this filter hook to specify which sidebars you want to initialize
function current_initialization_sidebars( $sidebars ) {

  // The sidebars are assigned in this manner.
  // The array key is very important because it is used as a suffix in the initialization function
  // for each sidebar. The value is what is used in the html attributes.
  $sidebars['info'] = 'info-sidebar';

  return $sidebars;

}

And:

add_filter( 'alter_initialization_widgets_info', 'current_info_widgets' );
// Add a filter hook for each sidebar you have. The hook name is derived from
// the array keys passed in the alter_initialization_sidebars filter. 
// Each filter has a name of 'alter_initialization_widgets_' and the array 
// key appended to it.

function current_info_widgets( $widgets ) {
  // This filter function is used to add widgets to the info sidebar. Add each widget
  // you want to assign to this sidebar to an array.

  return $widgets = array(
    // Use the name of the widget as specified in the call to the WP_Widget constructor
    // as the array key.

    // The archives widget is a widget which is shipped with wordpress by default.
    // The arguments used by this widget, as all other default widgets, can be found
    // in wp-includes/default-widgets.php. 

    'archives' => array(
      // Pass in the array options as an array
      'title' => 'Old Content',
      'dropdown' => 'on',
      // The 'on' value is arbitrarily chosen, the widget actually only checks for
      // a non-empty value on both of these options
      'count' => 'on',
    ),
 );

}

Ideally you would call initialize_sidebars in a setup function which is called upon plugin or theme activation like this: Theme activation:

add_action( 'after_switch_theme', 'my_activation_function' );
function my_activation_function() {
  initialize_sidebars();
}

Plugin activation:

register_activation_hook( __FILE__, 'my_activation_function' );
function my_activation_function() {
  initialize_sidebars();
}

To summarize the usage of this conglomerate of functions:

  1. create a function that initializes the sidebars which is hooked to the 'alter_initialization_sidebars' filter.

  2. create a function for each sidebar you just added which is hooked to the 'alter_initialization_widgets_$sidebarname' filter. Replace $sidebarname with the name of each sidebar you created in step 1.

You can also simply copy this uncommented code into your functions file and start creating your filter functions right away: Code on pastie (without initialization filter functions)

2

First of all, thanks to @toscho for the detailed answer.

This is a simple example for those who are searching for a simple solution and default widget options:

$active_sidebars = get_option( 'sidebars_widgets' ); //get all sidebars and widgets
$widget_options = get_option( 'widget_name-1' );
$widget_options[1] = array( 'option1' => 'value', 'option2' => 'value2' );

if(isset($active_sidebars['sidebar-id']) && empty($active_sidebars['sidebar-id'])) { //check if sidebar exists and it is empty

    $active_sidebars['sidebar-id'] = array('widget_name-1'); //add a widget to sidebar
    update_option('widget_name-1', $widget_options); //update widget default options
    update_option('sidebars_widgets', $active_sidebars); //update sidebars
}

Note 1: You can get sidebar-id going to widgets menu and inspecting the wanted sidebar. The first <div id="widgets-holder-wrap">'s <div> child has the sidebar-id.

Note 2: You can get the widget_name going to widgets menu and inspecting the wanted widget. You'll see something like <div id="widget-6_widget_name-__i__" class="widget ui-draggable">.

I wish it helps.

0

This is how you do it:

(WARNING, this could REMOVE all previous widgets if you did not put back the original widgets into the widgets array.)

    $widgets = array(
    'middle-sidebar' => array(
        'widget_name'
    ),
    'right-sidebar' => array(
        'widget2_name-1'
    )
);
update_option('sidebars_widgets', $widgets);

The -number can be used if you later want to add options to the widget with something like this:

    update_option('widget_widget_name', array(
    1 => array(
        'title' => 'The tile',
        'number' => 4
    ),
    '_multiwidget' => 1
));
2
  • 1
    DON'T FOLLOW THIS, I CAN'T RATE THIS DOWN. ALL MY WIDGETS DISAPPEARED AFTER USING THIS CODE.
    – EresDev
    Commented Jul 11, 2014 at 23:34
  • You need to get the existing widgets array first otherwise you'll erase them all like the above comment stated. $widgets = get_option( 'sidebars_widgets' );
    – cowgill
    Commented Nov 11, 2016 at 10:03

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.