Using Hooks in my Custom Panel Field

TL;DR: Can I register hook functions in a custom panel field, or do I have to use a plugin?


I wrote a (relatively) complex panel field to manage participants of events.

There are a few cases when I have to send out emails (money has been paid, forms have been handed in, …). The current code stays very close to the existing controller methods of the structure field:

public function moneyPaid($entryId) {
  $self      = $this;
  $model     = $this->model();
  $structure = $this->structure($model);
  $entry     = $structure->find($entryId);

  // snip [some safe guards]

  $form = $this->form('money', $model, function() use($self, $model, $structure, $entryId, $entry) {

    // snip [manipulate the entry]

    $structure->update($entryId, $entry);

    // send email here

    $self->redirect($model);
  });
  return $this->modal('money', compact('form'));
}

When the user confirms the form, the structure entry gets edited and saved. When this has happened, I need to send my email. Instead of pasting the same email code everywhere, I thought about using the hook system and trigger a custom event. This works perfectly fine.
The only problem is the registration of the listener: as far as I know, this has to be done in a plugin. However, I would like to keep it in the field definition, as it will be needed anywhere else. Is there a way this can be achieved?

I think you don’t need a hook but simply a public static method that can be called from your field methods. You can then call this method from anywhere and pass the email parameters to it.

Let me know if you need further information.

Of course that works; I guess I was just excited to use an event driven system. If there is no easy way to register the hooks, this exactly what I’ll end up using.

Sure you can use hooks:

kirby()->hook('myemailhook', function($arg1, $arg2) {...});
kirby()->trigger('myemailhook', array($arg1, $arg2));

But I think that events aren’t that useful here. You only have one function that sends emails, so it’s not a many-to-many relationship. But if you’d rather use hooks anyway or if I forgot something, go ahead. :slight_smile:

1 Like

And where would you put that code so that the event listener is actually defined before the event might be triggered? The only thing that I could come up with the constructor of my field controller, but that doesn’t seem to be the cleanest solution either.

The rationale behind going for events/hooks was that in general there will be more things happening than just sending emails. Imagine the event running out of places, spots freeing up, … They trigger different things obviously, and I like modeling such stuff with events when I can.

You can register the hooks outside of your class:

<?php

class MyField extends BaseField {
  ...
}

kirby()->hook('fabiansperrle.myfield.myhook', array('MyField', 'sendEmail'));

If you then trigger the hook, the static method MyField::sendEmail() will be called with the passed hook arguments.

Do you have any further information for me here to this solution @lukasbestle ? I want to create a select field (duplicated from the standard select field) which triggers whenever a certain value of the select field is saved.

As far as I can tell from your description, this is unrelated to my solution above. But you can do it like this:

<?php

class MySelectField extends SelectField {
  public function result() {
    $result = parent::result();
    
    if($result === 'some-value') {
      // your custom code
    }
    
    return $result;
  }
}

The result() method will be called on save, so you can hook your code into there.

I like the possibility to add custom hooks from a plugin so I’ve added yet another wiki page: https://github.com/jenstornell/kirby-secrets/wiki/Hooks