Jump to content
Ivan Gretsky

[SOLVED] Template prepend file runs twice when in a Page::render hook

Recommended Posts

Good day!

I am trying to do a frontend check in a Page::render hook and return completely different markup on condition:
 

<?php 

// This is site/ready.php

wire()->addHookAfter('Page::render', function (HookEvent $event) {

	$page = $event->object;

    ... //Some condition here
		
	$config = wire("config");
	$t = new ProcessWire\TemplateFile($config->paths->templates . "test.php");
	$t->setArray(array(
			'page' => wire()->pages->get("1234"),
		)
	);
	$t->setPrependFilename($config->paths->templates . '_init.php');
	$t->setAppendFilename($config->paths->templates . '_main.php');
  	$out = $t->render();
	$event->return = $out;
}

But I get this error: Cannot redeclare SomeFubction() (previously declared in ...\site\templates\_func.php:123)

_func.php is included with include_once in _init.php. But somehow _init.php happens to run twice.

Can't get my head around this one. Please help!

Share this post


Link to post
Share on other sites

Maybe this does not work (I'm on mobile), but have you tried to set a var in _init.php like this:

if(isset($alreadyExecuted) && $alreadyExecuted) return;
$alreadyExecuted = true; // set it in the first run.

EDIT: Or bind the include of _functions.php on the condition of the var.

  • Like 1

Share this post


Link to post
Share on other sites

I did put this in _init.php:

<?php 

namespace ProcessWire;

bd("inside init");
bd($alreadyExecuted, '$alreadyExecuted');
if(isset($alreadyExecuted) && $alreadyExecuted) return;
$alreadyExecuted = true; // set it in the first run.
bd("inside init 2");

And here's what I get:
741099019_.png.8d4280b549c9669def2defbf3039ba42.png

Share this post


Link to post
Share on other sites

Ok, maybe scoping is what is in the way. Try setting as $GLOBALS["alreadyExecuted"].

  • Like 1

Share this post


Link to post
Share on other sites

Changing the condition to use a super global variable indeed helped me to prevent _init.php from running once again. But in this case the TemplateFile object doesn't get any variables from _init.php which it need to render correctly.

I thought, that I need to move all the code to another hook before the Page:render. So I made PageRender::beforeRenderPage hookable and moved the code there. But that still didn't work. I am definitely lost here((

Share this post


Link to post
Share on other sites

Let all your code you need to run twice in the root of _init.php.

Wrap all other, like the include_once of the _functions.php into a superglobal var condition, or, if the include of the _functions.php is the only issue you have, you can wrap this in a condition with function_exists:

if(!function_exists("aFunctionNameThatExistsInFunctionsPhp")) include(__DIR__ . "/_functions.php");

 

Share this post


Link to post
Share on other sites

Those functions are needed in _main.php to render correctly. I think there must be some better solution. If I run $page->render() in the same hook, it works fine. Though it obviously also load both _init.php and _main.php on the way.

Share this post


Link to post
Share on other sites

@Ivan Gretsky I am not sure that this will help, but for one project I been using these hooks for a similar task. Maybe it will give you some ideas.

public function init()
	{
		$this->addHookBefore('PageRender::renderPage', $this, 'hookBeforePageRenderTemplater');
		$this->addHookAfter('TemplateFile::render', $this, 'hookAfterTemplateRender');
		$this->wire('config')->ignoreTemplateFileRegex = "/(\.before\.)|(\.after\.)|(\.{$this->template_files_suffix}\.)|(^_)/";

	}


public function hookBeforePageRenderTemplater($e)
	{
		$event = $e->arguments(0);
		$page  = $event->object;
		if ($page->template->name == 'admin') return;
		$options                 = $event->arguments(0);
		$template                = $page->template;
		$options['prependFiles'] = [
			"{$template}.before.php",
			"_init.php",
		];
		$options['appendFiles']  = [
			"{$template}.after.php",
			"_after.php",
		];

		$event->setArgument(0, $options);
	}


public function hookAfterTemplateRender($event)
	{
		if ($this->wire('page') == null) return;
		if (strpos($event->object->filename, 'TracyDebugger') !== false) return;
		if (str_replace('site/assets/cache/FileCompiler/', '', $event->object->filename) !== $this->wire('page')->template->filename) return;
		if (strpos($_SERVER['REQUEST_URI'], $this->wire('config')->urls->admin) === 0 || $this->wire('page')->template->name == 'admin') return;
		$template = $event->object;
		$options  = $template->options;


		if (is_array($options) && array_key_exists('pageStack', $options)) {
			$view   = $this->wire($this->api_view_var);
			$layout = $this->wire($this->api_layout_var);
			$page   = $this->wire('page');

			$page_template = $page->template;

			if ($template->halt) {
				return;
			};

			if (empty($options['pageStack']) && $page_template->templater_url_segments) $this->wire('session')->redirect($page->url);
			$template_name = ($page->template->altFilename) ? $page->template->altFilename : $page->template->name;
			$view_file     = $this->getViewFilePath($template_name);

			if (is_file($view_file)) {
				$view->setFilename($view_file);
			} else {
				throw new WireException("View file for this page template ({$view_file}) does not exist");
			}
			$event->return = $event->return . $layout->render();
		}
	}

 

  • Like 2

Share this post


Link to post
Share on other sites

I don't remember why there are all these checks in hookAfterTemplateRender, but in such way it could identify a template that was used for rendering of the current page

  • Like 2

Share this post


Link to post
Share on other sites

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 account

Sign in

Already have an account? Sign in here.

Sign In Now

  • Recently Browsing   0 members

    No registered users viewing this page.

×
×
  • Create New...