FormBuilder Hooks

Hooks are an optional way for developers to modify the behavior of FormBuilder. This section include a guide on how to use hooks in FormBuilder, including many examples and a reference/list of available hooks and what they do.

FormBuilder hooks: how-to guide

In the FormBuilder support board, the most common questions are about solving highly custom needs with FormBuilder hooks. Several current and prospective users have asked for a guide and reference to using FormBuilder hooks, to see what kind of things are possible. Though the following is also a pretty good guide to using hooks whether in FormBuilder or elsewhere in ProcessWire.

Before we jump into hooks (a development task), I should say that FormBuilder is a tool that was created to simplify creation of forms by making it possible to do so without any development. And this remains the case today. For most, you can do everthing you need to in FormBuilder without touching code or learning about hooks. FormBuilder is a tool that even your clients (who might know nothing about web development) can easily create and publish forms in. However, I've also found that many in our audience really like solving highly custom needs with FormBuilder too... modifying the way things work, using FormBuilder to solve unique problems, and doing so with elegant solutions. And admittedly, when you get into it, it can be quite fun. This guide is for those that want to open the hood and customize the way that FormBuilder works from the development side.

Where to place hooks

Code for your FormBuilder hooks (and generally most custom ProcessWire hooks) is best put in your /site/ready.php file. Though if you only need your hook code to apply in the ProcessWire admin, then you might want to place it in /site/templates/admin.php instead. Or, if you only need your hook code to apply on the front-end of your site, then you might place it in /site/templates/_init.php. But when it doubt, use /site/ready.php, because that will work for just about any instance. FormBuilder also has a /site/templates/form-builder.inc file that used to be a common place for FormBuilder hooks. I say "used to be" because after the ready.php support was added to the core a few years back, that largely replaced the need for FormBuilder to have its own hooks file.

Hook examples

First, here's a simple example of how to hook a FormBuilder method. Most of the hooks in FormBuilder come from the FormBuilderProcessor class, which is what renders and processes form submissions. All of our examples will focus on that.

$forms->addHook('FormBuilderProcessor::methodName', function($e) {
  // hook code
}); 

The $forms variable is one provided by FormBuilder, and it represents the entire FormBuilder module. You can attach your hook with “addHook”, “addHookBefore” or “addHookAfter”, depending on whether you want to execute your code before or after the method you are hooking into. Hooking before enables you to manipulate arguments that get sent to the method, while hooking after enables you to examine or manipulate the value returned from the hooked method. For cases where it doesn't matter whether your code is executed before or after the method, you can just use “addHook” (without before or after), like in the example above.

Lets take a look at hooking before a method. Hooking before the renderReady method would enable you to perform some action or modification right before the form is rendered:

$forms->addHookBefore('FormBuilderProcessor::renderReady', function($e) {
  $processor = $e->object; // FormBuilderProcessor instance
  $form = $e->arguments(0); // retrieve argument by number (zero based)
  $form = $e->arguments('form'); // OR retrieve argument by name
  if($processor->formName == 'foo') {
    // do something for form named "foo"
  } else if($processor->formName == 'bar-baz') {
    // do something else for form named "bar-baz"
  }
}); 

As an example, you might use a renderReady method to pre-populate values for a form. In this case, if a user is logged in, we'll pre-populate their email address in the appropriate field named "email":

$forms->addHookBefore('FormBuilderProcessor::renderReady', function($e) {
  $form = $e->arguments(0);
  $user = $e->wire('user');
  // if not logged in, exit now
  if(!$user->isLoggedIn()) return;
  // see if there a field in the form named 'email'
  $inputfield = $form->getChildByName('email');
  // if there is an email field and it's empty, populate user's email
  if($inputfield && $inputfield->isEmpty()) {
    $inputfield->attr('value', $user->email);
  }
}); 

Hooking after a method is useful because it lets you examine or modify its return value. In this case, a "Hello World" paragraph would appear below every form rendered by FormBuilder:

$forms->addHookAfter('FormBuilderProcessor::render', function($e) {
  $str = $e->return;
  $str .= "<p>Hello World</p>";
  $e->return = $str;
}); 

Hooking before a method is useful because it lets you not just see, but also modify its arguments before the method you are hooking gets called:

$forms->addHookBefore('FormBuilderProcessor::saveEntry', function($e) {
  $data = $e->arguments(0);
  // set field "foo" to value "bar" before it gets saved as an entry
  $data['foo'] = 'bar';
  // populate the argument back so it reflects the new value
  $e->arguments(0, $data);
}); 

Here's another example: adding a file attachment to an auto-responder email. In this case, a user subscribes to a list and receives a bonus article PDF file in their confirmation email:

$forms->addHook('FormBuilderProcessor::emailFormResponderReady',
  function($e) {
    $form = $e->arguments(0); // or arguments('form')
    $mailer = $e->arguments(1); // or arguments('email')
    if($form->name == 'subscribe') {
      $mailer->addFileAttachment('/path/to/bonus-article.pdf');
    }
  }
); 

For this last example, lets refuse subscriptions from @hotmail.com since they've all been spam accounts lately:

$forms->addHook('FormBuilderProcessor::processInputDone', function($e) {
  $form = $e->arguments(0);
  if($form->name != 'subscribe') return; // we only want "subscribe" form
  $field = $form->getChildByName('email');
  $email = $field->attr('value');
  if(stripos($email, '@hotmail.com')) {
    $field->error('Please use a non-hotmail email address');
  }
});

