How to delete cached transients from a widget instance properly?

So I have created this widget which pulls a number of posts. And the query is being cached via transients. But I need the transient to be deleted whenever a post is saved so the query can be refreshed.

Have a look at this code snippet.

Read More
add_action( 'save_post', 'delete_transient' );

function delete_transient() {
     if ( $_POST['post_type'] === 'post' ) {
          // this is a hack around to delete widget transients
          $max = 20;

          for( $i = 1; $i <= $max; $i++ ) { 
           delete_transient( 'my-posts-widget-' . $i );
          }          
     }
}

Ok so everything setup and working however because a widget can have multiple instances, deleting the transient is becoming an issue. Here is why. Every time you put a widget into the sidebar, the unique number WP assigns is incremented. So if someone happened to drag the widget out and then put it back again, it will increment the number again. So my $max = 20 will soon not be valid anymore as the id could then be 30… I could set the $max to 100000+ but that is not practical.

So with that in mind, you can see this is not a fully working solution. So my question is how are you guys handling this or have an idea of a work around?

Thanks for looking.

Related posts

Leave a Reply

2 comments

  1. Use a widget cache and forget the actual number of active widgets.

    In your widget add this to your method widget():

    public function widget( $args, $instance ) 
    {
        // get the cache
        $cache = wp_cache_get( 'UNIQUE_WIDGET_IDENTIFIER', 'widget' );
    
        if ( !is_array( $cache ) )
            $cache = array();
    
        if ( ! isset ( $args['widget_id'] ) )
            $args['widget_id'] = $this->id;
    
        if ( isset ( $cache[ $args['widget_id'] ] ) )
            return print $cache[ $args['widget_id'] ];
    
        // go on with your widget logic, put everything into a string and …
    
        $cache[ $args['widget_id'] ] = $widget_string;
    
        wp_cache_set( 'UNIQUE_WIDGET_IDENTIFIER', $cache, 'widget' );
    
        print $widget_string;
    }
    

    And in your widget’s __construct() register the flush actions:

    add_action( 'save_post',    array( $this, 'flush_widget_cache' ) );
    add_action( 'deleted_post', array( $this, 'flush_widget_cache' ) );
    add_action( 'switch_theme', array( $this, 'flush_widget_cache' ) );
    

    The callback in your widget class is just a simple …

    public function flush_widget_cache() 
    {
        wp_cache_delete( 'UNIQUE_WIDGET_IDENTIFIER', 'widget' );
    }
    

    Transients time out even if nothing has changed. That’s not what you need here.
    Don’t forget to replace UNIQUE_WIDGET_IDENTIFIER with your own value. 🙂

  2. So I came up with this hacky work around for now but it works.

    add_action( 'save_post', 'delete_transient' );
    
    function delete_transient() {
         if ( $_POST['post_type'] === 'post' ) {
              $ids = get_widget_id_number();
    
              foreach( $ids as $id ) { 
                  delete_transient( 'my-posts-widget-' . $id );
              }          
         }
    }
    
    function get_widget_id_number() {
        $widgets = get_option( 'sidebars_widgets' );
        $widget_ids = array();
    
        foreach( $widgets as $widget ) {
            if ( is_array( $widget ) ) {
                foreach( $widget as $widget_saved ) {
                    $widget_saved_name = substr( $widget_saved, 0, strrpos( $widget_saved, '-' ) );
                    $widget_id = substr( $widget_saved, -1, strrpos( $widget_saved, '-' ) );
                    if ( $widget_saved_name === 'my-posts-widget' )
                        $widget_ids[] = $widget_id;
                }
            }
        }
    
        array_unique( $widget_ids );
    
        return $widget_ids;
    }
    

    Hope that will help others in this situation.