Jump to content

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


Recommended Posts

Posted

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!

Posted

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
Posted

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

Posted

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((

Posted

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");

 

Posted

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.

Posted

@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
Posted

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
  • 2 weeks later...

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...