I have message entity repository and I want to cache results for query that fetches those messages. But the fetch repository function divides results into pages, so there is an offset and limit involved.
The only idea how I could invalidate just this one repository's result cache is to generate and remember every single cache id in another cache entry and then using that entry to invalidate them all.
Is there better solution to this problem, so I can simply invalidate result cache only for one entity/repository?
Edit: I just found out that cache id is not used for unique query but it caches all queries even if their variables changes.
public function getMessages($offset, $limit, $search = null){
$builder = $this->createQueryBuilder('m')
->orderBy('m.id', 'DESC')
->setFirstResult($offset)
->setMaxResults($limit);
if($search !== null){
$builder->where('m.title LIKE :searchQuery OR m.content LIKE :searchQuery')
->setParameter('searchQuery', '%'.$search.'%');
return $builder->getQuery()->getResult(Query::HYDRATE_ARRAY);
} else {
return $builder->getQuery()
->useResultCache(true, null, 'message_messages')
->getResult(Query::HYDRATE_ARRAY);
}
}
So I have 4 messages, and I have set to display 2 messages per page. So first query has 0 offset and 2 limit, and second query has 2 offset and also 2 limit.
And what happens is, both queries are cached under single cache id. I have made changes from phpMyAdmin and both pages were unchanged, which means both used cache. When I invalidated 'message_messages' cache id, both pages changed and changes made by phpMyAdmin were now visible.
I just want to say that this is working just how I wish it works. But I'm not sure if it is not some kind of undefined behavior?
I so this may be obvious for others, but I thought that if I useResultCache with id for eg. "resultCacheId1" and then I will use the same cache again, the second use will overwrite cache of first one. But what I found out is that it is grouped under one cache id like so:
The same function as from my question, I will just get rid of $search and $limit parameter to simplify it:
public function getMessages($offset){
$builder = $this->createQueryBuilder('m')
->orderBy('m.id', 'DESC')
->setFirstResult($offset)
->setMaxResults($limit)
->getQuery()
->useResultCache(true, null, 'message_messages')
->getResult(Query::HYDRATE_ARRAY);
}
For testing purposes, to fetch and look at what is inside the cache I use:
$em->getConfiguration()->getResultCacheImpl()->fetch('message_messages')
And if I use function once like getMessages(0) it will show up in the cache as:
array:1 {
"SELECT ... FROM message m0_ ORDER BY m0_.id DESC LIMIT 2 OFFSET 0-a:0:{}-a:0:{}" => array:2 {
0 => array:4 {id: 6, title: '...', content: '...', date: '...'},
1 => array:4 {id: 5, title: '...', content: '...', date: '...'},
}
}
And when I use it again like this getMessages(1) it will add another entry to 'message_messages' cache id:
array:1 {
"SELECT ... FROM message m0_ ORDER BY m0_.id DESC LIMIT 2 OFFSET 0-a:0:{}-a:0:{}" => array:2 {
0 => array:4 {id: 6, title: '...', content: '...', date: '...'},
1 => array:4 {id: 5, title: '...', content: '...', date: '...'},
},
"SELECT ... FROM message m0_ ORDER BY m0_.id DESC LIMIT 2 OFFSET 2-a:0:{}-a:0:{}" => array:2 {
0 => array:4 {id: 4, title: '...', content: '...', date: '...'},
1 => array:4 {id: 3, title: '...', content: '...', date: '...'},
}
}
And to invalidate it all I can just invalidate 'message_messages' cache id. So I don't need to build cache id for each offset unique like message_messages_0, message_messages_1, message_messages_2 and so one, and then invalidate them all in a loop.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With