Advanced WordPress Widgets

Posted by & filed under Advanced, Developing for WordPress, Widgets.

Custom widgets are fantastic additions to all WordPress themes and plugins, and they are relatively easy to create, once you have the no how. This tutorial will walk you through the steps needed to create just about any kind of widget. I will show you how to create different option fields, how to save widget options, how to use options to control the output of your widget, and, at the end, I will give you the complete code to an advanced Blog Authors Widget.

The Widget Class

There are several ways to set up a widget, but by far the best method is to use a CLASS structure. By creating a PHP class for our widget, it makes it very easy to create as many widgets as you wish, without having to worry about conflicting function names. You can simply duplicate the code from your first widget, rename the class, and you’re done! So we are going to use a class for our example widget, and it starts like this:

/**
 * Example Widget Class
 */
	class pippin_example_widget extends WP_Widget {
		// all of our widget code will go here
	}

You can name the class anything you want, but I would advise you to be meaningful in your naming convention. “My Awesome Widget” really doesn’t tell you anything, even though it probably is awesome. I always go for names that explain the widget’s function, such as “blog_authors_widget”. It is also a good practice to always prefix your class names, just as you should prefix your function names. This is to help avoid the possibility of conflicts with other developer’s functions or class names. So our example widget class name is “pippin_example_widget”.

The Widget Functions

Now that we have our widget class, we will begin filling it with the various functions we need to construct our widget. There will be four functions total:

  • pippin_example_widget()
  • widget()
  • update()
  • form()

Notice that I have not prefixed these function names. This is because each of these functions lives within our widget class and thus do not need unique names or prefixes.

The Constructor Function

The first function we will write is pippin_example_widget(), and yes, it should be named the same as your widget class. This is a very simple, short function that constructs our widget and gives it a name.

/** constructor */
    function pippin_example_widget() {
        parent::WP_Widget(false, $name = 'Example Widget');
    }

The only thing you should ever need to change with this function is the $name variable. This variable determines the name that appears on the widget in the admin widgets panel.

The Widget Output

Next comes the function that controls the widget’s output to the front end of the site. This function can contain just about anything you want, including HTML, CSS, jQuery, PHP, and more. Because this function does not have many set constraints on what you can do with it, I’m just going to show you examples of how you can use it.

First of all, our base function is going to look something like this:

/** @see WP_Widget::widget */
    function widget($args, $instance) {
        extract( $args );

	// these are our widget options
    $title = apply_filters('widget_title', $instance['title']);
	$text = $instance['text'];
	$checkbox = $instance['checkbox'];
	$textarea = $instance['textarea'];
	$select = $instance['select'];

    echo $before_widget;

	if ( $title ) {
	echo $before_title . $title . $after_title;
	}
         echo $after_widget;
    }

There are several important things going on here. First of all, the two parameters passed to the function are used to retrieve the saved options that we have set on the widget panel (don’t worry, we haven’t actually gotten there yet). These two parameters should never change, and nor should the extract($args) function call just after the opening }.

The next couple lines are the widget options. The options are all stored in the $instance parameter, which is an array, so I have set up a unique variable name for each option.

  • $title – This is the main widget title
  • $text – This is our sample text input field
  • $checkbox – This is our sample checkbox field
  • $textarea – This is our sample textarea field
  • $select – This is our sample select menu

Note that these options don’t actually work just yet, but we will set them up in a moment. For now just prepend that we’ve already taken care of them :)

The widget() function above as it is will do nothing more than display the widget title, so we are going to want to make it a little more advanced. I said earlier that you can do just about anything with this function that you want. That’s because it is the function that controls the output of our widget on the front end of the site. We are going to use our option (defined a moment ago) to control the exact output of the widget

