Create a posts by category widget in WordPress 2.8

bycat_widget

With WordPress 2.8, Automatic introduced a new widget management panel but also a new way of making your own widgets. Before is was a bunch of messy functions, now it’s a nice widget API easier than ever.

Justin Tadlock wrote a very comprehensive article about  making widget in 2.8 and I will use this to make a widget that displays only posts from a given category name. In CMS like build I often need a way to display the recent post from the new/blog category and I always end up pasting a custom WP_Query. And I am not happy about that. I’d rather use a widget that will do the job for me from the admin.

(If you just want the code and don’t care about the explanations the file is downloadable here. Just include it in your function.php file and you’re done.)

Let’s get started

First we have to create the class that will old all the widget methods. This class should extend the WP_Widget class to be able to use all the built in functions that will make your life easier.

class Posts_By_Cat extends WP_Widget {

Then we need to set up a constructor for this class, in order to create the widget object in php. A constructor is automatically called when WordPress call the widget.

function Posts_By_Cat() {
	/* Widget settings. */
	$widget_ops = array( 'classname' => 'posts_by_cat', 'description' => 'Display posts from only one defined category' );

	/* Create the widget. */
	$this->WP_Widget( 'posts_by_cat-widget', 'Posts by Category', $widget_ops);
}

Now think about what a widget needs to do: display stuff on the front end, display editing option in the back end, save values passed from the back end. And we will have exactly that, one function for each.

Display on the front end

function widget( $args, $instance ) {
	extract( $args );

	/* User-selected settings. */
	$title = apply_filters('widget_title', $instance['title'] );
	$catname = $instance['catname'];

	// to sure the number of posts displayed isn't negative or more than 15
	if ( !$number = (int) $instance['number'] )
		$number = 10;
	else if ( $number < 1 )
		$number = 1;
	else if ( $number > 15 )
		$number = 15;

	//the query that will get posts from a specific category. 
	//Wr slug the category because you actualy need the slug and not the name
	$r = new WP_Query(array('category_name' => $this->slug($catname), 'showposts' => $number, 'nopaging' => 0, 'post_status' => 'publish', 'caller_get_posts' => 1));
	
	//display the posts title as a link
	if ($r->have_posts()) :
?>
	<?php echo $before_widget; ?>
	<?php if ( $title ) echo $before_title . $title . $after_title; ?>
	<ul>
	<?php  while ($r->have_posts()) : $r->the_post(); ?>
	<li><a href="<?php the_permalink() ?>" title="<?php echo esc_attr(get_the_title() ? get_the_title() : get_the_ID()); ?>"><?php if ( get_the_title() ) the_title(); else the_ID(); ?> </a></li>
	<?php endwhile; ?>
	</ul>
	<?php echo $after_widget; ?>
<?php
		wp_reset_query();  // Restore global post data stomped by the_post().
	endif;

	echo $after_widget;
}

Display the back end option form

function form( $instance ) {

	/* Set up some default widget settings. */
	$defaults = array( 'title' => 'Example', 'catname' => 'news', 'number' => 5);
	$instance = wp_parse_args( (array) $instance, $defaults ); ?>

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

	<p>
		<label for="<?php echo $this->get_field_id( 'catname' ); ?>">Category name:</label>
		<input class="widefat" id="<?php echo $this->get_field_id( 'catname' ); ?>" name="<?php echo $this->get_field_name( 'catname' ); ?>" value="<?php echo $instance['catname']; ?>" />
	</p>
	
	<p>
		<label for="<?php echo $this->get_field_id( 'number' ); ?>">Number of posts to show</label>
		<input id="<?php echo $this->get_field_id( 'number' ); ?>" name="<?php echo $this->get_field_name( 'number' ); ?>" value="<?php echo $instance['number']; ?>" size="3" />
		<br /><small>(at most 15)</small>
	</p>

	<?php
}

Update values when the options are saved

function update( $new_instance, $old_instance ) {
	$instance = $old_instance;

	/* Strip tags (if needed) and update the widget settings. */
	$instance['title'] = strip_tags( $new_instance['title'] );
	$instance['catname'] = strip_tags( $new_instance['catname'] );
	$instance['number'] = $new_instance['number'];
	
	return $instance;
}

And now all we need to do is register our widget with WordPress and it should appear in the widget panel

/* Add our function to the widgets_init hook. */
add_action( 'widgets_init', 'post_by_cat_widgets' );

/* Function that registers our widget. */
function post_by_cat_widgets() {
	register_widget( 'Posts_By_Cat' );
}

The whole code with the slug function

<?php

/* Add our function to the widgets_init hook. */
add_action( 'widgets_init', 'post_by_cat_widgets' );

/* Function that registers our widget. */
function post_by_cat_widgets() {
	register_widget( 'Posts_By_Cat' );
}

class Posts_By_Cat extends WP_Widget {
	function Posts_By_Cat() {
		/* Widget settings. */
		$widget_ops = array( 'classname' => 'posts_by_cat', 'description' => 'Display posts from only one defined category' );

		/* Widget control settings. */
		//$control_ops = array( 'width' => 300, 'height' => 350, 'id_base' => 'posts_by_cat-widget' );

		/* Create the widget. */
		$this->WP_Widget( 'posts_by_cat-widget', 'Posts by Category', $widget_ops);
	}

