Jump to content

Stop using Hooks!


bernhard
 Share

Recommended Posts

Ok, sorry, clickbait ? Hooks are great! But sometimes, there are even better solutions:

I'm cleaning up RockForms to finally release it ? I have some pages that are only for storing data (like form entries and such), so I don't want them to be editable, not even for superusers, as I control them solely via code in my module.

-- Solution 1 --

With a regular hook that would look like this:

<?php
// site/ready.php
$wire->addHookAfter("Page::editable", function($event) {
  $page = $event->object;
  if($page instanceof \RockForms\Root) $event->return = false;
});

That's quite nice, but this approach has some drawbacks: First, sooner or later you might end up with hook-hell in ready.php; That's not ideal and really hard to debug on more complex projects. Second, as we are defining the hook with a callback in a non-OOP style these hooks get a LOT harder to debug!

Have a look at tracy's debug panel:

0NwM4qx.png

The second highlighted hook is the one coming from ready.php and it does not show any helpful information whereas the first one does show clearly that the hook is attached in RockForms\Root in the method "hookUneditEntries" (it should be hookUneditRoot, but I made a mistake when copy-pasting, sorry ? ).

-- Solution 2 --

So the next best solution IMHO is using custom page classes! Then you get OOP style and a lot better structure for your project with really very little effort! Just create a file in /site/classes and that's it.

Now to attach hooks directly in custom page classes you have to do one additional step. You can watch my video about this if you are interested. If not, head over to solution number 3 which is even simpler ? 

This solution might look something like this:

<?php

namespace RockForms;

use ProcessWire\HookEvent;
use ProcessWire\Page;
use RockMigrations\MagicPage;

use function ProcessWire\wire;

class Root extends Page
{
  use MagicPage;

  public function init()
  {
    wire()->addHookAfter("Page::editable", $this, "hookUneditRoot");
  }

  protected function hookUneditRoot(HookEvent $event): void
  {
    $page = $event->object;
    if (!$page instanceof self) return;
    $event->return = false;
  }
}

This might look like a lot more code, but it's a lot better in the long run in my opinion as things that are related solely to the root page are inside the Root.php file of my module/project.

-- Solution 3 --

But then I remembered: As our "Root"-page is a custom page class and PW checks if the page is editable or not by calling $page->editable() we can simply override this method like so:

<?php

namespace RockForms;

use ProcessWire\Page;

class Root extends Page
{ 
  public function editable() {
    return false;
  }
}

You don't even need to make it a "MagicPage" because you don't need an init() method to attach any hooks.

Now it's only very little additional code compared to a hook in ready.php but with a lot cleaner setup ?

It's not a new invention, but I thought I'd share it nevertheless. Maybe it's helpful for some and maybe it's a good reminder for others, that even hooks are sometimes "overkill" ?  

  • Like 9
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

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...