/** @see WP_Widget::widget */
	function widget($args, $instance) {
	    extract( $args );

	// these are our widget options
	    $title = apply_filters('widget_title', $instance['title']);
	$text = $instance['text'];
	$checkbox = $instance['checkbox'];
	$textarea = $instance['textarea'];
	$select = $instance['select'];

    echo $before_widget;

	// if the title is set
	if ( $title ) {
	echo $before_title . $title . $after_title;
	}

	// if the text field is set
	if ( $text ) {
	echo '
<div class="widget-text">' . $text . '</div>
';
	}

	// if the checkbox is checked
	if ( $checkbox == true ) {
	echo '

This message is displayed if our checkbox is checked.

';
	}

	// if text is entered in the textarea
	if ( $textarea ) {
	echo '
<div class="widget-textarea">' . $textarea . '</div>
';
	}

	// output text depended on which option is picked
	if ( $select == 'one' ) {
	echo '

Option One is Selected

';
	} else if ( $select == 'two' ) {
	echo '

Option Two is Selected

';
	} else {
	echo '

Option Three is Selected

';
	}
         echo $after_widget;
	}

What I have done is use the option values to control what is displayed. For example, I checked to see if the CHECKBOX field was checked (or had a value of TRUE), and if it was, display a message. I also displayed a different message for each SELECT menu option. These are all really, really simple examples but they convey a very important point: by utilizing your widget options (and a little bit of syntax) you can display anything you want inside of a widget.

While I have left the output to nothing more than simple text messages, you can just as easily use the options to control a post query, just as I did with my Better Recent Posts Widget.

Saving the Widget Options

The next function in our widget class is the update() function. This one does nothing more than save the options chosen from the widgets panel. It’s a very simple function.

/** @see WP_Widget::update */
    function update($new_instance, $old_instance) {
	$instance = $old_instance;
	$instance['title'] = strip_tags($new_instance['title']);
	$instance['text'] = strip_tags($new_instance['text']);
	$instance['checkbox'] = strip_tags($new_instance['checkbox']);
	$instance['textarea'] = strip_tags($new_instance['textarea']);
	$instance['select'] = strip_tags($new_instance['select']);
    return $instance;
    }

Remember how I told you that the widget options were stored in an array variable called $instance? Well, this function takes two parameters, $old_instance which contains all the option values already saved, and $new_instance which contains all of the options that we have just updated. the function does nothing more than take the old instance and set each value in the array equal to the appropriate value in the new instance, thus saving our options

It is important to note that each of the instance variables matches those of the widget() function above. If your variable names do not match, your options will not save correctly.

There is only one more function to write, and that is the one that displays the widget options form in the widgets panel in the WordPress admin.

The Widget Options Form

Once again, there is nothing really special about this function, it simply outputs an HTML form with a field for each of our options. Let’s see the function, then I’ll explain parts of it.

 /** @see WP_Widget::form */
function form($instance) {	

    $title = esc_attr($instance['title']);
	$text = esc_attr($instance['text']);
	$checkbox = esc_attr($instance['checkbox']);
	$textarea = esc_attr($instance['textarea']);
	$select = esc_attr($instance['select']);

    ?>

	 <p>
      	<label for="<?php echo $this->get_field_id('title'); ?>"><?php _e('Widget Title'); ?></label>
      	<input class="widefat" id="<?php echo $this->get_field_id('title'); ?>" name="<?php echo $this->get_field_name('title'); ?>" type="text" value="<?php echo $title; ?>" />
    </p>

     <p>
      	<label for="<?php echo $this->get_field_id('text_field'); ?>"><?php _e('This is a single line text field:'); ?></label>
      	<input class="widefat" id="<?php echo $this->get_field_id('text_field'); ?>" name="<?php echo $this->get_field_name('text_field'); ?>" type="text" value="<?php echo $text_field; ?>" />
    </p>

	<p><
      	<input id="<?php echo $this->get_field_id('checkbox'); ?>" name="<?php echo $this->get_field_name('checkbox'); ?>" type="checkbox" value="1" <?php checked( '1', $checkbox ); ?>/>
    	<label for="<?php echo $this->get_field_id('checkbox'); ?>"><?php _e('This is a checkbox'); ?></label>
    </p>

	<p>
    	<label for="<?php echo $this->get_field_id('textarea'); ?>"><?php _e('This is a textarea:'); ?></label>
    	<textarea class="widefat" id="<?php echo $this->get_field_id('textarea'); ?>" name="<?php echo $this->get_field_name('textarea'); ?>"><?php echo $textarea; ?></textarea>
    </p>

	<p>
		<label for="<?php echo $this->get_field_id('select'); ?>"><?php _e('This is a select menu'); ?></label>
		<select name="<?php echo $this->get_field_name('select'); ?>" id="<?php echo $this->get_field_id('select'); ?>" class="widefat">
			<?php
			$options = array('one', 'two', 'three');
			foreach ($options as $option) {
				echo '<option value="' . $option . '" id="' . $option . '"', $select == $option ? ' selected="selected"' : '', '>', $option, '</option>';
			}
			?>
		</select>
	</p>

    <?php
}

