0

When you try to build the same form twice on a page, the submit handler of the first form gets executed.

In order to overcome this issue you need to generate a unique form ID. How do you generate a unique ID based on some pass context.

  • 1
    In Drupal 7 there was hook_forms for this. In Drupal 8 use the \Drupal\Core\Form\FormInterface::getFormId() – user21641 Nov 7 '16 at 15:55
4

As with Drupal 7, forms need to have a unique form ID. What's different is that in Drupal 8, the connection between the form (code) and the form ID is reversed.

In Drupal 7, as pointed out by @Ivan Jaros in the comment, you start with the form_id, so we had hook_forms() that allowed to map form ID's to a function that then built the form.

In Drupal 8, you first need to create a form object so that its getFormId() method can return a unique ID. The challenge there is that getFormId() is called very early, specifically before buildForm(), so the usual way of passing arguments to forms does not work.

What you need to do is create the form object yourself instead of just passing the class name to the form builder, then you can set any data on it that you want before passing it the form builder.

To get the form class, you can either instantiate it yourself, but then you can't use the ::create() method to inject dependencies, for that, you need to use the class resolver, from FormBuilder::getFormId(), this is what it does by default if you pass in a class name:

if (is_string($form_arg) && class_exists($form_arg)) {
  $form_arg = $this->classResolver->getInstanceFromDefinition($form_arg);
}

You could even use the current object or any other existing object as a form, as long as it implements \Drupal\Core\Form\FormInterface. One example in core that does that is the DraggableListBuilder and BlockListBuilder, see its render() method:

public function render() {
  if (!empty($this->weightKey)) {
    return $this->formBuilder()->getForm($this);
  }
  return parent::render();
}

That has the advantage that you do not need another class, you could make a block plugin that displays a form do it in the same class, for example. On the other side, that prevents you from re-using that form elsewhere and could also result in conflicts if the same method names are used for something else.

  • Does this mean that you return something like a randomly generated string from getFormId()? – rudolfbyker Feb 1 '17 at 6:05
  • 1
    It needs to be predictable and the same for all requests. Simplenews generates a random string, but then stores that in the block configuration. – Berdir Feb 1 '17 at 7:07

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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