When the user submits the form with a name@hotmail.com email address, the form will be rendered again with an error on the email field asking them to “Please use a non-hotmail email address.”

FormBuilder hooks reference

With the above examples aside, lets take a closer look at all the methods you can hook in the FormBuilderProcessor class. These methods enable you to hook into just about every aspect of runtime form building, rendering, processing, validation and whatever happens after a successful form submission (aka actions).

These hooks are in the format: returnType methodName($arg0, $arg1, $arg2, etc.). If there is no return value, then returnType will not be shown.

InputfieldForm populate(array $data, $entryID)
Populate $this->form with the [name=>value] data from the given associative array.

string render($id = 0)
Render the form output, or follow-up success message. If $id is populated, it is the id of existing form entry.

string renderReady(InputfieldForm $form, $formFile = '', $vars = array())
Called when ready to render, and returns rendered output. Note the $formFile and $vars arguments are only populated in embed method D.

bool processInput($id = 0)
Process input for submitted form. If $id is populated, it is the id of existing form entry.

processInputReady(InputfieldForm $form)
Called right before $form->processInput() is called.

processInputDone(InputfieldForm $form)
Called after $form->processInput() and spam filtering is completed.

int|bool saveForm(InputfieldForm $form, $id = 0)
Save the form to the database entry, page, or email(s) per form action settings. If $id is populated, it is the id of an existing entry being saved.

int saveEntry(array $data)
Save a form entry where $data is the given entry. Existing entry should have populated id property. Returns id of saved entry.

Page|null savePage(array $data, …)
Save given entry $data to a Page. There are more arguments ($status and $onlyFields) but they usually aren't present. See FormBuilder for additional arguments.

bool savePageCheckName(Page $page)
Hook called before $page->save() to validate that page name is allowed. Returns false if save should be aborted.

bool allowSavePageField(Page $page, $pageFieldName, …)
Hook called to determine if the given field info is allowed to be saved in Page? See FormBuilder for additional arguments.

savePageReady(Page $page, array $data)
Hook called right before Page is about to be saved.

array savePageDone(Page $page, array $data, $isNew, …)
Hook called after a page has been saved. Returns the entry $data that was saved. See FormBuilder for full arguments.

bool emailForm(InputfieldForm $form, array $data)
Called to email the form result to the administrator(s). Returns true on success, false on fail.

emailFormReady(InputfieldForm $form, FormBuilderEmail $email)
Called when $email object is ready, but message not yet sent.

bool emailFormResponder(InputfieldForm $form, array $data)
Called to send auto-responder email. Returns true on success, false on fail.

emailFormResponderReady(InputfieldForm $form, FormBuilderEmail $email)
Called when $email object ready, but message not yet sent. You might hook this to add a file attachment, for example.

bool postAction2(array $data)
Called to send $data to external 3rd party URL specified in the form settings. This is what gets used when you want to silently send data along to another 3rd party service like MailChimp, Salesforce, or the like. The next hook method (postAction2Ready) might actually be the more useful one though.

string postAction2Ready(WireHttp $http, array $data, $method, $url, …)
Called when ready to send to external URL. Returns response string or boolean false on fail. See FormBuilder for additional arguments.

void formSubmitSuccess(InputfieldForm $form)
Called when form has been successfully submitted and saved.

void formSubmitError(InputfieldForm $form, array $errors)
Called when there were errors that prevented successful submission of form.

string renderSuccess($message)
Called to render the given success message or process success action string (which might also instruct it to do a redirect).

string renderSuccessMessage($message, $markupTemplate = '')
Render succcess message string only (called by renderSuccess).

string renderSuccessRedirect($url)
Render or execute a redirect to given $url (called by renderSuccess).

string renderErrors()
Render error messages.

array renderErrorsReady(array $errors)
Called when errors ready to render, hooks can optionally modify $event->return array of errors.

string renderError($error, $errorTemplate = '')
Render single error message into markup.

string wrapOutput($out)
Wraps all FormBuilder output in a FormBuilder-specific <div>.

Please also see the README-HOOKS.txt file included with your copy of FormBuilder for the most up-to-date list of hooks available with the version of FormBuilder you are using.

Twitter updates

  • New post: An introductory look at ProcessWire LoginRegisterPro, a module for providing new user registration, secure logins, profile editing, and more— More
    13 December 2019
  • ProcessWire 3.0.146 on the dev branch contains about 22 commits with a combination of useful upgrades and issue report resolutions, more details in this forum post: More
    22 November 2019
  • ProcessWire 3.0.144 and 3.0.145 add improved field template context override settings and include a new Inputfields API, along with numerous other issue fixes, optimizations and improvements to the core. More
    8 November 2019

Latest news

  • ProcessWire Weekly #291
    In the 291st issue of ProcessWire Weekly we're going to take a closer look at the latest core updates, check out a brand new third party module called ProcessHelpVideos, and introduce a new site of the week. Read on!
    Weekly.pw / 8 December 2019
  • Login Register Pro
    Blog / 13 December 2019
  • Subscribe to weekly ProcessWire news

“Yesterday I sent the client a short documentation for their ProcessWire-powered website. Today all features already used with no questions. #cmsdoneright—Marc Hinse, Web designer/developer