Reading list Switch to dark mode

    How to use Caching in WooCommerce Addons

    Updated 2 November 2023

    Introduction

    In a WooCommerce addon, we can implement different types of caching. In this article, we’ll explain majorly 3 types of caching in WooCommerce addons. These are Object caching, File caching, and Transient Caching.

    In our previous blog, we have already explained the basics of Caching in WordPress.

    1. Object Caching

    In object caching we store our data on an object of a class. As we know an object of a class persists throughout the page while the page is loading. So we can use the result of a query later on a page if we save it on the first fetch request.

    <?php
    /**
     * Object caching class.
     *
     * @package WK_Caching
     */
    defined( 'ABSPATH' ) || exit(); // Exit if access directly.
    
    if ( ! class_exists( 'WK_Caching_Object' ) ) {
    	/**
    	 * WK_Caching_Object Class.
    	 */
    	class WK_Caching_Object {
    		/**
    		 * Instance variable
    		 *
    		 * @var $instance
    		 */
    		protected static $instance = null;
    
    		/**
    		 * Instance variable.
    		 *
    		 * @var $instance
    		 */
    		protected $cache = array();
    
    		/**
    		 * Constructor.
    		 */
    		public function __construct() {
    		}
    
    		/**
    		 * Ensures only one instance of this class is loaded or can be loaded.
    		 *
    		 * @return object
    		 */
    		public static function get_instance() {
    			if ( ! static::$instance ) {
    				static::$instance = new self();
    			}
    			return static::$instance;
    		}
    
    		/**
    		 * Set the data in object cache on a key with group.
    		 *
    		 * @param string     $key The key on which data will be saved.
    		 * @param mixed      $data Data to save.
    		 * @param string|int $data_group The data group.
    		 */
    		public function set( $key, $data, $data_group = '0' ) {
    			WK_Caching::log( "Set Cache key: $key, Cache group: $data_group" );
    			$this->cache[ $data_group ][ $key ] = $data;
    		}
    
    		/**
    		 * Get the cached data by the key or data group.
    		 *
    		 * @param string     $key Key of the data.
    		 * @param string|int $data_group The data group.
    		 *
    		 * @return bool|mixed
    		 */
    		public function get( $key, $data_group = '0' ) {
    			if ( isset( $this->cache[ $data_group ] ) && isset( $this->cache[ $data_group ][ $key ] ) && ! empty( $this->cache[ $data_group ][ $key ] ) ) {
    				return $this->cache[ $data_group ][ $key ];
    			}
    
    			return false;
    		}
    
    		/**
    		 * Reset the cache by group or complete reset by force param.
    		 *
    		 * @param string $data_group The data group.
    		 * @param bool   $force Reset the complete cache object.
    		 *
    		 * @return void
    		 */
    		public function reset( $data_group = '0', $force = false ) {
    			if ( true === $force ) {
    				$this->cache = array();
    			} elseif ( isset( $this->cache[ $data_group ] ) ) {
    				$this->cache[ $data_group ] = array();
    			}
    		}
    	}
    }

    Here we have created a class ‘WK_Caching_Object‘ to store the query result on its object keys. Then created a class element ‘$cache’ of type array to save the result. Finely created 3 methods namely set, get, and reset to handle the CRUD operation.

    2. File Caching

    In file caching, we store the result of a DB query in some text/JSON file. While querying the same result we supply the result data from the file rather than DB. And due to faster access of files over DB the Turn Around Time improves significantly.

    Searching for an experienced
    WordPress Company ?
    Find out More
    <?php
    /**
     * File caching.
     *
     * @package WK_Caching
     */
    defined( 'ABSPATH' ) || exit(); // Exit if access directly.
    
    /**
     * Including WordPress Filesystem API
     */
    require_once ABSPATH . '/wp-admin/includes/file.php';
    
    if ( function_exists( 'WP_Filesystem' ) ) {
    	WP_Filesystem();
    }
    
    if ( class_exists( 'WP_Filesystem_Direct' ) ) {
    	/**
    	 * WK_Caching_File class.
    	 */
    	class WK_Caching_File extends WP_Filesystem_Direct {
    		/**
    		 * Upload directory.
    		 *
    		 * @var mixed
    		 */
    		private $upload_dir;
    
    		/**
    		 * Core directory name.
    		 *
    		 * @var string
    		 */
    		private $wk_core_dir = '';
    
    		/**
    		 * Directory name for files in Uploads folder.
    		 *
    		 * @var string
    		 */
    		private $wk_dir = 'wkwc';
    
    		/**
    		 * Module wise directory name.
    		 *
    		 * @var string
    		 */
    		private $module_dir_name = '';
    
    		/**
    		 * __construct
    		 *
    		 * @param  string $module Module wise directory name.
    		 *
    		 * @return void
    		 */
    		public function __construct( $module ) {
    			$upload            = wp_upload_dir();
    			$this->upload_dir  = $upload['basedir'];
    			$this->wk_core_dir = $this->upload_dir . '/' . $this->wk_dir;
    			$this->set_module_dir( $module );
    
    			$this->makedirs();
    		}
    
    		/**
    		 * Set Module name.
    		 *
    		 * @param string $module Module name.
    		 *
    		 * @return void
    		 */
    		public function set_module_dir( $module ) {
    			if ( '' !== $module ) {
    				$this->module_dir_name = $module;
    			}
    		}
    
    		/**
    		 * Get module directory.
    		 *
    		 * @return string
    		 */
    		public function get_module_dir() {
    			return $this->wk_core_dir . '/' . $this->module_dir_name;
    		}
    
    		/**
    		 * Create file.
    		 *
    		 * @param string $file File directory.
    		 * @param int    $time Time stamp.
    		 * @param string $atime At time.
    		 *
    		 * @return string
    		 */
    		public function touch( $file, $time = 0, $atime = 0 ) {
    			$file = $this->file_path( $file );
    
    			return parent::touch( $file, $time, $atime );
    		}
    
    		/**
    		 * Get file path.
    		 *
    		 * @param string $file File name.
    		 *
    		 * @return string
    		 */
    		public function file_path( $file ) {
    			$file_path = $this->wk_core_dir . '/' . $this->module_dir_name . '/' . $file;
    
    			return $file_path;
    		}
    
    		/**
    		 * Folder path.
    		 *
    		 * @param string $folder_name Folder name.
    		 *
    		 * @return string
    		 */
    		public function folder_path( $folder_name ) {
    			$folder_path = $this->wk_core_dir . '/' . $folder_name . '/';
    
    			return $folder_path;
    		}
    
    		/**
    		 * Is readable.
    		 *
    		 * @param string $file File name.
    		 *
    		 * @return bool
    		 */
    		public function is_readable( $file ) {
    			$file = $this->file_path( $file );
    
    			return parent::is_readable( $file );
    		}
    
    		/**
    		 * Is writable.
    		 *
    		 * @param string $file File name.
    		 *
    		 * @return bool
    		 */
    		public function is_writable( $file ) {
    			$file = $this->file_path( $file );
    
    			return parent::is_writable( $file );
    		}
    
    		/**
    		 * Put contents.
    		 *
    		 * @param string $file File name.
    		 * @param string $contents File content.
    		 * @param bool   $mode File mode.
    		 *
    		 * @return bool
    		 */
    		public function put_contents( $file, $contents, $mode = false ) {
    			$file = $this->file_path( $file );
    
    			return parent::put_contents( $file, $contents, $mode );
    		}
    
    		/**
    		 * Delete file.
    		 *
    		 * @param string $file File name.
    		 * @param bool   $recursive Recursive.
    		 * @param string $type File type.
    		 *
    		 * @return bool
    		 */
    		public function delete_file( $file, $recursive = false, $type = 'f' ) {
    			$file = $this->file_path( $file );
    
    			return parent::delete( $file, $recursive, $type );
    		}
    
    		/**
    		 * Delete all.
    		 *
    		 * @param string $folder_name Folder name.
    		 * @param bool   $recursive Is recursive.
    		 *
    		 * @return bool
    		 */
    		public function delete_all( $folder_name, $recursive = false ) {
    			$folder_path = $this->folder_path( $folder_name );
    
    			return parent::rmdir( $folder_path, $recursive );
    		}
    
    		/**
    		 * Gets details for files in a directory or a specific file.
    		 *
    		 * @since 2.5.0
    		 *
    		 * @param string $path           Path to directory or file.
    		 * @param bool   $include_hidden Optional. Whether to include details of hidden ("." prefixed) files. Default true.
    		 * @param bool   $recursive      Optional. Whether to recursively include file details in nested directories. Default false.
    		 *
    		 * @return array|false {
    		 *     Array of files. False if unable to list directory contents.
    		 *
    		 *     @type string $name        Name of the file or directory.
    		 *     @type string $perms       *nix representation of permissions.
    		 *     @type int    $permsn      Octal representation of permissions.
    		 *     @type string $owner       Owner name or ID.
    		 *     @type int    $size        Size of file in bytes.
    		 *     @type int    $lastmodunix Last modified unix timestamp.
    		 *     @type mixed  $lastmod     Last modified month (3 letter) and day (without leading 0).
    		 *     @type int    $time        Last modified time.
    		 *     @type string $type        Type of resource. 'f' for file, 'd' for directory.
    		 *     @type mixed  $files       If a directory and $recursive is true, contains another array of files.
    		 * }
    		 */
    		public function dirlist( $path, $include_hidden = true, $recursive = false ) {
    			if ( $this->is_file( $path ) ) {
    				$limit_file = basename( $path );
    				$path       = dirname( $path );
    			} else {
    				$limit_file = false;
    			}
    			if ( ! $this->is_dir( $path ) ) {
    				return false;
    			}
    
    			$dir = dir( $path );
    			if ( ! $dir ) {
    				return false;
    			}
    
    			$ret = array();
    
    			while ( false !== ( $entry = $dir->read() ) ) {
    				$struc         = array();
    				$struc['name'] = $entry;
    
    				if ( '.' == $struc['name'] || '..' == $struc['name'] ) {
    					continue;
    				}
    
    				if ( ! $include_hidden && '.' == $struc['name'][0] ) {
    					continue;
    				}
    
    				if ( $limit_file && $struc['name'] != $limit_file ) {
    					continue;
    				}
    
    				$struc['perms']       = $this->gethchmod( $path . '/' . $entry );
    				$struc['permsn']      = $this->getnumchmodfromh( $struc['perms'] );
    				$struc['number']      = false;
    				$struc['owner']       = $this->owner( $path . '/' . $entry );
    				$struc['group']       = $this->group( $path . '/' . $entry );
    				$struc['size']        = $this->size( $path . '/' . $entry );
    				$struc['lastmodunix'] = $this->mtime( $path . '/' . $entry );
    				$struc['lastmod']     = gmdate( 'M j', $struc['lastmodunix'] );
    				$struc['time']        = gmdate( 'h:i:s', $struc['lastmodunix'] );
    				$struc['type']        = $this->is_dir( $path . '/' . $entry ) ? 'd' : 'f';
    
    				if ( 'd' == $struc['type'] ) {
    					if ( $recursive ) {
    						$struc['files'] = $this->dirlist( $path . '/' . $struc['name'], $include_hidden, $recursive );
    					} else {
    						$struc['files'] = array();
    					}
    				}
    
    				$ret[ $struc['name'] ] = $struc;
    			}
    			$dir->close();
    			unset( $dir );
    			return $ret;
    		}
    
    		/**
    		 * Delete folder.
    		 *
    		 * @param string $folder_path Folder path.
    		 * @param bool   $recursive Delete recursive.
    		 *
    		 * @return bool
    		 */
    		public function delete_folder( $folder_path, $recursive = false ) {
    			return parent::rmdir( $folder_path, $recursive );
    		}
    
    		/**
    		 * If file exists.
    		 *
    		 * @param string $file File name.
    		 *
    		 * @return bool
    		 */
    		public function exists( $file ) {
    			$file = $this->file_path( $file );
    
    			return parent::exists( $file );
    		}
    
    		/**
    		 * Get contents.
    		 *
    		 * @param string $file File name.
    		 *
    		 * @return bool
    		 */
    		public function get_contents( $file ) {
    			$file = $this->file_path( $file );
    
    			return parent::get_contents( $file );
    		}
    
    		/**
    		 * Make Dirs.
    		 *
    		 * @return void
    		 */
    		public function makedirs() {
    			$module = $this->module_dir_name;
    
    			if ( parent::is_writable( $this->upload_dir ) ) {
    				if ( false === $this->is_dir( $this->wk_core_dir ) ) {
    					$this->mkdir( $this->wk_core_dir );
    					$file_handle = @fopen( trailingslashit( $this->wk_core_dir ) . '/.htaccess', 'w' ); // phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged, WordPress.WP.AlternativeFunctions.file_system_read_fopen
    					if ( $file_handle ) {
    						fwrite( $file_handle, 'deny from all' ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_read_fwrite
    						fclose( $file_handle ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_read_fclose
    					}
    				}
    				$dir = $this->wk_core_dir . '/' . $module;
    				if ( false === $this->is_dir( $dir ) ) {
    					$this->mkdir( $dir );
    				}
    			}
    		}
    	}
    }

    To implement the file caching we created a class ‘WK_Caching_File’ and extended the WordPress File System Direct class ‘WP_Filesystem_Direct’ to override different file-related methods. We set a folder to create inside the ‘wp-content/uploads’ folder.

    We put the result in JSON format in the file and delete the file after the result is modified or any DB data changes. In the process we retrieve the data first from the DB store it in a file and then serve it for rendering.

    3. Transient Caching

    In transient caching, we utilize the concept of transient API to store the result of the DB query in the option table with an expiration time.

    <?php
    /**
     * Transient caching.
     *
     * @package WK_Caching
     */
    defined( 'ABSPATH' ) || exit(); // Exit if access directly.
    
    if ( ! class_exists( 'WK_Caching_Transient' ) ) {
    	/**
    	 * WK_Caching_Transient class.
    	 */
    	class WK_Caching_Transient {
    		/**
    		 * Instance variable
    		 *
    		 * @var $instance
    		 */
    		protected static $instance = null;
    
    		/**
    		 * WKWC_Wallet_Transient_Cache constructor.
    		 */
    		public function __construct() {
    
    		}
    
    		/**
    		 * Ensures only one instance of this class is loaded or can be loaded.
    		 *
    		 * @return object
    		 */
    		public static function get_instance() {
    			if ( ! static::$instance ) {
    				static::$instance = new self();
    			}
    			return static::$instance;
    		}
    
    		/**
    		 * Set the transient contents by key and group within page scope.
    		 *
    		 * @param string $transient_key Transient key.
    		 * @param mixed  $transient_value Transient value.
    		 * @param string $transient_group Transient group.
    		 * @param int    $expiry Default 1 hour.
    		 */
    		public function set( $transient_key, $transient_value, $transient_group = 'wk', $expiry = 3600 ) {
    			$option_key = '_wkwc_transient_' . $transient_group . '_' . $transient_key;
    
    			WK_Caching::log( "Set transient key: $transient_key, Option key: $option_key, Transient group: $transient_group, Expiry: $expiry" );
    
    			$transient_value = array(
    				'time'  => time() + (int) $expiry,
    				'value' => $transient_value,
    			);
    
    			$file_writing       = $this->enable_file_writing();
    			$cache_in_transient = $this->enable_transient_caching();
    
    			if ( class_exists( 'WK_Caching_File' ) && true === $file_writing ) {
    				$file_api = new WK_Caching_File( $transient_group . '-transient' );
    				$file_api->touch( $option_key );
    
    				if ( $file_api->is_writable( $option_key ) && $file_api->is_readable( $option_key ) ) {
    					$transient_value = maybe_serialize( $transient_value );
    					$file_api->put_contents( $option_key, $transient_value );
    					WK_Caching::log( "Data written in file, transient key: $transient_key, Option key: $option_key, Transient group: $transient_group, Expiry: $expiry" );
    				} elseif ( $cache_in_transient ) {
    					// wkwc folder is not writable.
    					WK_Caching::log( "Data written in transient, transient key: $transient_key, Option key: $option_key" );
    					update_option( $option_key, $transient_value, false );
    				}
    			} elseif ( $cache_in_transient ) {
    				// WK file API method not available.
    				WK_Caching::log( "Cache Data written in transient, transient key: $transient_key, Option key: $option_key" );
    				update_option( $option_key, $transient_value, false );
    			}
    		}
    
    		/**
    		 * Get the transient contents by the transient key or group.
    		 *
    		 * @param string $transient_key Transient Key.
    		 * @param string $transient_group Transient Group.
    		 *
    		 * @return bool|mixed
    		 */
    		public function get( $transient_key, $transient_group = 'wk' ) {
    			$option_key = '_wkwc_transient_' . $transient_group . '_' . $transient_key;
    
    			$file_writing       = $this->enable_file_writing();
    			$cache_in_transient = $this->enable_transient_caching();
    
    			WK_Caching::log( "Get transient key: $transient_key, Option key: $option_key, Transient group: $transient_group, File writing: $file_writing, Transient caching enabled: $cache_in_transient" );
    
    			if ( class_exists( 'WK_Caching_File' ) && true === $file_writing ) {
    				$file_api = new WK_Caching_File( $transient_group . '-transient' );
    				if ( $file_api->is_writable( $option_key ) && $file_api->is_readable( $option_key ) ) {
    					$data  = $file_api->get_contents( $option_key );
    					$data  = maybe_unserialize( $data );
    					$value = $this->get_value( $option_key, $data );
    
    					if ( false !== $value ) {
    						WK_Caching::log( "Data found in file, Transient key: $transient_key, Option key: $option_key" );
    						return $value;
    					}
    
    					$file_api->delete( $option_key );
    				}
    			}
    
    			if ( ! $cache_in_transient ) {
    				return false;
    			}
    
    			// WK file api method is not available.
    			$data = get_option( $option_key, false );
    			if ( false === $data ) {
    				return false;
    			}
    
    			WK_Caching::log( "Data found in Transient with key: $transient_key, Option key: $option_key" );
    
    			return $this->get_value( $option_key, $data, true );
    		}
    
    		/**
    		 * Get transient value.
    		 *
    		 * @param string $transient_key Transient key.
    		 * @param string $data JSON data.
    		 * @param bool   $db_call is DB call.
    		 *
    		 * @return bool|string
    		 */
    		public function get_value( $transient_key, $data, $db_call = false ) {
    			$current_time = time();
    			if ( is_array( $data ) && isset( $data['time'] ) ) {
    				if ( $current_time > (int) $data['time'] ) {
    					if ( true === $db_call ) {
    						delete_option( $transient_key );
    					}
    
    					return false;
    				}
    				return $data['value'];
    			}
    
    			return false;
    		}
    
    		/**
    		 * Delete the transient by key.
    		 *
    		 * @param string $transient_key Transient key.
    		 * @param string $transient_group Transient group.
    		 */
    		public function delete_transient( $transient_key, $transient_group = 'wk' ) {
    			$option_key = '_wkwc_transient_' . $transient_group . '_' . $transient_key;
    
    			$file_writing       = $this->enable_file_writing();
    			$cache_in_transient = $this->enable_transient_caching();
    
    			WK_Caching::log( "Deleting transient and file, Option key: $option_key, File writing: $file_writing, Cache in transient: $cache_in_transient" );
    
    			if ( class_exists( 'WK_Caching_File' ) && true === $file_writing ) {
    				$file_api = new WK_Caching_File( $transient_group . '-transient' );
    
    				if ( $file_api->exists( $option_key ) ) {
    					WK_Caching::log( "Deleting file: $option_key" );
    					$file_api->delete_file( $option_key );
    				}
    			}
    
    			// Removing db transient.
    			if ( $cache_in_transient ) {
    				WK_Caching::log( "Deleting transient: $option_key" );
    				delete_option( $option_key );
    			}
    		}
    
    		/**
    		 * Delete all the transients
    		 *
    		 * @param string $transient_group Transient Group.
    		 */
    		public function delete_all_transients( $transient_group = '' ) {
    			global $wpdb;
    
    			// Removing files if file api exist.
    			$file_writing       = $this->enable_file_writing();
    			$cache_in_transient = $this->enable_transient_caching();
    
    			WK_Caching::log( "Deleting all transients and files: File writing: $file_writing, Cache in transient: $cache_in_transient" );
    
    			if ( class_exists( 'WK_Caching_File' ) && true === $file_writing ) {
    				$file_api = new WK_Caching_File( $transient_group . '-transient' );
    				$file_api->delete_all( $transient_group . '-transient', true );
    				WK_Caching::log( "Deleting all file: $transient_group" );
    			}
    
    			if ( $cache_in_transient ) {
    				// Removing db transient.
    				$query = "DELETE FROM `$wpdb->options` WHERE `option_name` LIKE '%_wk_transient_{$transient_group}%'";
    				$wpdb->query( $query ); //phpcs:ignore WordPress.DB.PreparedSQL
    
    				WK_Caching::log( "Deleting all transient: $transient_group" );
    			}
    		}
    
    		/**
    		 * Delete all wk plugins transients.
    		 */
    		public function delete_force_transients() {
    			global $wpdb;
    
    			// Removing files if file api exist.
    			$file_writing       = $this->enable_file_writing();
    			$cache_in_transient = $this->enable_transient_caching();
    
    			WK_Caching::log( "Deleting force transients and files: File writing: $file_writing, Cache in transient: $cache_in_transient" );
    
    			if ( class_exists( 'WK_Caching_File' ) && true === $file_writing ) {
    				$file_api = new WK_Caching_File( 'wk-transient' );
    
    				$upload      = wp_upload_dir();
    				$folder_path = $upload['basedir'] . '/wk';
    				$file_api->delete_folder( $folder_path, true );
    
    				WK_Caching::log( "Deleting force files: $folder_path" );
    			}
    
    			if ( $cache_in_transient ) {
    				// Removing db transient.
    				$query = "DELETE FROM `$wpdb->options` WHERE `option_name` LIKE '%_wk_transient_%'";
    				$wpdb->query( $query ); //phpcs:ignore WordPress.DB.PreparedSQL
    
    				WK_Caching::log( 'Deleting force transient' );
    			}
    		}
    
    		/**
    		 * Allow to stop file writing.
    		 *
    		 * @return bool
    		 */
    		protected function enable_file_writing() {
    			return apply_filters( '_wkwc_enable_file_writing', true );
    		}
    
    		/**
    		 * Allow to stop file writing.
    		 *
    		 * @return bool
    		 */
    		protected function enable_transient_caching() {
    			return apply_filters( '_wkwc_enable_transient_caching', true );
    		}
    	}
    }

    We have created the class ‘WK_Caching_Transient’ and added the method set, get, and delete methods for performing CRUD operations. Added File caching at the top and transient caching as a fallback because transient caching involves DB operations.

    Implementation

    To provide the public methods for using these 3 types of caching we have created a Core class ‘WK_Caching_Core’ and provide set, get, and reset methods.

    <?php
    /**
     * The Core cache class.
     *
     * @package WK_Caching
     */
    defined( 'ABSPATH' ) || exit(); // Exit if access directly.
    
    if ( ! class_exists( 'WK_Caching_Core' ) ) {
    	/**
    	 * WK_Caching_Core Class.
    	 */
    	class WK_Caching_Core {
    		/**
    		 * Instance variable
    		 *
    		 * @var $instance
    		 */
    		protected static $instance = null;
    
    		/**
    		 * Constructor.
    		 */
    		public function __construct() {
    		}
    
    		/**
    		 * Ensures only one instance of this class is loaded or can be loaded.
    		 *
    		 * @return object
    		 */
    		public static function get_instance() {
    			if ( ! static::$instance ) {
    				static::$instance = new self();
    			}
    			return static::$instance;
    		}
    
    		/**
    		 * Set the data in different cache on a key with group.
    		 *
    		 * @param string     $key The key on which data will be saved.
    		 * @param mixed      $data Data to save.
    		 * @param string|int $data_group The data group.
    		 * @param int        $expiry Expiry in seconds in case of transient.
    		 */
    		public function set( $key, $data, $data_group, $expiry = 3600 ) {
    			WK_Caching::log( "Set Cache key: $key, Cache group: $data_group" );
    			$cache_obj = WK_Caching_Object::get_instance();
    			$cache_obj->set( $key, $data, $data_group );
    
    			$transient_obj = WK_Caching_Transient::get_instance();
    			$transient_obj->set( $key, $data, $data_group, $expiry );
    		}
    
    		/**
    		 * Get the cached data by the key or data group.
    		 *
    		 * @param string     $key Key of the data.
    		 * @param string|int $data_group The data group.
    		 *
    		 * @return bool|mixed
    		 */
    		public function get( $key, $data_group ) {
    			WK_Caching::log( "Get Cache key: $key, Cache group: $data_group" );
    			$cache_obj = WK_Caching_Object::get_instance();
    			$data      = $cache_obj->get( $key, $data_group );
    
    			if ( ! empty( $data ) ) {
    				return $data;
    			}
    
    			$transient_obj = WK_Caching_Transient::get_instance();
    			return $transient_obj->get( $key, $data_group );
    		}
    
    		/**
    		 * Reset the cache by group or complete reset by force param.
    		 *
    		 * @param string $key The key to reset.
    		 * @param string $data_group The data group to rest.
    		 * @param bool   $force Reset the complete cache object.
    		 *
    		 * @return void
    		 */
    		public function reset( $key, $data_group, $force ) {
    			WK_Caching::log( "Reset Cache key: $key, Cache group: $data_group, Force: $force" );
    			$cache_obj = WK_Caching_Object::get_instance();
    			$cache_obj->reset( $data_group, $force );
    
    			$transient_obj = WK_Caching_Transient::get_instance();
    
    			if ( $force ) {
    				$transient_obj->delete_force_transients();
    			} elseif ( empty( $key ) ) {
    				$transient_obj->delete_all_transients( $data_group );
    			} else {
    				$transient_obj->delete_transient( $key, $data_group );
    			}
    		}
    	}
    }

    While setting data, we have set the data in object cache and file caching. If file writing is not enabled we have added transient caching in fallback and also implemented it in the same for reading the data from the cache.

    To implement it in our WooCommerce Wallet System module for getting wallet transactions list data, we created and object of the ‘WK_Caching_Core’ class. In the first attempt there is no data in the cache so get the data from the database using SQL queries.

    On getting results from SQL queries, set the cache and by default, the result is saved in the object cache followed by file or transient caching depending on whether file caching is enabled or not. Thus on subsequent requests, the result will be served from the caching API.

    /**
    		 * Get Transaction data.
    		 *
    		 * @param array $args Transaction Data query params.
    		 *
    		 * @return array
    		 */
    		public function get_transactions( $args ) {
    			$wpdb_obj = $this->wpdb;
    
    			$cache_group = empty( $args['cache_group'] ) ? 0 : $args['cache_group'];
    			$cache_key   = empty( $args['cache_key'] ) ? '' : $args['cache_key'];
    			$cache_obj   = WK_Caching_Core::get_instance();
    
    			if ( ! empty( $cache_key ) ) {
    				$result = $cache_obj->get( $cache_key, $cache_group );
    
    				if ( ! empty( $result ) ) {
    					WKWC_Wallet::log( "Object Cached group: $cache_group, Cached key: $cache_key, result : " . print_r( $result, true ) );
    					return $result;
    				}
    			}
    
    			$sql = 'SELECT ';
    
    			$sql .= empty( $args['fields'] ) ? '* ' : $args['fields'];
    			$sql .= " FROM {$wpdb_obj->prefix}wkwc_wallet_transactions WHERE 1=1";
    
    			if ( ! empty( $args['transaction_id'] ) ) {
    				$sql .= $wpdb_obj->prepare( ' AND `id`=%d', esc_sql( $args['transaction_id'] ) );
    			}
    
    			if ( ! empty( $args['customer'] ) ) {
    				$sql .= $wpdb_obj->prepare( ' AND (`customer`=%d || `sender`=%d)', esc_sql( $args['customer'] ), esc_sql( $args['customer'] ) );
    			}
    
    			if ( ! empty( $args['limit'] ) ) {
    				$sql .= $wpdb_obj->prepare( ' LIMIT %d', esc_sql( $args['limit'] ) );
    			}
    
    			if ( ! empty( $args['offset'] ) ) {
    				$sql .= $wpdb_obj->prepare( ' OFFSET %d', esc_sql( $args['offset'] ) );
    			}
    
    			$result = $wpdb_obj->get_results( $sql, 'ARRAY_A' );
    
    			$cache_obj->set( $cache_key, $result, $cache_group );
    
    			return $result;
    		}

    Conclusion: Caching in WooCommerce Addons

    That’s all about the implementation of 3 popular methods of caching in WooCommerce add-ons. We have shown the flow and working of Object caching followed by File caching and Transient caching.

    We also saw that object caching is only available for subsequent requests for the same data during a single page loading.

    File and Transient caching persists till the expiry time is given or updating/deleting the data. These caching techniques accomplish our goal to minimize database queries for similar results again and again.

    We are implementing these caching methods on our popular plugins like Multi Vendor Marketplace for WooCommerce and Point of Sale System for WooCommerce.

    Support

    For any technical assistance, please raise a ticket or reach us by mail at [email protected]

    Kindly visit the WooCommerce Addons page to see our addons and can also explore our WooCommerce Development Services.

    . . .

    Leave a Comment

    Your email address will not be published. Required fields are marked*


    Be the first to comment.

    Back to Top

    Message Sent!

    If you have more details or questions, you can reply to the received confirmation email.

    Back to Home

    Table of Content