How to use cache in OpenMage / Magento 1.x to speed things up

Updated . Posted . Visible to the public.

Mechanism: cache in model

First, save the cache

// app\code\core\Mage\Core\Model\App.php Mage_Core_Model_App
/**
 * Saving cache data
 *
 * @param   mixed $data
 * @param   string $id
 * @param   array $tags
 * @param null|false|int $lifeTime
 * @return  $this
 */
public function saveCache($data, $id, $tags = [], $lifeTime = false)
{
    $this->_cache->save($data, $id, $tags, $lifeTime);
    return $this;
}

$id is a unique string that identify the cache. It is customary all uppercase.

If $tags doesn't have Mage_Core_Model_Config::CACHE_TAG, then Mage_Core_Model_App::CACHE_TAG will be added:

// app\code\core\Mage\Core\Model\Cache.php
/**
 * Save data
 *
 * @param string $data
 * @param string $id
 * @param array $tags
 * @param null|false|int $lifeTime
 * @return bool
 */
public function save($data, $id, $tags = [], $lifeTime = null)
{
    if ($this->_disallowSave) {
        return true;
    }

    /**
     * Add global magento cache tag to all cached data exclude config cache
     */
    if (!in_array(Mage_Core_Model_Config::CACHE_TAG, $tags)) {
        $tags[] = Mage_Core_Model_App::CACHE_TAG;
    }
    return $this->getFrontend()->save((string)$data, $this->_id($id), $this->_tags($tags), $lifeTime);
}

So all caches are tag either Mage_Core_Model_Config::CACHE_TAG or Mage_Core_Model_App::CACHE_TAG.

Second, retrieve the cache

Mage::app()->loadCache($id)

There's no need to specify the tags when retrieving.

What are cache tags?

Tags are used to tag the cache to groups, so that it forms part of those groups, where each group can be deleted individually.

// Call Mage_Core_Model_App::saveCache()
Mage::app()->saveCache(serialize($data), 'UNPPERCASE_UNIQUE_STRING', [array_of_tags]);

The tags are highlighted in the following screenshot:

Image

Not all tags are included in the list. For example, Mage_Core_Model_App::CACHE_TAG and custom tags.

Button Flush Magento Cache will flush cache with tags Mage_Core_Model_App::CACHE_TAG which is MAGE; and Mage_Core_Model_Config::CACHE_TAG, which is CONFIG.

Button Flush Cache Storage will flush all caches, irregardless of tags.

See

  1. app\code\core\Mage\Adminhtml\controllers\CacheController.php
  2. app\code\core\Mage\Core\Model\Cache.php

Cache in Collection

