Webform show custom composite result at their own line in submissions download I have a webform with a custom composite field
THE custom composite has one text field that can be repeated an unlimited amount of times 

here is an image of the form:


What I want is to show each student name in a separate line when I download the results.
Is it possible to do it using a module?
if not I will be glad to hear if there are hooks that can manipulate the downloaded data
I solved the above issue by creating a new Webform Exporter plugin, extending the default "Delimited text" in a custom module. I assume you download them in either CSV or MS Excel file format. I've only tried the below for the CSV export, but I see no reason for it not working for MS Excel ones as well.

The idea in a short summary is:


Create a custom WebformExporter plugin extending the default "Delimited text" exporter (class DelimitedWebformExporter)
Override the writeSubmission method which adds the data to the CSV string and make it run once for every sub-element of your composite element


How to create a custom WebformExporter plugin

In a custom module, create the file

&lt;custom_module_root&gt;/src/Plugin/WebformExporter/CustomDelimitedWebformExporter.php

And in it create a class CustomDelimitedWebformExporter with the necessary annotations in order to define the WebformExporter plugin. Also since we want to extend the default exporter for delimited text, our class should extend the "Delimited text" class DelimitedWebformExporter.

&lt;?php

namespace Drupal\custom_module\Plugin\WebformExporter;

use Drupal\webform\Plugin\WebformExporter\DelimitedWebformExporter;

/**
 * Defines a delimited text exporter.
 *
 * @WebformExporter(
 *   id = "custom_delimited",
 *   label = @Translation("Custom Delimited text"),
 *   description = @Translation("Exports results as delimited text file, splitting the composite element's multiple values to different rows."),
 * )
 */
class CustomDelimitedWebformExporter extends DelimitedWebformExporter {

}


If you did this correctly you should see (after clearing cache) a new option in the "Export format" dropdown in the Results Download page of the webform.

How to override the submission writing to loop for each subelement

Now override the writeSubmission method. That method in the parent class builds a new row of results for each submission and then puts the data in the CSV handler. Since we want the results in different rows, we therefore need to call the parent method just as many times and print each subelement.

use Drupal\webform\WebformSubmissionInterface;

const COMPOSITE_ELEMENT_KEY = 'composite_element_key';

/**
 * {@inheritdoc}
 */
public function writeSubmission(WebformSubmissionInterface $webform_submission) {
  $element = $webform_submission-&gt;getElementData(self::COMPOSITE_ELEMENT_KEY);
  if ($element) {
    for ($i = 0; $i &lt; sizeof($element); $i++) {
      $webform_submission-&gt;setElementData(self::COMPOSITE_ELEMENT_KEY, [0 =&gt; $element[$i]]);
      parent::writeSubmission($webform_submission);
    }
  } else {
    parent::writeSubmission($webform_submission);
  }
}


A couple of notes:


The above works for one specific composite element in your webform. You can adjust the behaviour if you need it for more that one or for an arbitrary one (i.e. by checking if the element type is composite)
As the above does not change anything for the rest of the webform's elements, the rest of the columns in the results will have their data copied in every extra row created.


End result

&lt;?php

namespace Drupal\custom_module\Plugin\WebformExporter;

use Drupal\webform\Plugin\WebformExporter\DelimitedWebformExporter;
use Drupal\webform\WebformSubmissionInterface;

/**
 * Defines a delimited text exporter.
 *
 * @WebformExporter(
 *   id = "custom_delimited",
 *   label = @Translation("Custom Delimited text"),
 *   description = @Translation("Exports results as delimited text file, splitting the composite element's multiple values to different rows."),
 * )
 */
class CustomDelimitedWebformExporter extends DelimitedWebformExporter {

  const COMPOSITE_ELEMENT_KEY = 'composite_element_key';

  /**
   * {@inheritdoc}
   */
  public function writeSubmission(WebformSubmissionInterface $webform_submission) {
    $element = $webform_submission-&gt;getElementData(self::COMPOSITE_ELEMENT_KEY);
    if ($element) {
      for ($i = 0; $i &lt; sizeof($element); $i++) {
        $webform_submission-&gt;setElementData(self::COMPOSITE_ELEMENT_KEY, [0 =&gt; $element[$i]]);
        parent::writeSubmission($webform_submission);
      }
    } else {
      parent::writeSubmission($webform_submission);
    }
  }

}