	function widget( $args, $instance ) {
		extract( $args );

		/* User-selected settings. */
		$title = apply_filters('widget_title', $instance['title'] );
		$catname = $instance['catname'];

		// to sure the number of posts displayed isn't negative or more than 15
		if ( !$number = (int) $instance['number'] )
			$number = 10;
		else if ( $number < 1 )
			$number = 1;
		else if ( $number > 15 )
			$number = 15;

		//the query that will get post from a specific category. 
		//Wr slug the category because you actualy need the slug and not the name
		$r = new WP_Query(array('category_name' => $this->slug($catname), 'showposts' => $number, 'nopaging' => 0, 'post_status' => 'publish', 'caller_get_posts' => 1));
		
		//display the posts title as a link
		if ($r->have_posts()) :
	?>
		<?php echo $before_widget; ?>
		<?php if ( $title ) echo $before_title . $title . $after_title; ?>
		<ul>
		<?php  while ($r->have_posts()) : $r->the_post(); ?>
		<li><a href="<?php the_permalink() ?>" title="<?php echo esc_attr(get_the_title() ? get_the_title() : get_the_ID()); ?>"><?php if ( get_the_title() ) the_title(); else the_ID(); ?> </a></li>
		<?php endwhile; ?>
		</ul>
		<?php echo $after_widget; ?>
	<?php
			wp_reset_query();  // Restore global post data stomped by the_post().
		endif;

		echo $after_widget;
	}

	function update( $new_instance, $old_instance ) {
		$instance = $old_instance;

		/* Strip tags (if needed) and update the widget settings. */
		$instance['title'] = strip_tags( $new_instance['title'] );
		$instance['catname'] = strip_tags( $new_instance['catname'] );
		$instance['number'] = $new_instance['number'];
		
		return $instance;
	}

	function form( $instance ) {

		/* Set up some default widget settings. */
		$defaults = array( 'title' => 'Example', 'catname' => 'news', 'number' => 5);
		$instance = wp_parse_args( (array) $instance, $defaults ); ?>

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

		<p>
			<label for="<?php echo $this->get_field_id( 'catname' ); ?>">Category name:</label>
			<input class="widefat" id="<?php echo $this->get_field_id( 'catname' ); ?>" name="<?php echo $this->get_field_name( 'catname' ); ?>" value="<?php echo $instance['catname']; ?>" />
		</p>
		
		<p>
			<label for="<?php echo $this->get_field_id( 'number' ); ?>">Number of posts to show</label>
			<input id="<?php echo $this->get_field_id( 'number' ); ?>" name="<?php echo $this->get_field_name( 'number' ); ?>" value="<?php echo $instance['number']; ?>" size="3" />
			<br /><small>(at most 15)</small>
		</p>

		<?php
	}

	function slug($string)
	{
		$slug = trim($string);
		$slug= preg_replace('/[^a-zA-Z0-9 -]/','', $slug); // only take alphanumerical characters, but keep the spaces and dashes too...
		$slug= str_replace(' ','-', $slug); // replace spaces by dashes
		$slug= strtolower($slug); // make it lowercase
		return $slug;
	}

}

?>

I’ve got some ideas for improvement, like option to display the excerpt of each post or not but it has proven very useful so far. Now it is your turn to build your own widgets.

Related Items and Services:
Ladders at great prices
Security Management available here

8 responses to “Create a posts by category widget in WordPress 2.8”

  1. Jan Seidl

    Nice article on wordpress plugin building! Keep up!

  2. Curtis Penner

    Just what I was looking for…

    Now where do I save this so it shows up in the widgets section?

  3. mike

    Thanks for this – it should make a good base for a widget I want, and an introduction into how to build other widgets in future.

  4. senseforweb

    Nice content on wordpress plugin building! Thank you for share

  5. Thomas J

    Before you invent the wheel, always check if someone else have done it before…
    This is exactly what I needed! Thanks man!

Leave a Reply

  1. Create a posts by category widget in WordPress 2.8

    [...] Visit Source. [...]

Theme Forest ad
Wordpress Themes Collection ad
Woothemes

Poll

How do you start your WordPress plugins development projects?

View Results

Loading ... Loading ...