Welcome to Abdul Malik Ikhsan's Blog

Zend Framework 2 : Using Zend\Cache and HydratingResultSet to Save Database Resultset into Cache

Posted in Teknologi, Tutorial PHP, Zend Framework 2 by samsonasik on September 27, 2012

Re-load data from the database every web was accessed is not good for your server ‘health’. Zend Framework has a component named Hydrator. The Hydrator is a simple component to provide mechanisms both for hydrating objects, as well as extracting data sets from them.
To save database ResultSet into cache, You need to use Zend\Db\ResultSet\HydratingResultSet which can extract the resultSet into associative array. In this post, i will show you how to cache your database resultset into filesystem.


Let’s code :
1. Register Zend\Cache\Storage\Filesystem into service manager. You can place it at global.php

return array(
    'db' => array(/*common code*/),
    'service_manager' => array(
        'factories' => array(
            'Zend\Db\Adapter\Adapter' => 'Zend\Db\Adapter\AdapterServiceFactory',
            'Zend\Cache\Storage\Filesystem' => function($sm){
		    $cache = Zend\Cache\StorageFactory::factory(array(
			'adapter' => 'filesystem',
			'plugins' => array(
			    'exception_handler' => array('throw_exceptions' => false),
			    'serializer'
			)
		    ));
		    
		    $cache->setOptions(array(
			    'cache_dir' => './data/cache'
		    ));
                    
                    return $cache;
	    },
        ),
    ),
);

2. Define Your Sample class

namespace SampleModule\Model;

use Zend\InputFilter\Factory as InputFactory;
use Zend\InputFilter\InputFilter;
use Zend\InputFilter\InputFilterAwareInterface;
use Zend\InputFilter\InputFilterInterface;

class Sample implements InputFilterAwareInterface
{
    public $id;
    public $name;
    public $gender;
    
    protected $inputFilter;

    public function exchangeArray($data)
    {
        $this->id     = (isset($data['id'])) ? $data['id'] : null;
        $this->name = (isset($data['name'])) ? $data['name'] : null;
        $this->gender = (isset($data['gender'])) ? $data['gender'] : null;
    }
    
   public function getArrayCopy()
    {
        return get_object_vars($this);
    } 
    
    public function setInputFilter(InputFilterInterface $inputFilter)
    {
        throw new \Exception("Not used");
    }
    
    public function getInputFilter()
    {
        /* common code here */
        return $this->inputFilter;
    }
}

3. next, create your Sample table class :

namespace SampleModule\Model;

use Zend\Db\Adapter\Adapter;
use Zend\Db\ResultSet\HydratingResultSet;
use Zend\Db\TableGateway\AbstractTableGateway;
use Zend\Db\Sql\Select;

use Zend\Cache\Storage\StorageInterface;

class SampleTable extends AbstractTableGateway
{
    protected $table = 'sampletable';
    protected $cache;
    
    public function __construct(Adapter $adapter)
    {
        $this->adapter = $adapter;
        $this->resultSetPrototype = new HydratingResultSet();
        $this->resultSetPrototype->setObjectPrototype(new Sample());
        
        $this->initialize();
    }
    
    public function setCache(StorageInterface $cache)
    {
        $this->cache = $cache;
    } 
    
    public function fetchAll()
    {   
        if( ($resultSet = $this->cache->getItem('samplecache')) == FALSE) {
           
            $resultSet = $this->select(function (Select $select){
                $select->columns(array('id', 'name', 'gender'));
                $select->order(array('id asc'));  
            });

            $resultSet = $resultSet->toArray();
            $this->cache->setItem('samplecache',  $resultSet );
        }
 
        return $resultSet;
    }
}

4. Last step, register Zend\Cache in Module.php to inject cache into Sample Table Class.

namespace SampleModule;

use Zend\ModuleManager\Feature\AutoloaderProviderInterface;
use Zend\ModuleManager\Feature\ConfigProviderInterface;
use Zend\ModuleManager\Feature\ServiceProviderInterface;

class Module  implements
    AutoloaderProviderInterface,
    ConfigProviderInterface,
    ServiceProviderInterface
{
    public function getAutoloaderConfig(){/*common code*/}
    public function getConfig(){/*common code*/}
    
    public function getServiceConfig()
    {
	return array(
	    'factories' => array(
                'SampleModule\Model\SampleTable' =>  function($sm){
                    $dbAdapter    = $sm->get('Zend\Db\Adapter\Adapter');
		    $cacheAdapter = $sm->get('Zend\Cache\Storage\Filesystem');
		    
                    $table     = new Model\SampleTable($dbAdapter);
		    $table->setCache($cacheAdapter);
		    
                    return $table;
                },
            ),	    
	);
    }
}

Remember, the resultset is now an array. Zend\Cache will serialize this and save into file. if we want to call in another resource, we need to call as array, not as Iterator.
for example :

namespace SampleModule\Controller;

use Zend\Mvc\Controller\AbstractActionController;
 
class TablesampleController extends AbstractActionController
{
    protected $sampletable;
    
    public function indexAction()
    {
        $resultSet = $this->getSampleTable()->fetchAll();
        
        //JUST TESTING......
        foreach($resultSet as $row)
        {
            echo $row['name']."<br />";
        }
    }
    
