LostKobrakai Posted May 20, 2016 Share Posted May 20, 2016 WireCache is a really nice way to quickly cache data to the database, but when working with json data there are some quirks. Imagine the following examples: $data = $cache->get('my-key', WireCache::expireHourly); if(!$data){ $data = […] $cache->save('my-key', $data, WireCache::expireHourly); } // API response $response = json_decode($data); $image = $response->data[0]->image; // or $html = "<div data-json='$data'></div>"; Both should work from the quick look. Both will fail as soon as the cache kicks in. This is because the implementation of WireCache tries to be smart and does automatically decode data, which is detected to be json. But it doesn't just decode it like in my example, but rather uses json_decode()'s second parameter to decode the json as associative array(s) instead of stdobject(s). If you prefer the object syntay to traverse your json data or you really want to store raw json, then I've got two hooks for you, which do prevent the automatic json detection of WireCache, so you can work with the stringified json as you need to. Just replace the get() and save() calls in the example with getRaw() and saveRaw(). $wire->addHook('WireCache::saveRaw', function(HookEvent $event){ $args = $event->arguments(); $args[1] = '::RAW::' . $args[1]; return $event->return = call_user_func_array([$event->object, 'save'], $args); }); $wire->addHook('WireCache::getRaw', function(HookEvent $event){ $args = $event->arguments(); return $event->return = str_replace('::RAW::', '', call_user_func_array([$event->object, 'get'], $args)); }); 11 Link to comment Share on other sites More sharing options...
Stikki Posted May 20, 2016 Share Posted May 20, 2016 Or get + set combined: $data = $cache->get('my-key', WireCache::expireHourly, function() { return []; // return array (for example), if returns absolute false, won't get cached. }); Link to comment Share on other sites More sharing options...
LostKobrakai Posted May 20, 2016 Author Share Posted May 20, 2016 I don't like the one line syntax, because I'd need to explicitly 'use' all variables declared somewhere else in the template, so they are available in the anonymous function. And secondly it's not possible to add the things I added above without recreating the whole get() function in a hook. Link to comment Share on other sites More sharing options...
Bill C Posted May 20, 2016 Share Posted May 20, 2016 @LostKobrakai, Thank you for taking the time to point this out. I have not yet done any coding involving Wire Cache, I've just started playing around with Hanna codes , but I am definitely going to be saving this link for future reference. Thanks again. Link to comment Share on other sites More sharing options...
MoritzLost Posted February 15, 2019 Share Posted February 15, 2019 Thanks for this @LostKobrakai! I just came across this very problem, thankfully I found this thread. Took me a while to understand how your solution works, certainly a creative solution to just add a string to make the cached value invalid JSON ? I modified your code slightly: const RAW_PREFIX = '::RAW::'; $wire->addHook('WireCache::saveRaw', function (HookEvent $event) { $args = $event->arguments(); $args[1] = RAW_PREFIX . $args[1]; return $event->return = call_user_func_array([$event->object, 'save'], $args); }); $wire->addHook('WireCache::getRaw', function (HookEvent $event) { $args = $event->arguments(); $cached_val = call_user_func_array([$event->object, 'get'], $args); return $event->return = $cached_val === null ? null : substr($cached_val, strlen(RAW_PREFIX)); }); I made two notable changes: If the Cache API returns null (i.e. no cached value exists), getRaw will also return null instead of an empty string. Use substr instead of str_replace, since it will be faster for long strings and it won't break anything in case the RAW_PREFIX (::HOOK::) appears anywhere inside the cached value (I'm a bit paranoid ? ) 1 Link to comment Share on other sites More sharing options...
baronmunchowsen Posted March 10, 2020 Share Posted March 10, 2020 @MoritzLost Do you have an example of using your getRaw hook with a function as a fallback (see last example: https://processwire.com/api/ref/wire-cache/get/) In this scenario, the returned value of the function is set as the cache value, but this falls back to the $cache->save where, ideally, it would use the saveRaw. Would you recommend not using the function fallback and instead using getRaw and saveRaw (based on the value returned from getRaw). Thanks! Link to comment Share on other sites More sharing options...
MoritzLost Posted March 11, 2020 Share Posted March 11, 2020 @baronmunchowsen Hm, that is true. I don't often use the callback argument, so I haven't encountered that problem yet. The hooked method passes on the argument, but if it's used the WireCache class will still use the normal save() method to save the return value, as you said. I suppose you could replicate the functionality of the protected renderCacheValue method inside the hook method, and execute the fallback function manually instead of passing it on to $cache->get. But for my taste that would be too much code duplication. So yes, I'd say it's best to not use the fallback function with the saveRaw method. Link to comment Share on other sites More sharing options...
baronmunchowsen Posted March 11, 2020 Share Posted March 11, 2020 Thanks @MoritzLost I implemented similar to as follows, which itself could be abstracted to a function or method: $cache_key = '...some cache key...'; $response = $response = $this->cache->getRaw($cache_key, 120); if(!$response) { $response = '{...some json response...}'; $this->cache->saveRaw($cache_key, $response, 120); } return $response; Link to comment Share on other sites More sharing options...
Recommended Posts
Create an account or sign in to comment
You need to be a member in order to leave a comment
Create an account
Sign up for a new account in our community. It's easy!
Register a new accountSign in
Already have an account? Sign in here.
Sign In Now