gebeer Posted April 24, 2015 Share Posted April 24, 2015 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 More sharing options...
LostKobrakai Posted April 24, 2015 Share Posted April 24, 2015 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 More sharing options...
gebeer Posted April 24, 2015 Author Share Posted April 24, 2015 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 More sharing options...
LostKobrakai Posted April 24, 2015 Share Posted April 24, 2015 Ok, in this case as you're module is already running when you attach the hook it should be working without making it autoload. In this case I also don't see, why the hook won't work. Link to comment Share on other sites More sharing options...
gebeer Posted April 24, 2015 Author Share Posted April 24, 2015 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 More sharing options...
LostKobrakai Posted April 24, 2015 Share Posted April 24, 2015 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. 1 Link to comment Share on other sites More sharing options...
gebeer Posted April 24, 2015 Author Share Posted April 24, 2015 Great idea Will give this a shot. Link to comment Share on other sites More sharing options...
kongondo Posted April 24, 2015 Share Posted April 24, 2015 This confirms for me that modules need to be autoload in order to place hooks in them, like the wiki suggests.. I'm not sure about this. In Menu Builder I have two hooks and they work fine but Menu Builder is not an Autoload module... 1 Link to comment Share on other sites More sharing options...
Wanze Posted April 24, 2015 Share Posted April 24, 2015 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 3 Link to comment Share on other sites More sharing options...
gebeer Posted April 25, 2015 Author Share Posted April 25, 2015 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 More sharing options...
gebeer Posted April 25, 2015 Author Share Posted April 25, 2015 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 More sharing options...
Soma Posted April 25, 2015 Share Posted April 25, 2015 I never use config->scripts in my templates. I use two custom configs to not get in conflict with core ones. What if I don't want that script parsed in before body? Link to comment Share on other sites More sharing options...
Wanze Posted April 25, 2015 Share Posted April 25, 2015 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 More sharing options...
gebeer Posted April 25, 2015 Author Share Posted April 25, 2015 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. 1 Link to comment Share on other sites More sharing options...
Gadgetto Posted March 17, 2020 Share Posted March 17, 2020 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. 1 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