Europe/London
Nov 2025
Career
2 min read

Drupal Webform module -- advanced handler patterns you should know

Custom WebformHandlerBase implementations for complex grant applications -- conditional logic, file attachments, and multi-recipient email routing.

The Drupal Webform module is extraordinarily powerful. The built-in email handler covers 80% of use cases — but when you need conditional logic, file attachments, multi-recipient routing, or integration with external systems, you need a custom handler. Here are the patterns I keep reaching for.

The handler base class

All custom handlers extend WebformHandlerBase and override the relevant hook methods. The most useful are postSave (after submission is saved), preSave (for validation and data manipulation), and confirmForm (for post-submission redirects).

/**
 * @WebformHandler(
 *   id = "grant_application",
 *   label = @Translation("Grant Application Handler"),
 *   category = @Translation("Custom"),
 *   description = @Translation("Handles grant application submissions."),
 *   cardinality = \Drupal\webform\Plugin\WebformHandlerInterface::CARDINALITY_SINGLE,
 *   results = \Drupal\webform\Plugin\WebformHandlerInterface::RESULTS_PROCESSED,
 *   submission = \Drupal\webform\Plugin\WebformHandlerInterface::SUBMISSION_REQUIRED,
 * )
 */
class GrantApplicationHandler extends WebformHandlerBase {
  public function postSave(
    WebformSubmissionInterface $submission,
    bool $update = TRUE
  ): void {
    if ($update) return; // Only fire on initial submission
    $this->processNewApplication($submission);
  }
}

Conditional email routing

The Fish Fund application routes emails to different reviewers depending on the applicant's country. Rather than hardcoding this logic, we store the routing table in Drupal config and load it dynamically in the handler.

Use webform tokens for dynamic values. The webform token system gives you access to any submission value in email subjects and bodies. Combine this with conditional handler configuration and you can handle complex routing without any PHP for the simple cases.

File attachment handling

Webform managed file fields save files to Drupal's file system with a temporary status. In your handler's postSave, call $file->setPermanent() and $file->save() on every attached file — otherwise they get deleted by Drupal's file cleanup cron.

private function makePermanent(WebformSubmissionInterface $submission): void {
  $data = $submission->getData();
  foreach (['supporting_doc', 'budget_spreadsheet'] as $field) {
    if (!empty($data[$field])) {
      $file = File::load($data[$field]);
      if ($file) {
        $file->setPermanent();
        $file->save();
      }
    }
  }
}