Custom Webform Element in Drupal: Radio Buttons with Score Input Field

I’m developing a custom Drupal module to add a new form element to the Webform module, which allows users to assign a score to each option in a radio button field. I’m new to Drupal development and would appreciate any guidance on what I might be doing wrong. Objective:

To create a custom Webform element where each radio option can have an additional score input field. Here is the code for webform_custom_choice.module:

<?php

/**
 * Implements hook_webform_element_info_alter().
 */
function webform_custom_choice_webform_element_info_alter(array &$element_info) {
  $element_info['choice_with_score'] = [
    'class' => 'Drupal\webform_custom_choice\Plugin\WebformElement\ChoiceWithScore',
    'label' => t('Choice with Score'),
    'description' => t('Adds a radio button with an extra score field per option.'),
  ];
}

/**
 * Implements hook_webform_element_presave().
 */
function webform_custom_choice_webform_element_presave(&$element) {
  if (isset($element['#score'])) {
    foreach ($element['#options'] as $key => &$option) {
      $element['#score'][$key] = $element['#score'][$key] ?? 0;
    }
  }
}

/**
 * Implements hook_webform_submission_presave().
 */
function webform_custom_choice_webform_submission_presave(\Drupal\webform\Entity\WebformSubmission $webform_submission) {
  $data = $webform_submission->getData();

  if (isset($data['choice_with_score'])) {
    $choice_value = $data['choice_with_score'];
    $element = $webform_submission->getWebform()->getElement('choice_with_score');
    $score = isset($element['#score'][$choice_value]) ? $element['#score'][$choice_value] : 0;

    $data['choice_with_score'] = ['value' => $choice_value, 'score' => $score];
    $webform_submission->setData($data);
  }
}

Here is the code for ChoiceWithScore.php:

php

<?php

namespace Drupal\webform_custom_choice\Plugin\WebformElement;

use Drupal\webform\Plugin\WebformElement\Radios;
use Drupal\Core\Form\FormStateInterface;

/**
 * Provides a 'choice_with_score' element.
 *
 * @WebformElement(
 *   id = "choice_with_score",
 *   label = @Translation("Choice with Score"),
 *   description = @Translation("Provides a radio button element with an additional score field for each option."),
 *   category = @Translation("Choices"),
 * )
 */
class ChoiceWithScore extends Radios {

  /**
   * {@inheritdoc}
   */
  public function prepare(array &$element, ?\Drupal\webform\WebformSubmissionInterface $webform_submission = null) {
    parent::prepare($element, $webform_submission);

    if (isset($element['#options'])) {
      foreach ($element['#options'] as $key => &$value) {
        if (isset($element['#score'][$key])) {
          $value .= ' (Score: ' . $element['#score'][$key] . ')';
        } else {
          $element['#score'][$key] = 0;
        }
      }
    }
  }

  /**
   * {@inheritdoc}
   */
  public function getDefaultProperties() {
    $properties = parent::getDefaultProperties();
    $properties['states_wrapper'] = [];
    $properties['id'] = 'choice_with_score';
    $properties['composite'] = FALSE;
    $properties['multiline'] = FALSE;
    $properties['score'] = [];

    return $properties;
  }

  /**
   * {@inheritdoc}
   */
  public function settingsForm(array $form, FormStateInterface $form_state, array $complete_form) {
    $form = parent::settingsForm($form, $form_state, $complete_form);

    $options = $form_state->getValue(['properties', 'options']) ?: $this->getElementOptions();
    $scores = $form_state->getValue(['properties', 'score']) ?: $this->getElementScore();

    foreach ($options as $key => $label) {
      $form['score'][$key] = [
        '#type' => 'number',
        '#title' => $this->t('Score for @option', ['@option' => $label]),
        '#default_value' => isset($scores[$key]) ? $scores[$key] : 0,
        '#description' => $this->t('Specify a score for this option.'),
      ];
    }

    return $form;
  }

  /**
   * Helper function to get scores for each option.
   */
  private function getElementScore() {
    return $this->getElementProperty('score', []);
  }
}


Issues:

  • I can’t see the extra score input fields when creating a webform.
  • When I test the form, the question doesn’t display, and when I submit the form, I can only see the selected option without the score.

What I might be missing or doing wrong?

public function prepare(array &$element, ?\Drupal\webform\WebformSubmissionInterface $webform_submission = null) {
parent::prepare($element, $webform_submission);

foreach ($element['#options'] as $key => $value) {
    // Add a number input for score next to each option.
    $element['#options'][$key] = [
        '#type' => 'radios',
        '#title' => $value,
        '#options' => [$key => $value],
    ];
    $element['#options'][$key]['score'] = [
        '#type' => 'number',
        '#title' => $this->t('Score for @option', ['@option' => $value]),
        '#default_value' => $element['#score'][$key] ?? 0,
    ];
}

}

Form Submission Handling

When handling form submissions, make sure you’re correctly processing the scores. You may want to modify the webform_submission_presave() function to ensure you’re saving the scores properly along with the choice:

public function webform_custom_choice_webform_submission_presave(\Drupal\webform\Entity\WebformSubmission $webform_submission) {
    $data = $webform_submission->getData();
    if (isset($data['choice_with_score'])) {
        $choice_value = $data['choice_with_score'];
        $element = $webform_submission->getWebform()->getElement('choice_with_score');

        // Collect scores
        $scores = [];
        foreach ($element['#options'] as $key => $value) {
            $scores[$key] = $data['choice_with_score'][$key]['score'] ?? 0;
        }

        $data['choice_with_score'] = [
            'value' => $choice_value,
            'scores' => $scores,
        ];
        $webform_submission->setData($data);
    }
}

Settings Form

Make sure you are correctly saving and loading the scores in your settings form. When setting up the settingsForm() method, ensure that the scores are persisted properly.

Clear Cache

Whenever you make changes to your module, don’t forget to clear Drupal’s cache. You can do this through the admin interface or by using Drush with the command: