marco Posted October 2, 2013 Posted October 2, 2013 Hi folks, I recently released the module Template Twig Replace that enables templating using the Twig engine instead of the default php/html templates. The module hooks into PageRender::___renderPage, replacing the original method and setting the Twig rendering results to the event's return property. I've taken a good amount of information from this post to achieve the results. Now I've discovered that pages will not be cached if the module is active. Some digging into the core (/wire/modules/PageRender.module) showed, that the method, I'm hooking not only renders a page but handles page caching (and some form of access control) as well. As a quick fix I duplicated the code handling cache files in my module to ensure pages a properly cached as usual. The fix is already commited to github and should be available in the module directory soon. This is would my module currently looks like: public function init() { // ... some more stuff .. // replace default page rendering by hooked method $this->addHookBefore('PageRender::renderPage', $this, 'renderPageWithTwig'); } /** * Hook callback for PageRender::renderPage * * Replaces default page rendering entirely. * * @param HookEvent $event The hook event * @throws WirePermissionException Page is not currently viewable. */ public function renderPageWithTwig(HookEvent $event) { $parentEvent = $event->arguments(0); // grab event provided to PageRender::renderPage // don't mess with admin templates $page = $parentEvent->object; if ($page->template == 'admin') return; // double check page's status // taken from PageRender::__render() if ($page->status >= Page::statusUnpublished && !$page->viewable()) { throw new WirePermissionException('Page \'' . $page->url . '\' is not currently viewable.'); } // forced replacing of default page rendering behaviour $event->replace = true; // look for cached data // taken from PageRender::__render() $options = count($parentEvent->arguments) ? $parentEvent->arguments[0] : array(); $defaultOptions = array( 'forceBuildCache' => false, ); $options = array_merge($defaultOptions, $options); $cacheAllowed = wire('modules')->get('PageRender')->isCacheAllowed($page); $cacheFile = null; if ($cacheAllowed) { $cacheFile = wire('modules')->get('PageRender')->getCacheFile($page); if(!$options['forceBuildCache'] && ($data = $cacheFile->get()) !== false) { $parentEvent->return = $data; return; } } // allow page fields to be accessed directly in Twig // e.g. {{ page.myfield }} instead of {{ page.get('myfield') }} Page::$issetHas = true; // render template $twigVars = $this->collectVariables($page->output); $output = $this->getTwig()->render($page->template->name . '.' . wire('config')->templateExtension, $twigVars); // cache output if possible // taken from PageRender::__render() if (!empty($output) && $cacheAllowed && !is_null($cacheFile)) $cacheFile->save($output); // manually set return of original event $parentEvent->return = $output; } But I think, the best option would be finding a hook the only renders a page's content and replacing this method. Is there a better hook to use that leaves handling page caching and maybe other pre- or post-processing alone and does template rendering only? Regards, Marco 1
Soma Posted October 2, 2013 Posted October 2, 2013 I'm not seeing through all this but have you seen this http://processwire.com/talk/topic/1421-twig/ ? Maybe something in there helpful? I'm not sure there's another method like you have now, but then I don't care about template engine anyway On a side note: "Instead of contaminating your template's and chunk's markup with php code, you would have the Twig templating syntax at hand." Should read: "Instead of using powerful ProcessWire template syntax code in your template's and chunk's markup, you would "contaminate" your templates with the Twig templating syntax." Sorry but just had to 3
ryan Posted October 12, 2013 Posted October 12, 2013 Marco, I think the hook you might be looking for would be TemplateFile::render. The only issue is that this is used by many things, like rendering of partials and such. So you'd probably want to hook into just the instance used by Page::render. Not tested, but I think you could do this by checking for the presence of the $options array (and better yet a property we know would be there--I'll use $options['pageStack']), which would indicate it's the instance you want: public function init() { $this->addHookAfter('TemplateFile::render', $this, 'hookRender'); } public function hookRender(HookEvent $event) { $templateFile = $event->object; $options = $templateFile->options; // checking for $options, which we know is given to page renders // this is to make sure it's not some other use of TemplateFile if(is_array($options) && array_key_exists('pageStack', $options)) { // this is a page render, go ahead and process the output $output = $event->return; $page = $templateFile->page; // I'm assuming you want this $output = yourTwigStuff($output $page); // replace with your Twig code $event->return = $output; } }
tpr Posted February 13, 2016 Posted February 13, 2016 Is there any similar to check whether it is a wireRenderFile() call? $options may not available in that case, and I'm not sure checking for "$templateFile->filename" is suffice.
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