How to Create a Custom WordPress Widget

Widget is one of the most useful features provided by WordPress. Most WordPress developers even don’t use this feature.  Even only a few developers know that there is an option to Create  a Custom WordPress Widget

If you are one of those few who know that we can create a Custom WordPress Widget, this post is for you. In this, I will walk you through the complete process of how to create a custom WordPress Widget.

Here I will use an example with the following scenario, which was the client’s actual requirement.

The requirement was to create a custom category (Taxonomy) for the woocommerce products with the name “Items”.

All the terms added to Items taxonomy needs to be added to different product page sidebar like shop page, single page etc. First thing came to my mind was to create a shrtcode and then add to the sidebar of different pages but then I realize that instead I should create a custom WordPress widget which I can use at different locations by just drag and drop and even if client wants to remove that any time then he/she can remove.

Following are the steps I performed to create a custom WordPress widget that can add a custom taxonomy with the name “items”.

Step 1 – Create a custom taxonomy with the name “items”

Add the following code to your active theme’s functions.php or inside the plugin main file.

<?php
/**
 * add custom taxonomy to woocommerce products
 */
add_action( 'init', 'dcs_custom_taxonomy_Item' );
function dcs_custom_taxonomy_Item()  {
    $labels = array(
        'name'                       => 'Items',
        'singular_name'              => 'Item',
        'menu_name'                  => 'Item',
        'all_items'                  => 'All Items',
        'parent_item'                => 'Parent Item',
        'parent_item_colon'          => 'Parent Item:',
        'new_item_name'              => 'New Item Name',
        'add_new_item'               => 'Add New Item',
        'edit_item'                  => 'Edit Item',
        'update_item'                => 'Update Item',
        'separate_items_with_commas' => 'Separate Item with commas',
        'search_items'               => 'Search Items',
        'add_or_remove_items'        => 'Add or remove Items',
        'choose_from_most_used'      => 'Choose from the most used Items',
    );
    $args = array(
        'labels'                     => $labels,
        'hierarchical'               => true,
        'public'                     => true,
        'show_ui'                    => true,
        'show_admin_column'          => true,
        'show_in_nav_menus'          => true,
        'show_tagcloud'              => true,
    );
    register_taxonomy( 'item', 'product', $args );
    register_taxonomy_for_object_type( 'item', 'product' );
}

Explanation:

  1. The code starts with the opening PHP tag <?php.
  2. Lines 2-4 provide a comment block that briefly describes the purpose of the code, which is to add a custom taxonomy to WooCommerce products.
  3. Line 5 uses the add_action function to hook into the ‘init’ action of WordPress, triggering the execution of the dcs_custom_taxonomy_Item function when WordPress initializes.
  4. Lines 6-33 define the dcs_custom_taxonomy_Item function, where the custom taxonomy and its settings are configured.
  5. Lines 7-21 define an array $labels containing labels for the custom taxonomy.
  6. Lines 23-30 define another array $args containing parameters for the custom taxonomy, such as labels, hierarchical structure, visibility in the UI, and more.
  7. Line 32 uses the register_taxonomy function to register the custom taxonomy. It takes the taxonomy name (‘item’), the object type it belongs to (‘product’), and the arguments defined in $args.
  8. Line 33 uses the register_taxonomy_for_object_type function to associate the taxonomy (‘item’) with the object type (‘product’).

Step 2 – Create a custom WordPress widget

Now add the following code to your active theme’s functions.php or inside the plugin main file. This will create a custom widget with the name “DCS Widget”. You can change this and use any name for your widget.

<?php
/**
 * Custom widget in available widgets
 * @return [type] [description]
 */
function dcs_load_widget() {
    register_widget( 'dcs_widget' );
}
add_action( 'widgets_init', 'dcs_load_widget' );
class dcs_widget extends WP_Widget {
    function __construct() {
        parent::__construct(
            'dcs_widget',
            __('DCS Widget', 'dcs_widget_domain'),
            array( 'description' => __( 'Sample widget based on DCS Tutorial', 'dcs_widget_domain' ), ) 
        );
    }
    public function widget( $args, $instance ) {
        $title = apply_filters( 'widget_title', $instance['title'] );
        echo $args['before_widget'];
        if ( ! empty( $title ) )
            echo $args['before_title'] . $title . $args['after_title'];
        $terms = get_terms( array(
            'taxonomy' => 'item',
            'hide_empty' => false,
        ) );
        foreach ($terms as $key => $value) {
            echo "<br>".$value->name;
        }
        echo $args['after_widget'];
    }
    public function form( $instance ) {
        if ( isset( $instance[ 'title' ] ) ) {
            $title = $instance[ 'title' ];
        }
        else {
            $title = __( 'New title', 'dcs_widget_domain' );
        }
        ?>
        <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 esc_attr( $title ); ?>" />
        </p>
        <?php 
    }
    public function update( $new_instance, $old_instance ) {
        $instance = array();
        $instance['title'] = ( ! empty( $new_instance['title'] ) ) ? strip_tags( $new_instance['title'] ) : '';
        return $instance;
    }
}