    public function getSampleTable()
    {
        if (!$this->sampletable){
            $this->sampletable = $this->getServiceLocator()->get('SampleModule\Model\SampleTable');
        }
        return $this->sampletable;
    }
}

How about updating data? we have to reset the cache if we want to show updated result.

$this->cache->removeItem('samplecache');

Done !

References :
1. http://www.framework.zend.com/manual/2.0/en/modules/zend.stdlib.hydrator.html

25 Responses

Subscribe to comments with RSS.

  1. Luis L. said, on October 5, 2012 at 10:49 am

    Great work man!
    Keep it up.

  2. samsonasik said, on October 5, 2012 at 11:31 am

    Thanks 😉

  3. Marc Bennewitz said, on October 25, 2012 at 11:39 pm

    Please note: You can define cache adapter options directly on factory:

    // …
    ‘Zend\Cache\Storage\Filesystem’ => function($sm){
    $cache = Zend\Cache\StorageFactory::factory(array(
    ‘adapter’ => array(
    ‘name’ => ‘filesystem’,
    ‘options’ => array(
    ‘cache_dir’ => ‘./data/cache’
    ),
    ),
    ‘plugins’ => array(
    ‘exception_handler’ => array(‘throw_exceptions’ => false),
    ‘serializer’
    )
    ));
    return $cache;
    },
    // …

  4. ill tel u that later said, on January 31, 2013 at 12:52 pm

    thanks for u re tutorials , but an advise plz for each file or command give the full path ….for file or directory otherwise it will not be useful to read u re article , also pllzzzz choose a convenient name for u re project and class name

  5. Sasha said, on February 2, 2013 at 3:25 pm

    Hi, thank you for your tutors – great work, please keep writing.

    Could you give also an explanation (example) for Paginator Cache? Trying this sample (http://framework.zend.com/manual/2.1/en/modules/zend.paginator.advanced.html#caching-features) I get error “You cannot serialize or unserialize PDOStatement instances”

    • samsonasik said, on February 2, 2013 at 7:29 pm

      you should add :

                  $resultSet->buffer();
                  $resultSet->next();
      

      before converting to array() : $resultSet->toArray();

      • Sasha said, on February 2, 2013 at 8:10 pm

        i mean Zend\Paginator\Adapter\DbSelect Adapter:

        from my Table Class I receive Zend\Db\Sql\Select object:

        $select->from(‘album’)->order(‘artist ASC’);
        return $select;

        In my Controller i use:

        $paginator = new \Zend\Paginator\Paginator(new \Zend\Paginator\Adapter\DbSelect($select, $dbAdapter));
        $paginator->setItemCountPerPage(10);
        $paginator->setCurrentPageNumber($page);

        How can I cache $paginator for every page according to Paginator Caching features ( http://framework.zend.com/manual/2.1/en/modules/zend.paginator.advanced.html#caching-features )

        Thank you for your time.

      • samsonasik said, on February 2, 2013 at 9:16 pm

        try use ArrayAdapter.

  6. mixian said, on May 25, 2013 at 4:19 am

    HI,
    it’s possible to delete just one item from file cache and not entire file and recreate it ?

    Example, i have a more than 700 photos and i want delete just one…i do delete entire file cache ? It’s this more expensive?

    Thanks

      • Rocco said, on May 25, 2013 at 4:32 pm

        Hi. I read entire documentation but “addItem” doesn’t work.

        $r1 = array(‘test1′,’test2’);
        $r2 = array(‘test3′,’test4’);

        $key = ‘rocco’;

        $success = $this->cache->getItem($key, $success);
        if ($success){
        $this->cache->addItem($key, $r2);
        }
        else {
        $this->cache->setItem($key,$r);
        }
        return $this->cache->getItem($key);

      • Rocco said, on May 25, 2013 at 5:03 pm

        I SOLVED use incrementItem:

        $r2 = array(‘test3′,’test4′);
        $key = ‘rocco’;

        $this->cache->incrementItem($key, $r2);

      • samsonasik said, on May 26, 2013 at 3:06 am

        great!

  7. saritha said, on June 20, 2013 at 1:35 pm

    Better explained..but updating cache with this ‘$this->cache->removeItem(‘samplecache’);’ is actually killing the advantage of caching the result. could you pls tell me how can it be effective

    • samsonasik said, on December 7, 2013 at 6:41 pm

      No. it’s not killing, in next request after updating, you can re-cache again.

  8. haohan82 said, on December 7, 2013 at 2:17 pm

    How to set different cache settings for paginator if I have multiple paginator?

    paginator1 filesystem, paginator2 memcache .. etc..

  9. may saghira said, on November 7, 2014 at 9:32 pm

    how can we use the doctrine cache in the stting / getting result queries in our controllers ?

  10. noomloso (@noomloso28) said, on February 21, 2018 at 4:31 pm

    Thank you for tutorial.
    Do you have idea for remove all cache items?

    • noomloso (@noomloso28) said, on February 22, 2018 at 10:03 am

      OK, Solved.

      $sm = $this->getServiceLocator();
      $sepcialCache = $sm->get(‘Zend\Cache\Storage\Filesystem’);

      foreach ($serviceCache as $key => $value) {

      if($value){
      $serviceCache->removeItem($value);
      }

      }

      • samsonasik said, on February 22, 2018 at 6:57 pm

        you should can call $serviceCache->flush();


Leave a comment