Magento 2 – How to Cache a Product Collection by Key

cachecollection;magento2products

I have added a collection to buff out related products with extra "most viewed" products from a products top level category.

This is a slow query and takes about 0.2 seconds so was wondering if this collection can be cached with a key based on top level category so I can query if this collection exists and add products to the current related products collection from the cached collection instead of rerunning it each time?

Something like below but this was for Magento 1:

https://stackoverflow.com/questions/3755104/how-to-cache-a-collection-in-magento

There could be anything up to 4000 products that could benefit from pulling products from this cached collection in some cases.

    if($this->_cacheType->load($categoryMain)) {
        $collection = unserialize($this->_cacheType->load($categoryMain));
    } else {
        $collection = $this->mostViewedCollection->addAttributeToSelect(
            ['name','sku','price']
        )->addViewsCount()->setStoreId(
            $storeId
        )->addStoreFilter(
            $storeId
        )->setPageSize(
            $count
        )
        ->addCategoriesFilter(array('in' => $categoryIds));
        $this->_cacheType->save(serialize($collection), $categoryMain, [\Harrigo\AutoAssociatedProducts\Model\Cache\Type::CACHE_TAG], 86400);
    }

The collections however throw an error when being sereialized which leaves me stuck. I do not want to turn it into an array as guess i'll have to rebuild items.phtml to render the collection. I have actually noticed that the main slow down seems to be elsewhere after timing the collections but I am still interested in this and what the proper way to achieve something like above would be.

Best Answer

Cache an array of entity ids and reload the collection from that. Also, if the page size is variable, add that to the identifier. See below.

$cache_id = $categoryMain . '_' . $storeId . '_' . $count;
if($this->_cacheType->load($cache_id)) {
    $entity_ids = unserialize($this->_cacheType->load($cache_id));
    $collection = $this->mostViewedCollection
      ->addAttributeToSelect(['name','sku','price'])
      ->addFieldToFilter('entity_id', ['in' => $entity_ids])
      ->addViewsCount();
} else {
    $collection = $this->mostViewedCollection->addAttributeToSelect(
        ['name','sku','price']
    )->addViewsCount()->setStoreId(
        $storeId
    )->addStoreFilter(
        $storeId
    )->setPageSize(
        $count
    );

    $entity_ids = [];
    foreach ($collection as $c) {
        $entity_ids[] = $c->getId();
    }

    $this->_cacheType->save(serialize($entity_ids), $cache_id, [\Harrigo\AutoAssociatedProducts\Model\Cache\Type::CACHE_TAG], 86400);
}

Though I'm having some thoughts that the speed issue might be related to addViewsCount. Might be a good idea to add a cache to that method. Alternatively, a second cache_id with an array of view counts matched to entity_ids and looped into that collection could do the trick if you don't want to plugin to Harrigo\AutoAssociatedProducts.

Related Topic