Zend Framework 2 : Using Zend\Cache and HydratingResultSet to Save Database Resultset into Cache
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
[…] https://samsonasik.wordpress.com/2012/09/27/zend-framework-2-using-zendcache-and-hydratingresultset-t… […]
Great work man!
Keep it up.
Thanks 😉
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;
},
// …
thanks for the hint
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
Thanks for the suggestion.
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”
you should add :
before converting to array() : $resultSet->toArray();
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.
try use ArrayAdapter.
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
Read the docs : http://zf2.readthedocs.org/en/latest/modules/zend.cache.storage.adapter.html
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);
I SOLVED use incrementItem:
$r2 = array(‘test3′,’test4′);
$key = ‘rocco’;
$this->cache->incrementItem($key, $r2);
great!
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
No. it’s not killing, in next request after updating, you can re-cache again.
How to set different cache settings for paginator if I have multiple paginator?
paginator1 filesystem, paginator2 memcache .. etc..
define other service, please read about service manager : https://samsonasik.wordpress.com/2013/01/02/zend-framework-2-cheat-sheet-service-manager/
how can we use the doctrine cache in the stting / getting result queries in our controllers ?
maybe you can take a look http://marco-pivetta.com/doctrine-orm-zf2-tutorial/#/34
Thank you for tutorial.
Do you have idea for remove all cache items?
OK, Solved.
$sm = $this->getServiceLocator();
$sepcialCache = $sm->get(‘Zend\Cache\Storage\Filesystem’);
foreach ($serviceCache as $key => $value) {
if($value){
$serviceCache->removeItem($value);
}
}
you should can call $serviceCache->flush();