Jump to content

use hook in non autoload module


gebeer
 Share

Recommended Posts

Hello,

I have a hook in a custom method of a non autoload module and it is not calling my hook method.

In the wiki I read 

Modules that are intended to attach hooks in the application typically should be autoload because they listen in to classes rather than have classes call upon them. If they weren't autoload, then they might never get to attach their hooks.

Does this mean that hooks are only working in autoload modules?

I added 'singular' => true and  'autoload' => true to getModuleInfo() but still my hook funktion does not get called.

My hook call at the end of this render method

$this->inlineScript = $inlineScript;

$page->addHookAfter('render', $this, 'addInlineScript'); 

$page object is available. Also tried $this->addHookAfter('Page::render', $this, 'addInlineScript');

and my hook method

	public function addInlineScript($event) {
		exit("gbr");
		// $value contains the full rendered markup of a $page 
		$value  = $event->return; 

		$value = str_replace("</body>", $this->inlineScript . "</body>", $value);
		// set the modified value back to the return value 
		$event->return = $value; 
	}
 

Why is this not working? Any help would be much appreciated.

Link to comment
Share on other sites

To be able to hook a function if it's called your module must be present, therefore it needs to be autoloaded. The hooked module does't. 

Just to be sure, did you add the necessary "___" before the hooked function? Without those the function is not hookable. Also is it intended, that your function exit()s before anything gets done?

Link to comment
Share on other sites

Thank ou for the quick reply.

Maybe there is some misunderstanding here.

I don't want to make a function ___hookable that is part of my module. I want to add an after hook to Page::render where I call my method 'addInlineScript':

$page->addHookAfter('render', $this, 'addInlineScript');

This hook call I placed inside the render() method of my module.

In the meantime I found out that the hook gets called when I place it in init() method of my module.

But I don't want to call it from init() because the 'addInlineScript' method depends on some property that is created in render(). So I need to call the hook from there.

The exit() is just there to confirm that my method gets called.

I don't want the module to be autoload. I just understood from the wiki that it needs to be autoload in order to call a hook from within it. But I guess I got this wrong.

So I made the module none autoload again but it is still being loaded. Has this to do with module cache and how can I make it not autoload again? 

Link to comment
Share on other sites

I reinstalled the module to make PW know that it is not autoload anymore.

Now $this->addHookAfter('Page::render', $this, 'addInlineScript'); is not executed anymore. Doesn't matter whether I call it from init() or render()

This confirms for me that modules need to be autoload in order to place hooks in them, like the wiki suggests.

I guess I need to find another way of implementing my logic...

But thanks again for your help.

Link to comment
Share on other sites

Make a second module, which is autoload, let it install by the fieldtype module. Add the hook to Page::render and check the current page for a map field. If so, call the module which generates your markup and inject it. The markup generation only needs to be able to run independent of other functions, but rather just from the field context the page object provides to the autoload module.

  • Like 1
Link to comment
Share on other sites

In the meantime I found out that the hook gets called when I place it in init() method of my module.

But I don't want to call it from init() because the 'addInlineScript' method depends on some property that is created in render(). So I need to call the hook from there.

I think you're misunderstanding registering/executing hooks. When you register a hook, you do not call anything. Basically you tell ProcessWire that you want to execute custom logic whenever the hooked method is executed. In your case, your method "addInlineScript" is called after Pw rendered your page (after executing $page->render()). Autoloading just makes sure that your hooks are properly setup, before hooked methods are executed. You don't need autoloading, but then you must be sure that "registering" the hook happens before Pw executes the hooked method.

Sorry if the above does not make any sense, it's not easy to explain this stuff as non-native english speaker/writer :)

  • Like 3
Link to comment
Share on other sites

Thank you all for the insights on how hooks are working. This really helps me to better understand what is going on.

@Wanze

but then you must be sure that "registering" the hook happens before Pw executes the hooked method.

How would I approach that? My guess is that it needs to happen during init() of my module, am I right?

Link to comment
Share on other sites

Solved this way:

I made an additional autoload module 'MarkupAddInlineScript' to execute the hook.

In the render() method of the map markup module I assign my inline script string to the page object

$this->page->inlineScript = $inlineScript; 

Now in init() of MarkupAddInlineScript I execute the hook

$this->addHookAfter('Page::render', $this, 'addInlineScript'); 

that calls my hook method

	public function addInlineScript($event) {

		$page = $event->object;

		if ($page->inlineScript) {

			$event->return = str_replace("</body>", $page->inlineScript . "</body>", $event->return);

		}

	}
 

My hook method only executes when $page->inlineScript returns a value other then null. This will happen only on pages that instantiate a map object with $map = $modules->get('MarkupLeafletMap'); 

As a result I have the script that was previously insertet inline on the page, neatly attached to the end of my page body

<script type="text/javascript">...</script>
</body> 

All other scripts that are required for rendering the map are also attached at the end of the body before the "inline script" through $config->scripts->add() in the init() method of the 'MarkupLeafletMap' module.

Now when people install this module, they don't have to worry about including the various scripts themselves in the head or body and I can be sure that they get executed in the right order, too. 

Only thing users of the module need to do now is include 

<?php foreach($config->scripts as $url) echo "<script src='$url'></script>"; ?> 

before </body>

Link to comment
Share on other sites

How would I approach that? My guess is that it needs to happen during init() of my module, am I right?

The init() method is a good place because ProcessWire calls this method after the module is loaded and before "processing" the request (if autoload). Thus your are sure that the hook is registered before the hooked method is called.

Now in init() of MarkupAddInlineScript I execute the hook

$this->addHookAfter('Page::render', $this, 'addInlineScript'); 

that calls my hook method

In this case you are registering a hook, not executing it. You're telling ProcessWire that in any point of time, when the render() method on a page object is called, it should afterwards execute your "addInlineScript" method.

Link to comment
Share on other sites

I knew that this would cause conflict because there are so many different ways of implementing script loading. So your comments on this are more then welcome.

I read and revived this discussion about how to best approach script inclusion when developing a module. And chose a mixed approach because I'm really not sure myself how to best do it and to experiment with different methods.

The original approach of the Google Maps module just didn't seem perfect since it forces users to include stuff in the head. So what to do as a user if you don't want to have scripts in <head>. But then I guess there is no perfect way.

Main reason for injecting the "inline script" at the very end: wanted to make sure that scripts get loaded in the right order.

@Soma

why wouldn't you want to have it before </body>?


@Wanze

thanks for clarifying the terminology. I'm quite new to PW module development. So I really appreciate any input that I can learn from.

  • Like 1
Link to comment
Share on other sites

  • 4 years later...
On 4/24/2015 at 5:12 PM, Wanze said:

I think you're misunderstanding registering/executing hooks. When you register a hook, you do not call anything. Basically you tell ProcessWire that you want to execute custom logic whenever the hooked method is executed. In your case, your method "addInlineScript" is called after Pw rendered your page (after executing $page->render()). Autoloading just makes sure that your hooks are properly setup, before hooked methods are executed. You don't need autoloading, but then you must be sure that "registering" the hook happens before Pw executes the hooked method.

Just stumbled across this older post and I want to say: IMHO this is the simplest/best explanation for registering / executing hooks! Thanks!

On 4/24/2015 at 5:12 PM, Wanze said:

Sorry if the above does not make any sense, it's not easy to explain this stuff as non-native english speaker/writer

This is also a problem for me when I try to post more complex questions in the forum. I'm often not understood.

  • Like 1
Link to comment
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
 Share

×
×
  • Create New...