Skip to content

Instantly share code, notes, and snippets.

@dmaicher
Last active April 16, 2020 08:47
Show Gist options
  • Save dmaicher/e282c59d81dedde89bacb3bb9bb360cb to your computer and use it in GitHub Desktop.
Save dmaicher/e282c59d81dedde89bacb3bb9bb360cb to your computer and use it in GitHub Desktop.
use symfony/cache 3.2 tag aware cache as Doctrine DBAL query result cache
<?php
$sql = 'some query';
$parameters = [];
$types = [];
$queryCacheProfile = new QueryCacheProfile(7200, null, $tagAwareResultCache);
// use 'foo' as tag for query cache item
$tagAwareResultCache->setQueryCacheTags($sql, $parameters, $types, ['foo']);
// perform query and write it into our cache
$connection->executeCacheQuery($sql, $parameters, $types, $queryCacheProfile);
// invalidate previously stored cache item
$tagAwareResultCache->getTagAwareAdapter()->invalidateTags(['foo']);
<?php
use Doctrine\DBAL\Cache\QueryCacheProfile;
use Symfony\Component\Cache\Adapter\TagAwareAdapterInterface;
use Symfony\Component\Cache\CacheItem;
use Symfony\Component\Cache\DoctrineProvider;
class TagAwareQueryResultCache extends DoctrineProvider
{
/**
* @var TagAwareAdapterInterface
*/
private $tagAwareAdapter;
/**
* @var array
*/
private $queryTags = [];
/**
* @var string
*/
private $currentIdWithoutNamespace;
/**
* @param TagAwareAdapterInterface $tagAwareAdapter
*/
public function __construct(TagAwareAdapterInterface $tagAwareAdapter)
{
parent::__construct($tagAwareAdapter);
$this->tagAwareAdapter = $tagAwareAdapter;
}
/**
* @param $query
* @param array $params
* @param array $types
*
* @return string
*/
private function getDoctrineQueryCacheKey($query, array $params, array $types)
{
return (new QueryCacheProfile())->generateCacheKeys($query, $params, $types)[0];
}
/**
* {@inheritdoc}
*/
public function save($id, $data, $lifeTime = 0)
{
$this->currentIdWithoutNamespace = $id;
return parent::save($id, $data, $lifeTime);
}
/**
* {@inheritdoc}
*/
protected function doSave($id, $data, $lifeTime = 0)
{
/** @var CacheItem $item */
$item = $this->tagAwareAdapter->getItem(rawurlencode($id));
if (isset($this->queryTags[$this->currentIdWithoutNamespace])) {
$item->tag($this->queryTags[$this->currentIdWithoutNamespace]);
}
if (0 < $lifeTime) {
$item->expiresAfter($lifeTime);
}
$this->currentIdWithoutNamespace = null;
return $this->tagAwareAdapter->save($item->set($data));
}
/**
* @param string $query
* @param array $params
* @param array $types
* @param array $tags
*
* @return $this
*/
public function setQueryCacheTags($query, array $params, array $types, array $tags)
{
$this->queryTags[$this->getDoctrineQueryCacheKey($query, $params, $types)] = $tags;
return $this;
}
/**
* @return TagAwareAdapterInterface
*/
public function getTagAwareAdapter()
{
return $this->tagAwareAdapter;
}
}
@shubaivan
Copy link

shubaivan commented Apr 16, 2020

@dmaicher Hi, I just tested you case and cache not invalidated, because need use the same QueryCacheProfile when call executeCacheQuery and when call setQueryCacheTags.
Could you look please my changes ? It's works correctly, cache creaetd with tag and when called invalidateTags, cache invalidated
https://gist.github.com/shubaivan/5e8217f3b5b0afbd302a5a93506bb26e/revisions

@dmaicher
Copy link
Author

@shubaivan in my case it works, because I pass null as a cacheKey to the QueryCacheProfile constructor.

If one passes some string it will indeed not work properly 👍

@shubaivan
Copy link

@dmaicher I understand, you are right, if did not use $cacheKey for QueryCacheProfile constructor it will be null and in \Doctrine\DBAL\Cache\QueryCacheProfile::generateCacheKeys cache key will be the same $cacheKey = sha1($realCacheKey);. Thank you

@shubaivan
Copy link

@dmaicher but in your case used two separate object QueryCacheProfile one in TagAwareQueryResultCache and one for executeCacheQuery. Perhaps better use one like in my revision

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment