12

We just starting out with Drupal 8 and very quickly we ran into our first problem.

How should I alter an existing form in Drupal 8?

We need to alter the save method for the node form to do a redirect to an other page. We want to alter the node form to become something like a multistep form. After the user creates new content they get redirected to a new form (we created) to get some more information.

We solved our problem implementing hook_entity_type_alter().

function mymodule_entity_type_alter(&$entity_info) {
  $handlers = $entity_info['node']->get('handlers');
  $handlers['form']['default'] =    'Drupal\mymodule\Form\MyExtendedNodeForm';
  $handlers['form']['edit'] = 'Drupal\mymodule\Form\MyExtendedNodeForm';
  $entity_info['node']->set('handlers', $handlers);
}

We then created a new form class that extends the node form and alter the save method.

class MyExtendedNodeForm extends NodeForm {

    public function save(array $form, FormStateInterface $form_state) {
      parent::save($form, $form_state);
      $node = $this->entity;
      $form_state->setRedirect('entity.regions.add_form', ['nid' => $node->id()]);
  }

}

This works perfectly, but is it good? If another module does the same, our code does not get executed anymore.

  • 2
    Drupal 8 still supports hook_form_alter(). If you just need to redirect the form, it's enough to add a form submission handler that does the redirect. – kiamlaluno Jun 29 '15 at 9:05
  • @kiamlaluno That should be the answer I think :) – Berdir Jun 29 '15 at 9:08
  • @Berdir That alone would not be much helpful, I am afraid: My knowledge of Drupal 8 is pretty limited. Plus, the OP should make clear what "alter the save() method" actually means. Does it mean changing the way data is saved, or just do a redirect? In the first case, the answer is more complex. – kiamlaluno Jun 29 '15 at 9:18
  • @kiamlaluno Thx for the answer. In this case we only need to do a redirect after the save() so your answer should work. But I was also interested in how to alter other things in the form in the right Drupal 8 way. For example change how it is saved, or add fields to the form.. etc.. – Jens Steppe Jun 29 '15 at 9:57
5

I had to do the exact same thing yesterday, and I am still looking for a more Drupal 8 way to do this, but haven't found it. I ended up doing it this way:

<?php

/**
 * @file
 * This is my module.
 */

use Drupal\Core\Form\FormStateInterface;
use Symfony\Component\HttpFoundation\Request;

/**
 * Implements hook_form_alter().
 */
function MODULE_form_alter(array &$form, FormStateInterface $form_state, $form_id) {
  if ($form_id == 'user_form') {
    $form['actions']['submit']['#submit'][] = '_MODULE_goto';
  }
}

/**
 * Make the form go to route.id.
 */
function _MODULE_goto($form, FormStateInterface $form_state) {
  $form_state->setRedirect('route.id');
}

I am still very interested in doing it a different way, my module looked so clean without the .module file :).

| improve this answer | |
4

I'm using Drupal 8.1.1 and was trying to redirect users after they modified their account, i.e. clicking the Save button on the /user/edit page. I initially tried this:

/**
 * Implements hook_form_alter().
*/
function MODULE_form_alter(array &$form, FormStateInterface $form_state, $form_id) {
  if ($form_id == 'user_form') {
    $form['#submit'][] = '_MODULE_goto';
  }
}

While that worked for the user_login_form, it would not work for user_form. For user_form I had to use

$form['actions']['submit']['#submit'][] = '_MODULE_goto';

I hope that helps others who stumble across the same problem!

| improve this answer | |
2

Well I wanted to alter the site_information_settings form to add some fields.

As you, I had choice between

  • hook_form_alter (or hook_form_FORM_ID_alter)
  • extend the original form class

Wanted to make some OOP so I started writing a route subscriber service altering the system.site_information_settings route's _form attribute.

Then in my new class extending the SiteInformationForm, after the fields & their validators have been added and the submit function extended I felt exactly the same way as you... Now what if another module rewrites the form's route as well to use its own class ?

Back to the beginning, I had choice between two options... seems like I picked the wrong one.

hook_form_alter / hook_form_FORM_ID_alter seem to be the best way to alter an existing form.

| improve this answer | |
1

After checking this issue, this is what I've found that should suit your case :

/**
 * Implements hook_entity_type_alter().
 */
function mymodule_entity_type_alter(&$entity_info) {
 $entity_info['node']->setFormClass('edit','Drupal\mymodule\Form\CustomNodeForm');
}
| improve this answer | |
0

It looks like what you want to do, it not really to alter the save method, but to change the redirect when a node is saved.

In this situation, doing much like Drupal 7, hook_form_alter along with a custom submit handler (on the form or the button, depending on the form and requirement) would be a good solution.

What you describe in the question also works, but I would avoid overwriting the base class, unless I actually want to change something in the class.

The save method use just used as a submit handler anyways, so doesn't make much sense to overwrite just to add a redirect.

| improve this answer | |
0

I think the better method is to still use one of the hooks from the form_alter family, attached a submit handler, and within that set your redirect.

Contrib extending base classes to do this is hairy in my opinion. Especially since no function is changing, just redirecting.

| improve this answer | |
0

You can create a new EventSubscriber which listens to the KernelEvents::REQUEST event, and then react when you are submitting a node form, something like this

Anyway the hook_form_alter way works ok.

| improve this answer | |
0

What i found so far: Implementing a form alter hook and try to change the validate/submit handlers in the form alter hook itself only works if you change the $form['#validate]/ $form[#submit'] handlers. (calling form_state functions for this wont work in the form alter hook itself)

But when you try this inside a validation handler, it tends to work:

// Load form submit handlers.
$submit_handlers = $form_state->getSubmitHandlers();

// Drop the core entity submitForm function.
$key_core_submit = array_search('::submitForm', $submit_handlers);
if ($key_core_submit !== FALSE) {
  unset($submit_handlers[$key_core_submit]);
  // Add your own submit handler.
  array_unshift($submit_handlers, 'yourmodule_submit_handler');
}
// Update submit handlers.
$form_state->setSubmitHandlers($submit_handlers);

This is because the form has now been built completely, not the case in the form alter hook itself.

The idea is: you can add a validation handler as the last one, evaluate other handlers that exist for the submit, and change them right there.

If you want to also change validation handlers, make sure yours runs prior to any other and change what you want in the validate handler.

Still searching for a better way myself, i just always need to replace the validate/submit handlers for the user register form. But other modules could exist that break my modules implementation. So my module would need to be able to check for these, and that's possible this way. I do wonder if some entity form after build function exists to pull this off. (something that runs when the form has been completed and is about to be delivered) (that's not way to complex for this simple case)

Bypassing the save() method's redirect is not possible, but unsetting the save() is via this way (+ implement your own). True, a class is nicer and offers easier access to objects/etc, but overriding a core class indeed leads to issues when other modules want to use these. That's not the case if you use this code.

| improve this answer | |

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.