This function takes one $instance parameter, which allows our HTML form to read the saved options. The HTML form in this function is very straight forward, so if you have questions about it, please ask in the comments. I would like to explain how we connect each FORM field with its corresponding option, and how we ensure that the value entered (or selected) in each field is saved correctly.

There are several key functions used inside of this form() function:

  • $title – This is the main widget title
  • $text – This is our sample text input field
  • $checkbox – This is our sample checkbox field
  • $textarea – This is our sample textarea field
  • $select – This is our sample select menu

The first and second of these functions must match the option names at the top of this form() function, and also those of the option names used in the other functions. So, for example, for the TITLE option, we use $this->get_field_id(‘title’) and $this->get_field_name(‘title’). If these values do not match those of the other functions, your options will not save.

The only other part to the options form that might be a little confusing is the way that the SELECT field is set up. After we’ve done the normal field name and ID stuff, as described a moment ago, we use a FOREACH loop to display the options available in this SELECT menu. We use the foreach loop because we need to have a way of making the saved option as the selected option when we load the page, and because it is much more efficient than simply writing out each option. In order to mark the correct option as selected when the page loads, we use the conditional that looks like this:

foreach ($options as $option) {
	echo '&lt;option value="' . $option . '" id="' . $option . '"', $select == $option ? ' selected="selected"' : '', '&lt;', $option, '&lt;/option&lt;';
}

This essentially says: display the option value and id, and if it matches the value of the option saved in the database (remember our $instance variable), include the selected=”selected” attribute.

And that’s it for our widget options form. This also marks the end of our widget class, which means that there is only one more step before our widget is available to use in WordPress.

Activating the Widget

Our widget is built, it’s options can be saved, and all we need to do is turn it on. To do that, we use the add_action() function to hook our widget into WordPress.

// register example widget
	add_action('widgets_init', create_function('', 'return register_widget("pippin_example_widget");'));

This hook will register our widget and make it available for use. Note that the parameter passed to the register_widget() function inside of the hook is the same as our widget class name. If you use a different value than the name of your widget class, the widget will not work.

That’s everything! Keep reading below to see the complete code for an actually useful widget that you can use to display your blog authors.

Simple Blog Authors Widget

To help provide “real world” sense to this tutorial, I’m providing the code for a complete widget that will provide a simple widget that can be used to list your blog authors. The widget includes options for:

  • Widget Title
  • Author Gravatars
  • Author Post Counts

I’m not going to explain the code, as hopefully I’ve done that well enough above. If you want, simply copy the code below into your functions.php, or go to Pippin’s Plugins and download the complete widget as a plugin for free.

The widget code:

/**
 * Authors Widget Class
 */
class pippin_simple_authors_widget extends WP_Widget {

    /** constructor */
    function pippin_simple_authors_widget() {
        parent::WP_Widget(false, $name = 'Simple Authors Widget');
    }

