Can Posted July 31, 2015 Share Posted July 31, 2015 I highly recommend reading the second post, too before implementing anything, as it might simplify a lot, depending on your setup.. Because I just updated all MarkupCaches with newer WireCache, couple of weeks ago, and really like it, I thought why not share it. So I got _init.php as prependTemplateFile, and _out.php as appendTemplateFile. But let's check out the interesting part, for example an article.php template. but for some pages, for example blog, it makes sense to include all children ;-) You can include any page you like, or define a time or a template as expiration rule. Here my defaults from the _init.php $cacheNamespace = "hg"; $cacheTitle = "$page->template-" . $sanitizer->pageName($page->getLanguageValue($en, "title")) . "-$page->id-{$user->language->name}"; $cacheTitle .= $pageNum ? "-$pageNum": ''; $cacheExpire = $page; I'm not exactly sure if there is any benefit in using a namespace, you can omit the namespace part and if needed just prefix the cache title. Works both. You'll see why I added the namespace/prefix a little later ;-) For the title I'm getting, the template, english page title (you can of course use the language title and omit the language name part, but I liked it better to have the caches grouped.. After language name I'm adding the page number if present. If you need you can of course create a different, more or less specific cache title. Add get parameters or url segments for example. Then I have $cacheExpire already set to $page as default value, so I don't need to set it in every template So my markup (only the important parts) looks like this: //You can have anything you like or need uncached above this $cacheExpire = $page->chilren(); $cacheExpire->add($page); $cache->getFor($cacheNamespace, $cacheTitle, "id=$cacheExpire", function($pages, $page, $users, $user) use($headline) { // as you can see, within the function() brackets we can pass any Processwire variable we need within our cached output. // If you don't need any you can of course leave the brackets empty // and if you need any other variable wich you had to define outside this function you can pass them via use() // so here goes all your markup you want to have cached // for example huge lists, or whatever }); // Then I have some more uncached parts, a subscription form for example. // After this comes another cached part, which gets -pagination appended to the title. Otherwise it would override the previous one. // It's not only caching the pagination, I just needed a name for differentiation. $cache->getFor($cacheNamespace, $cacheTitle.'-pagination', "id=$cacheExpire", function($pages, $page, $users, $user) use($headline) { // so here comes more cached stuff }); After this your template could end or you can have more uncached and cached parts, just remember to append something to the cache title ;-) Now comes, at least for me, the fun part haha In my prepended _init.php template file I have the following code under the cache vars: if($user->isSuperuser() && $input->get->cache == 'del') { if($input->get->clearAllCaches == "true") { $allCaches = $cache->get("hg__*"); foreach($allCaches as $k => $v) $cache->delete($k); $session->alert .= "<div class='alert alert-success closable expire'>All (".count($allCaches).") caches have been deleted. <i class='fa fa-close'></i></div>"; } else { $currentPageCaches = $cache->get("hg__$page->template-" . $sanitizer->pageName($page->getLanguageValue($en, "title")) . "-$page->id*"); foreach($currentPageCaches as $k => $v) { $cache->delete($k); $session->alert .= "<div class='alert alert-success closable expire'>Cache: $k has been deleted. <i class='fa fa-close'></i></div>"; } } $session->redirect($page->url); } So when I append the parameter "?cache=del" to any URL all cache files within my namespace and beginning with the predefined $cacheTitle will be removed. Means all language variations and the "-pagination & -comments" caches will be deleted, too. This is the else part. But if I append "&clearAllCaches=true", to the first parameter, it will get all caches within my namespace and clear them. Without the namespace it would clear Processwires caches (like the module cache), too. I'm storing a little success message in a session called "alert" which is closable by the FontAwesome icon via jQuery and expires after some seconds, means it will remove itself, so I don't have to click ;-) Maybe it makes more sense to change the cache title and have the page->id first, so you could select all related caches with $cache->get("hg__{$page->id}*"); I liked them grouped by template in the database, but maybe I change my mind soon because of this For not having to type those params manually I have two buttons in my _out.php template file. I have a little, fixed to the left bottom corner admin menu with buttons for backend, edit (current page), and now clear cache button which unveils the clear all caches button on hover, so it's harder to click it by mistake. When someone writes a comment, I added similar lines as above, after saving the comment, to clear comment caches. Ah, the comment caches look like "-pagination" just with "-comments" appended instead. I don't know if there is an easy way to expire a cache when a new children (especially backend created) is created, other than building a little hook module. With MarkupCache it could be a pain to delete all those folders and files in /assets/ folder, especially with slow connection. The database driven WireCache makes it much faster, and easier when set up those few lines of code to make it for you. more about WireCache http://processwire.com/blog/posts/processwire-core-updates-2.5.28/#wirecache-upgrades https://github.com/ryancramerdesign/ProcessWire/blob/master/wire/core/WireCache.php Hope it helps someone and is okay for Tutorial section, if you have any questions, suggestions or ideas feel free to share. Hasta luego Can 11 Link to comment Share on other sites More sharing options...
Can Posted November 17, 2016 Author Share Posted November 17, 2016 (edited) a simple way of caching (nearly) everything using wirecache and 2 page render hooks in site/ready.php // the next ensures that the following code will only run on front end (otherwise back end would get cached, too which results in problems) // make sure to place anything you need in the backend before this line or change it to your needs.. if ((strpos($page->url, wire('config')->urls->admin) !== false) || ($page->id && $page->is('parent|has_parent=2'))) return; $cacheName = "prefix__$page->id-$page->template-{$user->language->name}"; if ($urlSegment1) $cacheName .= "-$urlSegment1"; if ($urlSegment2) $cacheName .= "-$urlSegment2"; if ($urlSegment3) $cacheName .= "-$urlSegment3"; if ($input->pageNum > 1) $cacheName .= "-page$input->pageNum"; // if already cached exit here printing cached content (only 1 db query) $wire->addHookBefore('Page::render', function() use($cache, $cacheName) { $cached = $cache->get($cacheName); if ($cached) { exit($cached); } }); // not cached so far, continue as usual but save generated content to cache $wire->addHookAfter('Page::render', function($event) use($cache, $cacheName) { $cached = $cache->get($cacheName); if (!$cached) { $cache->save($cacheName, $event->return); } unset($cached); }); of course not the same as a proper flat file cache like procache but at least saving many database queries make sure to adjust the $cacheName as needed, you can even cache query strings, almost everything you'd like and you could wrap everything in a condition to only run if no incoming post or query strings so form submits keep working example if (!count($input->get) && !count($input->post) && !$config->ajax) { // cache logic from above } have fun Edited December 24, 2016 by Can add backend cache prevention 8 Link to comment Share on other sites More sharing options...
szabesz Posted November 18, 2016 Share Posted November 18, 2016 Thanks for sharing @Can, looks pretty neat and dead simple! For small sites it should suffice. Link to comment Share on other sites More sharing options...
Can Posted November 18, 2016 Author Share Posted November 18, 2016 glad you like it @szabesz Right now I'm not using it productively but I'm planning to use it for a small site, too as it simplifies a lot because you can still fully use processwire/php dynamically 1 Link to comment Share on other sites More sharing options...
szabesz Posted November 18, 2016 Share Posted November 18, 2016 I have not yet used WireCache, but this is the kind of simplicity that I'm always looking for, that is why sooner or later I will try it out. Thanks! 1 Link to comment Share on other sites More sharing options...
adrianmak Posted December 22, 2016 Share Posted December 22, 2016 @Can how's the _out.php look like in order to work with wirecache? Link to comment Share on other sites More sharing options...
Can Posted December 22, 2016 Author Share Posted December 22, 2016 @adrianmak you mean the site/ready.php hook method mentioned in my second post? Then it really doesn't matter how you output your content, because site/ready.php will be invoked way before any page template. The second hook "addHookAfter(Page::render" runs after all you template files have been processed and the output is almost ready, at this point $event->return in this hook contains your whole markup and the hook puts all of it in one database row using wirecache. The first hook checks if there is a cached version of this page and outputs it using exit() which will just print the whole cached html markup and stops any further php, so you end up using only 1 database request for markup creation, if no cache file present it won't do anything, so everything will be output as normal (as if there were no caching at all) and then the second after hook will cache it... the second posts approach is much simpler to implement and cleaner than the first posts one ;-) Link to comment Share on other sites More sharing options...
adrianmak Posted December 23, 2016 Share Posted December 23, 2016 @Can I copy your ready.php code of second post in my site/ready.php Then I refresh several pages, but i didn't see any files output under site\assets\cache\ folder Link to comment Share on other sites More sharing options...
Can Posted December 23, 2016 Author Share Posted December 23, 2016 as mentioned above, wirecache uses database tables so you have to go to adminer or phpmyadmin (or similar) and check out the caches table of your page to see cached content, and you could change the page content before caching by e.g. adding a "cached" class to body or html tag sidenote: there is template cache and I think some predecessor to wirecache which use file based cache using the hooks you could implement your own caching logic/system, for example writing the page markup to a file using file_put_contents() and the before hook could check for the existence of the file and then file_get_contents() output it.. Link to comment Share on other sites More sharing options...
adrianmak Posted December 23, 2016 Share Posted December 23, 2016 @Can Your code save me a lot. But it is still not quite working on my problem My $cacheName is look like this "$config->prefix__$page->id-$page->template-{$user->language->name}"; When I checked back the cache table, the cache name has no such a prefix . As a result, mobile devices are still received the cache of desktop template. Is the $config variable is not vabailable in ready.php scope ? Link to comment Share on other sites More sharing options...
Can Posted December 23, 2016 Author Share Posted December 23, 2016 you define the cache name above/outside of the functions, like in my example right? It should actually work.. uh I'm quite sure you need to wrap $config->prefix in curly braces like so "{$config->prefix}__$page->id-$page->template-{$user->language->name}"; without them php thinks the two underscores are part of the variable but there is no variable called $config->prefix__ 1 Link to comment Share on other sites More sharing options...
adrianmak Posted December 23, 2016 Share Posted December 23, 2016 I overlooked the two underscores has such a meaning. Thanks for pointing it out. My problem has been solved. Link to comment Share on other sites More sharing options...
adrianmak Posted December 23, 2016 Share Posted December 23, 2016 Dont you think a single db query or a flat file cache, which one is faster? Link to comment Share on other sites More sharing options...
adrianmak Posted December 23, 2016 Share Posted December 23, 2016 I tried to append string <!--cached page--> before cache saved into db I altered the code into if (!$cached) { $cached .= "<!--cached page-->"; $cache->save($cacheName, $event->return); } However, checking with the cache table the string didn't append Link to comment Share on other sites More sharing options...
Can Posted December 23, 2016 Author Share Posted December 23, 2016 glad you solved it $cached is not being saved to cache, my code example might not be the most logical but it's short ;-) WireCache->save("cacheName", "data") so we directly save $event->return to cache and not $cached shortest way to achieve what you what would be if (!$cached) { $event->return .= "<!--cached page-->"; $cache->save($cacheName, $event->return); } or even if (!$cached) { $cache->save($cacheName, "$event->return<!--cached page-->"); } edit usually flat files are faster, this one is just using the easiest way as everything is already there with WireCache but as I said you can replace it quite easily with some flat file logic, but you would need to alter cache name too.. easier and more efficient would then be to use ProCache as it uses flat files and bypasses any php mysql serving just static files Link to comment Share on other sites More sharing options...
LostKobrakai Posted December 23, 2016 Share Posted December 23, 2016 2 hours ago, Can said: usually flat files are faster, this one is just using the easiest way as everything is already there with WireCache but as I said you can replace it quite easily with some flat file logic, but you would need to alter cache name too.. easier and more efficient would then be to use ProCache as it uses flat files and bypasses any php mysql serving just static files There's also the MarkupCache module, which is similar to WireCache, but file based. ProCache is indeed a nice solution, but like TemplateCache it's not going to solve @adrianmak's problem of caching different markup for mobile/desktop requests. 2 Link to comment Share on other sites More sharing options...
adrianmak Posted December 23, 2016 Share Posted December 23, 2016 I found that there is some minor issue with the code. All admins pages, even the pw's admin backend login page (cached), it is not possible to login to the backend. How could excluded all admin related pages not to cache? Updated: I solved by myself. Simply checking the template whether it is a admin template. If it is a admin template, dont read or write cache Link to comment Share on other sites More sharing options...
Can Posted December 24, 2016 Author Share Posted December 24, 2016 thanks for mentioning @LostKobrakai good to know right markupcache was the thing I was thinking about @adrianmak glad you solved it yourself, actually I forgot that I got my ready.php kind of split, first code blocks are intended to run on back and front end, like save hooks and stuff, then i got this code if ((strpos($page->url, wire('config')->urls->admin) !== false) || ($page->id && $page->is('parent|has_parent=2'))) return; i think i found this or something similar somewhere in the forums, am not entirely sure if the second part is really needed as the first part checking if url contains admin path (usually processwire) you could extend it by placing / before and after and I don't know if there are any possibilities of admin pages not containing admin url.. anyway, everything after this line will only run on front end.. 2 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