Dcycle Blog

Altering a list view in drupal, using an example with Webform

February 07, 2022

In some cases administrative lists are actually views using the core Views module, as is the case with /admin/content (which can be modified using the Views interface at /admin/structure/views/view/content). These are quite easy to modify in a development environment, export as config, then import in a production environment. How to do so is outside the scope of this article, though.

However, certain administrative views do not use the Views module; rather they use an entity’s list view, which requires some coding to modify.

For example the Webform module does not use Views to build its administrative list of webforms. So how can we alter it?

Follow along, this article will show you how!

Basic setup

In our example we will use Webform 6.1.2 on Drupal 9.3.4. The general idea should apply regardless of the version though.

We will start with a standard installation along with webform_ui and webform.

Our goal

Our goal will be to add a column to /admin/structure/webform showing the date of last submission, if any.

How the Webform administrative list works

webform.routing.yml defines the /admin/structure/webform path as follows:

entity.webform.collection:
  path: '/admin/structure/webform'
  defaults:
    _entity_list: 'webform'
    _title: 'Webforms'
  requirements:
    _custom_access: '\Drupal\webform\Access\WebformAccountAccess::checkOverviewAccess'

This means we are using the Webform entity’s list view to display a list of webforms. In turn, the Webform entity defines its list_builder as \Drupal\webform\WebformEntityListBuilder.

The code list builder itself is at ./src/WebformEntityListBuilder.php.

The buildHeader() and buildRow() methods are what need to overridden if we want to add a column.

Creating our subclass of \Drupal\webform\WebformEntityListBuilder

THe first thing we need to do is create a subclass of WebformEntityListBuilder. We can create our own custom module, and in it, at ./my_custom_module/src/MyCustomWebformEntityListBuilder.php, create our class, overriding WebformEntityListBuilder’s buildHeader() and buildRow(). We are injecting the ‘date.formatter’ service so we can format dates nicely the Drupal way.

<?php
# ./my_custom_module/src/MyCustomWebformEntityListBuilder.php

namespace Drupal\my_custom_module;

use Drupal\webform\WebformEntityListBuilder;
use Drupal\webform\Entity\WebformSubmission;
use Drupal\Core\Entity\EntityInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Drupal\Core\Entity\EntityTypeInterface;

/**
 * Custom webform list with last submission.
 *
 * See https://blog.dcycle.com/blog/2022-02-07/alter-list-view/.
 */
class MyCustomWebformEntityListBuilder extends WebformEntityListBuilder {

  /**
   * Injected date formatter.
   *
   * @var \Drupal\Core\Datetime\DateFormatterInterface
   */
  protected $dateFormatter;

  /**
   * {@inheritdoc}
   */
  public static function createInstance(ContainerInterface $container, EntityTypeInterface $entity_type) {
    $instance = parent::createInstance($container, $entity_type);

    $instance->dateFormatter = $container->get('date.formatter');

    return $instance;
  }

  /**
   * {@inheritdoc}
   */
  public function buildHeader() {
    return parent::buildHeader() + [
      'last_submission' => [
        'data' => $this->t('Latest submission'),
        'specifier' => 'latest',
      ],
    ];
  }

  /**
   * {@inheritdoc}
   */
  public function buildRow(EntityInterface $entity) {
    return parent::buildRow($entity) + [
      'last_submission' => $this->lastSubmission($entity),
    ];
  }

  /**
   * Get the last submission for a webform.
   *
   * @param \Drupal\Core\Entity\EntityInterface $entity
   *   A webform.
   *
   * @return string
   *   Either a formatted date, or the translated string "No submission".
   */
  public function lastSubmission(EntityInterface $entity) : string {
    if ($entity->isResultsDisabled()) {
      return $this->t('Results disabled (external)');
    }
    else {
      $query = $this->submissionStorage
        ->getQuery()
        ->accessCheck(TRUE)
        ->condition('webform_id', $entity->id())
        ->sort('completed', 'DESC')
        ->range(0, 1);

      $results = $query->execute();

      if ($row = array_pop($results)) {
        $submission = WebformSubmission::load(intval($row));
        return $this->dateFormatter->format($submission->completed->value);
      }
    }
    return $this->t('No submission');
  }

}

Altering the list builder for webforms

According to this DrupalAnswers thread we can alter the list builder for webform by adding this hook to our .module file:

<?php
# ./my_custom_module/my_custom_module.module

/**
 * Implements hook_entity_type_alter().
 *
 * See https://drupal.stackexchange.com/a/192813/13414.
 */
function my_custom_module_entity_type_alter(array &$entity_types) {
  /** @var $entity_types \Drupal\Core\Entity\EntityTypeInterface[] */
  $entity_types['webform']->setListBuilderClass('Drupal\my_custom_module\MyCustomWebformEntityListBuilder');
}

You will now see the latest submission in a brand new column in the list of webforms.

Next steps: sorting

I still cannot figure out how to sort the webforms by their last submission date. If anyone manages to do that, leave a comment in the comments thread.

Happy coding!