Writing a custom Wordpress multi-instance widget
Thursday, December 3rd, 2009While working on a soon-to-be finished Wordpress theme for a client, I ran into a situation where it would be useful to create a custom widget that they could use to organize content on their site. Basically, they wanted to be able to select a random post or page and display some associated meta data. Essentially a custom image and content teaser. They were going to be doing this several times throughout the site, but in slightly different configurations. A post here, a page there. It seemed silly to hard-code these features. Using a widget would allow them to swap them out for a Twitter stream, or an RSS news feed in the future.
Making multi-instance widgets in Wordpress 2.8+ couldn’t be easier. Here is a good example to start with from the Wordpress codex.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | <?php class My_Widget extends WP_Widget { function My_Widget() { // widget actual processes } function widget($args, $instance) { // outputs the content of the widget } function update($new_instance, $old_instance) { // processes widget options to be saved } function form($instance) { // outputs the options form on admin } } register_widget('My_Widget'); ?> |
And here is my finished widget (evolved from the above example).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 | <?php /* * Custom mini-post widget */ class FGX_MiniPost_Widget extends WP_Widget { function FGX_MiniPost_Widget() { // widget actual processes parent::WP_Widget(false, $name = 'Floragenex MiniPost', array( 'description' => 'Display a teaser for a post or a page.' )); } function widget($args, $instance) { // outputs the content of the widget global $post; extract( $args ); $type = $instance['type']; $include = (!empty($instance['include']) ? explode(',', $instance['include']) : ''); $category = (is_numeric($instance['category']) ? (int)$instance['category'] : ''); // Set up query for posts with the provided filters query_posts(array( 'post_type' => $type, 'post__in' => $include, 'post__not_in' => array($post->ID), 'cat' => $category, 'post_status' => 'publish', 'meta_key' => 'teaser_value', 'meta_value' => '', 'meta_compare' => '!=', 'orderby' => 'rand', 'posts_per_page' => '1' )); echo $before_widget; // Output widget, if a post exists that matches our query if ( have_posts() ) : while ( have_posts() ) : the_post(); $post_meta = get_post_custom($post->ID); echo (!empty($post_meta['image_value'][0]) ? '<a href="' . get_page_link() . '">' . '<img alt="image" src="' . get_bloginfo('template_url') . '/scripts/timthumb.php?src=' . htmlentities($post_meta['image_value'][0]) . '&h=62&w=180&zc=1" />' . '</a>' : '') . '<h3><a href="' . get_page_link() . '">' . get_the_title() . '</a></h3>' . '<p>' . htmlentities($post_meta['teaser_value'][0]) . '</p>' . '<p><a class="learn-more" href="' . get_page_link() . '">learn more</a></p>'; endwhile; else: echo '<p>No posts found.</p>'; endif; // Very important to reset the query here. wp_reset_query(); echo $after_widget; } function update($new_instance, $old_instance) { // processes widget options to be saved return $new_instance; } function form($instance) { // outputs the options form on admin $type = esc_attr($instance['type']); $include = esc_attr($instance['include']); $category = esc_attr($instance['category']); // Get the existing categories and build a simple select dropdown for the user. $categories = get_categories(); $cat_options = array(); $cat_options[] = '<option value="BLANK">Select one...</option>'; foreach ($categories as $cat) { $selected = $category === $cat->cat_ID ? ' selected="selected"' : ''; $cat_options[] = '<option value="' . $cat->cat_ID .'"' . $selected . '>' . $cat->name . '</option>'; } ?> <p> <label for="<?php echo $this->get_field_id('type'); ?>"> <?php _e('Content type:'); ?> </label> <select id="<?php echo $this->get_field_id('type'); ?>" class="widefat" name="<?php echo $this->get_field_name('type'); ?>"> <option value="post"<?php echo ($type === 'post' ? ' selected="selected"' : ''); ?>>Post</option> <option value="page"<?php echo ($type === 'page' ? ' selected="selected"' : ''); ?>>Page</option> </select> </p> <p> <label for="<?php echo $this->get_field_id('include'); ?>"> <?php _e('Include post IDs (optional):'); ?> </label> <input id="<?php echo $this->get_field_id('include'); ?>" class="widefat" type="text" name="<?php echo $this->get_field_name('include'); ?>" value="<?php echo $include; ?>" /> </p> <p> <label for="<?php echo $this->get_field_id('category'); ?>"> <?php _e('Include category (optional):'); ?> </label> <select id="<?php echo $this->get_field_id('category'); ?>" class="widefat" name="<?php echo $this->get_field_name('category'); ?>"> <?php echo implode('', $cat_options); ?> </select> </p> <?php } } // register widget register_widget('FGX_MiniPost_Widget'); ?> |
Just paste this code in your theme’s functions.php file and the widget should appear under your available widgets.
This was a quick braindump post, so please feel free to post with any specific questions.








