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

  • ProcessWire 3.0.142 has a lot of updates but the biggest is the addition of custom fields support for file and image fields. In this post, we take a closer look at that, and also outline all of the new features in the just-released FormBuilder v40— More
    11 October 2019
  • This week’s dev branch version brings you improvements to ProcessWire’s input->cookie API variable, plus it adds the ability to modify system URLs and paths at runtime. This post also includes some examples to demonstrate just how useful this can be— More
    20 September 2019
  • New post: This week we’ll take a look at a new version of FormBuilder that's on the way (with a screencast), as well as the latest version of the core: ProcessWire 3.0.140— More
    6 September 2019

Latest news

  • ProcessWire Weekly #283
    The 283rd issue of ProcessWire Weekly introduces ProcessWire 3.0.142 and FormBuilder v40, highlights some conference content, and features a new weekly poll continuing with the #processwireworks theme. Read on!
    Weekly.pw / 12 October 2019
  • ProcessWire 3.0.142 core updates
    This latest version of the core on the dev branch has a lot of updates, bnd the biggest is the addition of custom fields support for file and image fields. In this post, we take a closer look and also outline all of the new features in the just-released FormBuilder v40.
    Blog / 11 October 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