    /** @see WP_Widget::widget */
    function widget($args, $instance) {
        extract( $args );
		global $wpdb;

        $title = apply_filters('widget_title', $instance['title']);
		$gravatar = $instance['gravatar'];
		$count = $instance['count'];

		if(!$size)
			$size = 40;

        ?>
              <?php echo $before_widget; ?>
                  <?php if ( $title )
                        echo $before_title . $title . $after_title; ?>
							<ul>
							<?php

								$authors = $wpdb->get_results("SELECT ID FROM $wpdb->users ORDER BY ID");

								foreach($authors as $author) {

									$author_info = get_userdata($author->ID);

									echo '<li>';

										echo '<div style="float: left; margin-left: 5px;">';

										echo get_avatar($author->ID, 40);

										echo '</div>';

										echo '<a href="' . get_author_posts_url($author->ID) .'" title="View author archive">';
											echo $author_info->display_name;
											if($count) {
												echo '(' . count_user_posts($author->ID) . ')';
											}
										echo '</a>';

									echo '</li>';
								}
							?>
							</ul>
              <?php echo $after_widget; ?>
        <?php
    }

    /** @see WP_Widget::update */
    function update($new_instance, $old_instance) {
		$instance = $old_instance;
		$instance['title'] = strip_tags($new_instance['title']);
		$instance['gravatar'] = strip_tags($new_instance['gravatar']);
		$instance['count'] = strip_tags($new_instance['count']);
        return $instance;
    }

    /** @see WP_Widget::form */
    function form($instance) {	

        $title = esc_attr($instance['title']);
		$gravatar = esc_attr($instance['gravatar']);
		$count = esc_attr($instance['count']);

        ?>
         <p>
          <label for="<?php echo $this->get_field_id('title'); ?>"><?php _e('Title:'); ?></label>
          <input class="widefat" id="<?php echo $this->get_field_id('title'); ?>" name="<?php echo $this->get_field_name('title'); ?>" type="text" value="<?php echo $title; ?>" />
        </p>

		<p>
          <input id="<?php echo $this->get_field_id('count'); ?>" name="<?php echo $this->get_field_name('count'); ?>" type="checkbox" value="1" <?php checked( '1', $count ); ?>/>
          <label for="<?php echo $this->get_field_id('count'); ?>"><?php _e('Display Post Count?'); ?></label>
        </p>

		<p>
          <input id="<?php echo $this->get_field_id('gravatar'); ?>" name="<?php echo $this->get_field_name('gravatar'); ?>" type="checkbox" value="1" <?php checked( '1', $gravatar ); ?>/>
          <label for="<?php echo $this->get_field_id('gravatar'); ?>"><?php _e('Display Author Gravatar?'); ?></label>
        </p>

        <?php
    }

} // class utopian_recent_posts
add_action('widgets_init', create_function('', 'return register_widget("pippin_simple_authors_widget");'));

Once again, you can download the complete Simple Authors Widget plugin from Pippin’s Plugins.com. Enjoy!

[author id="pippin"]

Tags:

pippin

Pippin Williamson is a WordPress developer based in Lawrence, KS. He loves writing WordPress plugins and runs a WordPress plugin-dedicated site at Pippin's Plugins.com

