2019 Aug 14

React on a webform submission by using the form data inside a custom module that provides a new webform handler

A question often asked is how to use webform submission data to perform some task once the form has been submitted. This could range from creating a record via a third party API or touching some other part of Drupal given the submission data.

You're probably already familiar with handlers in webforms. The basic one most used is the email handler (which sends an email on form submission). The other common one is called "remote post", which essentially posts the webform data to another URL that you specify. This would be good for a custom script on another site, or maybe a local script not part of Drupal. It serves as a way to "forward" the data on to that URL for further processing.

The type we discuss here is similar to remote post, except all the processing logic resides in a custom module we'll create as part of the Drupal site.  

Creating a custom form handler

You’ll first need to create a new custom module, or have an existing module that you’d like to put the handler in to.

Here we'll outline what's needed for a standalone module.

You’ll need the following folder structure:

▾ modules/
  ▾ custom/
    ▾ custom_form_handler/
      ▾ src/
        ▾ Plugin/
          ▾ WebformHandler/

You'll then need to add the handler file in the WebformHandler directory. In this case it's called CreateNodeWebformHandler.php and has this content:

<?php

namespace Drupal\webform_create_event\Plugin\WebformHandler;

use Drupal\Core\Form\FormStateInterface;
use Drupal\node\Entity\Node;
use Drupal\media\Entity\Media;
use Drupal\file\Entity\File;
use Drupal\webform\Plugin\WebformHandlerBase;
use Drupal\webform\WebformSubmissionInterface;

/**
 * Create a new node entity from a webform submission.
 *
 * @WebformHandler(
 *   id = "Create a node",
 *   label = @Translation("Create a node"),
 *   category = @Translation("Entity Creation"),
 *   description = @Translation("Creates a new node from Webform Submissions."),
 *   cardinality = \Drupal\webform\Plugin\WebformHandlerInterface::CARDINALITY_UNLIMITED,
 *   results = \Drupal\webform\Plugin\WebformHandlerInterface::RESULTS_PROCESSED,
 *   submission = \Drupal\webform\Plugin\WebformHandlerInterface::SUBMISSION_REQUIRED,
 * )
 */

class CreateNodeWebformHandler extends WebformHandlerBase {

  /**
   * {@inheritdoc}
   */

  // Function to be fired after submitting the Webform.
  public function postSave(WebformSubmissionInterface $webform_submission, $update = TRUE) {
    // Get an array of the values from the submission.
    $values = $webform_submission->getData();

    $node_args = [
      'type' => 'event',
      'langcode' => 'en',
      'created' => time(),
      'changed' => time(),
      'uid' => 1,
      'moderation_state' => 'draft',
      'title' => $values['event_name'],
      'field_start_date' => $values['start_date'],
      'field_end_date' => $values['end_date'],
      'field_location' => $values['location'],
      'field_website' => $values['website'],
      'body' => [
        'value' => $values['event_description'],
        'format' => 'full_html'
      ]
    ];

    $node = Node::create($node_args);
    $node->save();

    $node_fr = $node->addTranslation('fr');
    $node_fr->title = $values['event_name'];
    $node_fr->body->value = $values['event_description'];
    $node_fr->body->format = 'full_html';
    $node_fr->field_start_date = $values['start_date'];
    $node_fr->field_end_date = $values['end_date'];
    $node_fr->field_location = $values['location'];
    $node_fr->field_website = $values['website'];
    $node_fr->save();
  }
}

The above example has code in it to create a new event node with a translation based on form data. In reality, you could put whatever you want in the postSave method. The last thing you'll need is the basic info file to complete the module (in this case it was called custom_form_handler.info.yml):

name: Webform Create Event
description: Provides a custom webform handler for the "submit an event" webform so it can create a new unpublished event type node
core: 8.x
package: Custom
type: module

Enabling the module and setting the handler

Once you enable the new custom module, you'll get the option of the new handler in the handlers list for each form. To get to the handlers section, go under the settings tab -> emails / handlers -> add handler for each form. You'll see your new handler in the list. In this example, it's called "Create a node".

view of webform handlers

Other available events

As a side note, you don't have to use the postSave method, it just happens to be the most useful since it fires one the webform data has been saved. These are the other ones available in case your timing needs to be different or if you're looking for webform submission deletion events:

  /**
   * {@inheritdoc}
   */
  public function preCreate(array $values) {
    $this->displayMessage(__FUNCTION__);
  }

  /**
   * {@inheritdoc}
   */
  public function postCreate(WebformSubmissionInterface $webform_submission) {
    $this->displayMessage(__FUNCTION__);
  }

  /**
   * {@inheritdoc}
   */
  public function postLoad(WebformSubmissionInterface $webform_submission) {
    $this->displayMessage(__FUNCTION__);
  }

  /**
   * {@inheritdoc}
   */
  public function preDelete(WebformSubmissionInterface $webform_submission) {
    $this->displayMessage(__FUNCTION__);
  }

  /**
   * {@inheritdoc}
   */
  public function postDelete(WebformSubmissionInterface $webform_submission) {
    $this->displayMessage(__FUNCTION__);
  }

  /**
   * {@inheritdoc}
   */
  public function preSave(WebformSubmissionInterface $webform_submission) {
    $this->displayMessage(__FUNCTION__);
  }

  /**
   * {@inheritdoc}
   */
  public function postSave(WebformSubmissionInterface $webform_submission, $update = TRUE) {
    $this->displayMessage(__FUNCTION__, $update ? 'update' : 'insert');
  }
}