Jump to content
Ivan Gretsky

Events and listeners in Processwire

Recommended Posts

Good day!

Is there an easy way to add events that some listeners can subscribe to in ProcessWire? I am thinking about adding these events in the template context, so I can add some listeners later. Preferably I'd like to be able to pass some variables to the listener. I am guessing it could be done calling some hookable method of a specially-made class. But please share anything more specific on that if you got some experience, colleagues.

Share this post


Link to post
Share on other sites

The more I read this the more it sounds like what you are after are custom hooks? 

Share this post


Link to post
Share on other sites

A while ago I added an event listener / event queue feature for Wireframe objects: https://github.com/wireframe-framework/Wireframe/blob/master/lib/EventListenerTrait.php. The rough idea is that the object itself keeps track of events and listeners, and if an event is emitted ($this->emit('event-name', $args)), related listeners will be notified. This concept was loosely based on the Vue.js events system.

This might be overtly complex for your needs, though. Something as simple as $this->runHooks('event-name', $args) would likely work just fine (though it depends on your use case) 🙂

  • Like 3

Share this post


Link to post
Share on other sites
26 minutes ago, kongondo said:

Just to add to the discussion....how Laravel does it:

https://laravel.com/docs/7.x/events

This is where I started))

 

36 minutes ago, LostKobrakai said:

Generally I think event handling can quite easily happen outside of processwire with something like https://event.thephpleague.com/2.0/.

Thanks, I'll look into it. But I always try to first use a native solution if it exists) And PW always tried to be all-in-one thing. I am actually thinking about building a module on this feature, so do not want to have an external dependency.

 

43 minutes ago, teppo said:

A while ago I added an event listener / event queue feature for Wireframe objects: https://github.com/wireframe-framework/Wireframe/blob/master/lib/EventListenerTrait.php. The rough idea is that the object itself keeps track of events and listeners, and if an event is emitted ($this->emit('event-name', $args)), related listeners will be notified. This concept was loosely based on the Vue.js events system.

This might be overtly complex for your needs, though. Something as simple as $this->runHooks('event-name', $args) would likely work just fine (though it depends on your use case) 🙂

The 1st part I didn't understand) But the second looks promising. Could you share an example of this in a template file context?

  • Like 1

Share this post


Link to post
Share on other sites
On 8/9/2020 at 3:37 PM, Ivan Gretsky said:

The 1st part I didn't understand) But the second looks promising. Could you share an example of this in a template file context?

Do you mean the part about "$this->runHooks('event-name', $args)"? 🙂

If so, what I meant is that you could register your "listener" by hooking into some non-existing method in the TemplateFile object (or class), and then "emit an event" by running hooks:

// somewhere early; prepend file or something:
$this->addHook('event-name', function(HookEvent $event) {
    echo "<!-- I'm " . $event->arguments['what'] . " -->";
});

// alternatively you could add the hook in module or init/ready.php:
wire()->addHook('TemplateFile::event-name', function(HookEvent $event) {
    echo "<!-- I'm still " . $event->arguments['what'] . " ->";
});

// then "emit an event" in the template file:
$this->runHooks('event-name', ['what' => 'listening']);

It's not quite the same thing, there's no queue, syntax is  a bit crude... but as I said before, this might be enough, depending on your actual use case 🙂

  • Like 4

Share this post


Link to post
Share on other sites

@teppo, a bit off-topic but I'm curious about the use of runHooks() because I haven't seen this used before. If you are adding a custom hook method "myEvent", what's the reason to do...

$this->runHooks('myEvent', ['what' => 'listening']);

...instead of...

$this->myEvent('listening');

 

  • Like 1

Share this post


Link to post
Share on other sites

@Robin S, that works (in some cases) as well, but there are two reasons why I mentioned runHooks:

  • Direct method call will cause an exception if myEvent hasn't been added as a hook method, runHooks won't. In this sort of scenario (events and listeners) the one emitting the event can't reliably know that someone is indeed listening, so this makes more sense.
  • Direct method calls require that the name be a valid method name, runHooks doesn't. Notice how I used 'event-name' in my example? 🙂

First one is really the key reason, second one is just a little quirk that one might find useful, i.e. it's easier to make sure that your event name can't accidentally clash with a real method.

Note: runHooks is tagged with #pw-internal. I've used this before in my code because I really needed it, but it's officially not a part of the public API, and thus in case Ryan decides to alter the implementation at some point, there's a chance that code relying on this feature may need revisiting. Just saying. Using only "official API methods" one would have to either check hooks with getHooks before calling the method (directly or via __call()), or just call it and handle possible exceptions with try ... catch.

  • Like 5

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