It's easy to add cache in collection, just initCache().

    /**
     *  class extends Mage_Customer_Model_Resource_Customer_Collection
     * @param int $groupId
     * @return array
     */
    public function toOptionArray($groupId)
    {
        $this->addAttributeToFilter('group_id', $groupId)
            ->addAttributeToSelect('mc_clinic_name')
            ->setOrder('mc_clinic_name', 'ASC')
            ->initCache(Mage::app()->getCache(), SOME_PREFIX', ['my_custom_tag', 'collections']);
            
        return $this->_toOptionArray('entity_id', 'mc_clinic_name');
    }

If the customer attribute mc_clinic_name is changed, it's easy to refresh the cache in order to reflect the change.

    /**
     * Observe event adminhtml_customer_save_after
     */
    public function adminhtmlCustomerSaveAfter($observer)
    {
        $customer = $observer->getEvent()->getCustomer();
        if (
            Mage::app()->useCache('collections')
            && $customer->hasData('mc_clinic_name')
            && $customer->getData('mc_clinic_name') !== $customer->getOrigData('mc_clinic_name')
        ) {
            Mage::app()->cleanCache(['my_custom_tag']);
        }
    }

With cache enabled, the response reduced from 36ms to 24ms.

Other examples:

$idPrefix = 'ATTRIBUTE_OPTIONS_ID_' . $this->getAttribute()->getId();
$tags = array_merge(
    array('eav', Mage_Core_Model_Translate::CACHE_TAG),
    $this->getAttribute()->getCacheTags()
);
$collection = Mage::getResourceModel('eav/entity_attribute_option_collection')
    ->setPositionOrder('asc')
    ->setAttributeFilter($this->getAttribute()->getId())
    ->setStoreFilter($this->getAttribute()->getStoreId())
    ->initCache(Mage::app()->getCache(), $idPrefix, $tags)
    ->load();
$this->_options[$storeId]        = $collection->toOptionArray();
$this->_optionsDefault[$storeId] = $collection->toOptionArray('default_value');

Speed improved from 1.3ms to 0.7ms:

class Sxxx_Xxgs_Model_Resource_Catalog_Product_Option_Collection extends Mage_Catalog_Model_Resource_Product_Option_Collection
{
    /**
     * @param Mage_Catalog_Model_Product|int $product
     * @param int $storeId        
     * @return array [option_title => option_id]
     */                  
    public function toOptionHashByProduct($product, $storeId = 0)
    {
        $productId = $product instanceof Mage_Catalog_Model_Product
            ? $product->getId()
            : $product;
        $idPrefix = 'XXGS_PRODUCT_OPTIONS_' . $productId . '_' . $storeId;
        $tags = [
            self::CACHE_TAG,
            Mage_Catalog_Model_Product::CACHE_TAG
        ];
        $this->addTitleToResult($storeId)
             ->addFieldToSelect('option_id')
             ->addFieldToFilter('product_id', $productId)
             ->initCache(Mage::app()->getCache(), $idPrefix, $tags)
             ;
        return $this->_toOptionHash('title', 'option_id');
    }
}

From 3ms to 1ms:

    /**
     * Return hash key in iso3 or default iso2.
     * Always use cache. To flush, click button 'Flush Cache Storage'
     * in backend Cache Storage Management.
     *
     * @param bool $iso3
     * @param string $subsetCacheKey Required if filter for subset of countries.
     * @param string[] $subset List of countries to filter.
     * @param string json
     */
    public function toJsonByIso($iso3 = false, $subsetCacheKey = '', $subset = [])
    {
        $cacheKey = "BASE_REGION_JSON_ISO_$subsetCacheKey" . (int) $iso3;
        if ($cache = Mage::app()->loadCache($cacheKey)) {
            return unserialize($cache);
        } 
        
        if ($iso3) {
            $this->getSelect()
                ->joinLeft(
                    ['country' => $this->_countryTable],
                    'main_table.country_id = country.country_id'
                );
            $key = 'iso3_code';
        } else {
            $key = 'country_id';
        }

        if ($subsetCacheKey) {
            $this->addFieldToFilter($key, ['in' => $subset]);
        }

        $json = $this->toJsonByKey($key);
        Mage::app()->saveCache(serialize($json), $cacheKey, ['collections']);
        
        return $json;
    }

Cache in Block

CMS Static Block

From the core:

class Mage_Cms_Block_Block extends Mage_Core_Block_Abstract
{

    /**
     * Initialize cache
     *
     * @return null
     */
    protected function _construct()
    {
        /*
        * setting cache to save the cms block
        */
        $this->setCacheTags(array(Mage_Cms_Model_Block::CACHE_TAG));
        // See also $this->setCacheKey('rss_catalog_new_'.$this->_getStoreId());
        $this->setCacheLifetime(false);
    }

    /**
     * Prepare Content HTML
     *
     * @return string
     */
    protected function _toHtml()
    {
        $blockId = $this->getBlockId();
        $html = '';
        if ($blockId) {
            $block = Mage::getModel('cms/block')
                ->setStoreId(Mage::app()->getStore()->getId())
                ->load($blockId);
            if ($block->getIsActive()) {
                /* @var $helper Mage_Cms_Helper_Data */
                $helper = Mage::helper('cms');
                $processor = $helper->getBlockTemplateProcessor();
                $html = $processor->filter($block->getContent());
                $this->addModelTags($block);
            }
        }
        return $html;
    }

    /**
     * Retrieve values of properties that unambiguously identify unique content
     *
     * @return array
     */
    public function getCacheKeyInfo()
    {
        $blockId = $this->getBlockId();
        if ($blockId) {
            $result = array(
                'CMS_BLOCK',
                $blockId,
                Mage::app()->getStore()->getCode(),
            );
        } else {
            $result = parent::getCacheKeyInfo();
        }
        return $result;
    }
}

We can disable cache in order to make use of all features in email template processor:

    protected function _construct()
    {
        /**
         * Disable cache to enable email template processor.
         */
        if ($this->getDisableCache()) {
            $this->unsCacheLifetime();
            return;
        }
//...
    }

    protected function _toHtml()
    {
        //...
            if ($block->getIsActive()) {
                /* @var Mage_Cms_Helper_Data $helper */
                $helper = Mage::helper('cms');
                $processor = $helper->getBlockTemplateProcessor();
                if ($this->getDisableCache()) {
                    $processor->setVariables($this->getData());
                }
                $html = $processor->filter($block->getContent());
                $this->addModelTags($block);
            }
        //...
    }

Enable Cache in Custom Block

How to do it in our own block:

protected function _construct()
{
    $this->addData([
        'cache_lifetime' => 9999999999, // Zend_Cache maximum value
        'cache_tags'     => ['sxxx_base_block_widget_select'],
        'cache_key'      => 'phonecode',
    ]);

    return parent::_construct();
}
// app/code/core/Mage/Directory/Block/Data.php
public function getCountryHtmlSelect($defValue=null, $name='country_id', $id='country', $title='Country')
{
    Varien_Profiler::start('TEST: '.__METHOD__);
    if (is_null($defValue)) {
        $defValue = $this->getCountryId();
    }
    $cacheKey = 'DIRECTORY_COUNTRY_SELECT_STORE_'.Mage::app()->getStore()->getCode();
    if (Mage::app()->useCache('config') && $cache = Mage::app()->loadCache($cacheKey)) {
        $options = unserialize($cache);
    } else {
        $options = $this->getCountryCollection()->toOptionArray();
        if (Mage::app()->useCache('config')) {
            Mage::app()->saveCache(serialize($options), $cacheKey, array('config'));
        }
    }
    $html = $this->getLayout()->createBlock('core/html_select')
        ->setName($name)
        ->setId($id)
        ->setTitle(Mage::helper('directory')->__($title))
        ->setClass('validate-select')
        ->setValue($defValue)
        ->setOptions($options)
        ->getHtml();

    Varien_Profiler::stop('TEST: '.__METHOD__);
    return $html;
}

// sample code
public function getCountryHtmlSelect($defValue)
{
    /** @var Mage_Directory_Block_Data $block */
    $block = $this->getLayout()->createBlock('directory/data');
    return $block->getCountryHtmlSelect($defValue, 'nationality', 'nationality', 'Nationality');
}
kiatng
Last edit
kiatng
Attachments
Posted by kiatng to OpenMage (2020-06-24 02:12)