Explanation:

  1. The code begins with the opening PHP tag <?php.
  2. Lines 2-4 provide a comment block explaining that the code is for a custom widget in available widgets.
  3. Lines 6-9 define a function dcs_load_widget to register the custom widget using register_widget. This function is hooked to the ‘widgets_init’ action.
  4. Lines 10-50 define a class dcs_widget that extends WP_Widget for the custom widget.
  5. Lines 11-15: The constructor (__construct) of the class sets up the widget, including its ID, name, and description.
  6. Lines 18-30: The widget method outputs the widget’s content on the front end. It retrieves the title and terms from the ‘item’ taxonomy, then displays them.
  7. Lines 32-44: The form method defines the widget form in the admin area. It includes an input field for the widget title.
  8. Lines 46-49: The update method sanitizes and saves the widget settings when updated in the admin area.

Optional (Bonus code) – Advanced option to exclude some terms by id

Add the following code instead of step 2 in the functins.php

<?php
/**
 * Custom widget in available widgets
 * @return [type] [description]
 */
function dcs_load_widget() {
    register_widget( 'dcs_widget' );
}
add_action( 'widgets_init', 'dcs_load_widget' );
class dcs_widget extends WP_Widget {
    function __construct() {
        parent::__construct(
            'dcs_widget',
            __('DCS Widget', 'dcs_widget_domain'),
            array( 'description' => __( 'Sample widget based on DCS Tutorial', 'dcs_widget_domain' ), ) 
        );
    }
    public function widget( $args, $instance ) {
        $title = apply_filters( 'widget_title', $instance['title'] );

        $excludeTerms = explode(",", $instance['excludeTerms']);

        $hideEmpty = $instance['hideEmpty'];

        echo $args['before_widget'];
        if ( ! empty( $title ) )
            echo $args['before_title'] . $title . $args['after_title'];
        $terms = get_terms( array(
            'taxonomy' => 'item',
            'hide_empty' => ($hideEmpty=="on")?true:false,
            'exclude' => $excludeTerms,
        ) );
        foreach ($terms as $key => $value) {
            echo "<br>".$value->name;
        }
        echo $args['after_widget'];
    }
    public function form( $instance ) {
        if ( isset( $instance[ 'title' ] ) ) {
            $title = $instance[ 'title' ];
        }
        else {
            $title = __( 'New title', 'dcs_widget_domain' );
        }

        if ( isset( $instance[ 'excludeTerms' ] ) ) {
            $excludeTerms = $instance[ 'excludeTerms' ];
        }

        if ( isset( $instance[ 'hideEmpty' ] ) ) {
            $hideEmpty = $instance[ 'hideEmpty' ];
        }
        ?>
        <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 esc_attr( $title ); ?>" />

            <label for="<?php echo $this->get_field_id( 'excludeTerms' ); ?>"><?php _e( 'Exclude these terms (comma separated ids):' ); ?></label> 
            <input class="widefat" id="<?php echo $this->get_field_id( 'excludeTerms' ); ?>" name="<?php echo $this->get_field_name( 'excludeTerms' ); ?>" type="text" value="<?php echo $excludeTerms; ?>" />


            <input class="widefat" id="<?php echo $this->get_field_id( 'hideEmpty' ); ?>" name="<?php echo $this->get_field_name( 'hideEmpty' ); ?>" type="checkbox" <?php echo $hideEmpty?"checked":''; ?> />
            <label for="<?php echo $this->get_field_id( 'hideEmpty' ); ?>"><?php _e( 'Hide Empty' ); ?></label> 

        </p>
        <?php 
    }
    public function update( $new_instance, $old_instance ) {
        $instance = array();
        $instance['title'] = ( ! empty( $new_instance['title'] ) ) ? strip_tags( $new_instance['title'] ) : '';
        $instance['excludeTerms'] = ( ! empty( $new_instance['excludeTerms'] ) ) ?  $new_instance['excludeTerms'] : '';
        $instance['hideEmpty'] = ( ! empty( $new_instance['hideEmpty'] ) ) ?  $new_instance['hideEmpty'] : '';
        return $instance;
    }
}