3

Is there a way to alter a form, implemented by another module, using a class, in the same way it is possible to build a form using a class?

1
  • 1
    You can extend the form class and get the original form build by calling the parent method parent::buildForm($form, $form_state). But this depends on how you intend to use the resulting class. If you can modify the form class or call the form class yourself, then this is no problem.
    – 4uk4
    Jun 13, 2017 at 13:12

2 Answers 2

2

Drupal core code still uses hook_form_alter() (or hook_form_BASE_FORM_ID_alter(), or hook_form_FORM_ID_alter()) for that purpose.
You could alter a route and change the form associated with that route, but this would mean that:

  • You would need to keep track of the form class that was associated to that route before you changed it; in this way, you would be able to let the previous class add its own form fields

  • You would need to alter all the routes using that form class, in order to make Drupal use your class

The closer I could see used from Drupal code, which means the code would be in a class, is what the Content moderation module does in content_moderation_form_alter().

function content_moderation_form_alter(&$form, FormStateInterface $form_state, $form_id) {
  \Drupal::service('class_resolver')
    ->getInstanceFromDefinition(EntityTypeInfo::class)
    ->formAlter($form, $form_state, $form_id);
}

Using that, you have the pro of using object-oriented code, allow other modules to extend the same form without complicating your code, and without interfering with code used from other modules.

Since the Drupal API page doesn't link the EntityTypeInfo class, it's the class defined in core/modules/content_moderation/src/EntityTypeInfo.php.

The relevant code is the following one, which shows how a class method is used as form submission handler.

  /**
   * Alters bundle forms to enforce revision handling.
   *
   * @param array $form
   *   An associative array containing the structure of the form.
   * @param \Drupal\Core\Form\FormStateInterface $form_state
   *   The current state of the form.
   * @param string $form_id
   *   The form id.
   *
   * @see hook_form_alter()
   */
  public function formAlter(array &$form, FormStateInterface $form_state, $form_id) {
    $form_object = $form_state->getFormObject();
    if ($form_object instanceof BundleEntityFormBase) {
      $type = $form_object->getEntity()->getEntityType();
      if ($this->moderationInfo->canModerateEntitiesOfEntityType($type)) {
        $this->entityTypeManager->getHandler($type->getBundleOf(), 'moderation')->enforceRevisionsBundleFormAlter($form, $form_state, $form_id);
      }
    }
    elseif ($form_object instanceof ContentEntityFormInterface) {
      $entity = $form_object->getEntity();
      if ($this->moderationInfo->isModeratedEntity($entity)) {
        $this->entityTypeManager
          ->getHandler($entity->getEntityTypeId(), 'moderation')
          ->enforceRevisionsEntityFormAlter($form, $form_state, $form_id);
        // Submit handler to redirect to the latest version, if available.
        $form['actions']['submit']['#submit'][] = [EntityTypeInfo::class, 'bundleFormRedirect'];
      }
    }
  }

This is the form submission handler that method is using.

  /**
   * Redirect content entity edit forms on save, if there is a forward revision.
   *
   * When saving their changes, editors should see those changes displayed on
   * the next page.
   *
   * @param array $form
   *   An associative array containing the structure of the form.
   * @param \Drupal\Core\Form\FormStateInterface $form_state
   *   The current state of the form.
   */
  public static function bundleFormRedirect(array &$form, FormStateInterface $form_state) {
    /* @var \Drupal\Core\Entity\ContentEntityInterface $entity */
    $entity = $form_state->getFormObject()->getEntity();

    $moderation_info = \Drupal::getContainer()->get('content_moderation.moderation_information');
    if ($moderation_info->hasForwardRevision($entity) && $entity->hasLinkTemplate('latest-version')) {
      $entity_type_id = $entity->getEntityTypeId();
      $form_state->setRedirect("entity.$entity_type_id.latest_version", [$entity_type_id => $entity->id()]);
    }
  }

As you see, it's not the same as extending FormBase, but it's more OOP than using plain functions.

0

No, there's no object-oriented alternative to hook_form_alter.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.