63 Responses

    • pippin July 21, 2011 at 4:05 am

      @Brando, thanks for the feedback. I’m not completely up to par with my PHP 5 constructors. Would you care to give an example for everyone?

  1. Andrew July 27, 2011 at 8:07 pm

    You started out really well, but I got lost when you changed you’re variables on me. I think you started borrowing from you plugin code instead of remaining consistent with the example started at the beginning. It took me a while to caught back up with what you were saying.

    Also, I think you’re got the wrong function in the code snippet for the form section. You’re stating the widget function again.

    In all I’m appreciative of your efforts. Thank you.

    Reply
    • pippin July 27, 2011 at 8:22 pm

      Ah, you’re right. I did get the wrong function there! It’s updated now and should make much more sense. Sorry about that. If you can point out where I changed variable names, I will update those as well.

    • Andrew July 27, 2011 at 8:33 pm

      the variables were fixed when you added the right code in there.

      You’re also missing a conditional statement code snippet after this:

      “FOREACH loop to display the options available in this SELECT menu. We use the foreach loop because we need to have a way of making the saved option as the selected option when we load the page, and because it is much more efficient than simply writing out each option. In order to mark the correct option as selected when the page loads, we use the conditional that looks like this:”

      Another question, The code snippet for the authors widget doesn’t have the update and the form functions. What’s up with that?

    • pippin July 27, 2011 at 9:08 pm

      Arg! The formatting got screwed up when the post was copied into WordPress. All of these issues should be fixed now. Thank you so much for spotting them.

  2. manos August 9, 2011 at 3:02 pm

    hello pippin!
    thanks for the usefull tutorial.
    as a wordpress beginner i want to ask:

    where do we save the widget class code?
    and where do we place the register widget code?

    thanks again!

    Reply
    • pippin November 3, 2011 at 1:55 pm

      Yes, that is definitely possible. There are quite a few tutorials out there describing how to use upload forms in WordPress.

  3. Anton January 27, 2012 at 6:00 pm

    I’m agree with other other users, great tutorial!
    I red few tutorials about widgets, but there were problems with displaying checkboxes, here is all working great)))
    By the way, how to set widget’s width and hight?
    Thanks))

    Reply
  4. Anthony December 1, 2012 at 8:20 pm

    Nice Tutorial.

    I am having an issue with the Select Menu. I have set it up like you, however when I select an option and click save, it changes the options on the front end (“you selected one”, “you selected two”..etc) however, on the backend after I click save the first option is displayed in the dropbdown and not the one that was saved.

    Reply
  5. Skyler young January 28, 2013 at 3:02 pm

    Thanks for this, it’s the best widget tutorial I’ve found so far. I’m still working through it, but notice that in “The Widget Options Form” section, code example lines 18-20, “text-field” should be simply “text” to match the $instance array already specified, correct?

    Reply
  6. Nath February 25, 2013 at 6:50 am

    You don’t seem to specify where all of this code is supposed to go?
    I’ve followed your tutorial and have been left with the code, but I have no idea what to do with it from here.
    I’ve tried putting it into functions.php, but that just breaks everything and returns a ‘server error’
    Can you perhaps be a bit more specific as to what one is meant to do with the code once written?
    Thanks for your tutorial, hopefully it’ll be useful for ‘custom widget beginners’ sometime in the future. :D

    Reply
    • David Arndt July 16, 2013 at 7:43 am

      You can put the php file in the plugins folder. Be sure to add a header which at least contains the plugin name
      e.g.
      /*
      Plugin Name: Better Recent Posts Widget
      */

  7. pincess-prom-dress October 5, 2013 at 7:20 am

    i study and use wordpress just recently , and i managed to build an great website and made it ready and for running . ..i always keep an eye on wordpress related study file , and thanks for sharing that great wordpress plugin , to be one of these wordpress user can be fun ,let’s learn together.

    Reply
  8. limon October 25, 2013 at 2:53 pm

    Hello pippin :) firstly thanks for great tuts !!
    But i little confused that you used text_field in form section but you store this input as a text name in $instance and all others plcase. More importanly when i used your exact same code then without $text – This is our sample text input field all are update correctly and also display properly :) but when I change get_field_id(‘text_field’); ?> all others for this input section and it work without any issue :)

    Can you please explain about this thing ! why you use text_field and if it’s ok then why it not working for my :)

    Basically I’m very beginner of this area and need to learn from you and others as well so waiting for your response and sorry for the bad english :(

    Reply
  9. this relevant Internet site November 27, 2013 at 8:48 pm

    There are different things that you should look for in a wall.
    You may be able to treat frozen pipes by using your hairdryer.
    Commercial plumbing offers the roof drainage system that involve the collection or disposal of the stromwater which connects the ground level and piping to below-ground stromwater drain.

    Reply
  10. GregyPooh December 26, 2013 at 1:23 am

    Hey Pippin,
    Great job at filling in the holes that the WP API left out.
    Big Ups!
    I’ll be sure to check back here for more.

    Reply

Leave a Reply

XHTML: You can use these tags: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>