- Issue created by @efpapado
- 🇵🇱Poland Graber
- Views Entity Form Field has 5k usage while VBO has 60k so I think the former should rather have an issue.
- VBO unsets the default `submit` button only, if Views Entity Form Field defined its own button instead (`veff_submit` or whatever, it wouldn't be affected by VBO.
In any case, happy to help to solve this somehow.
- 🇵🇱Poland Graber
I see Views adds a "Save" button by default.. I think every contrib module should unset that and set one of its own.. and core Views shouldn't provide that default button at all in the first place.
- 🇳🇴Norway efpapado
@graber Thanks for your quick reply. I started digging the code, here is what I found:
Both modules use the core class
\Drupal\views\Form\ViewsForm
that adds the form functionality to the view.
For both modules, when the form is submitted this function is called:\Drupal\views\Form\ViewsForm::submitForm
which then calls the\Drupal\views\Form\ViewsFormMainForm::submitForm
, which then does this:foreach ($view->field as $field) { if (method_exists($field, 'viewsFormSubmit')) { $field->viewsFormSubmit($form, $form_state); } }
Similar logic is followed on the validation step, in
\Drupal\views\Form\ViewsFormMainForm::validateForm
By looking at this, I understand that the core's logic is that all the possible fields that contain a form are supposed to have their forms validated/submitted, and then each module's validation/submit handler is responsible to figure out whether it should do something or not. So I think that what you wrote:
I see Views adds a "Save" button by default.. I think every contrib module should unset that and set one of its own.. and core Views shouldn't provide that default button at all in the first place.
is basically correct as approach.
I experimented a bit with having VEFF adding its own button, and calling its own submit handlers independently. It worked more or less, but new problems arose. When a VEFF field is added in the form, the VBO's validation breaks:
public function viewsFormValidate(&$form, FormStateInterface $form_state) { if ($this->options['buttons']) { $trigger = $form_state->getTriggeringElement(); $action_delta = \end($trigger['#parents']); $form_state->setValue('action', $action_delta); } else { $action_delta = $form_state->getValue('action'); }
In case of VBO's non-buttons configuration, the last line
$action_delta = $form_state->getValue('action');
expects a flat values array.
But claro is moving the VBO stuff in another container:
https://git.drupalcode.org/project/drupal/-/blob/11.x/core/themes/claro/...function claro_form_alter(array &$form, FormStateInterface $form_state, $form_id) { ... // Determine if the Views form includes a bulk operations form. If it does, // move it to the bottom and remove the second bulk operations submit. foreach (Element::children($form['header']) as $key) { if (str_contains($key, '_bulk_form')) { // Move the bulk actions form from the header to its own container. $form['bulk_actions_container'] = $form['header'][$key]; unset($form['header'][$key]); // Remove the supplementary bulk operations submit button as it appears // in the same location the form was moved to. unset($form['actions']); ...
and because VEFF is adding to the form a
#tree => TRUE
, the$action_delta = $form_state->getValue('action');
returns NULL.
This last part must become$action_delta = $form_state->getValue(['bulk_actions_container', 'action']);
in order to return the correct delta.Even when bypassing this, because of the core class's behaviour described above, whatever button triggers the submission, all the submit handlers will be called. So if the extra button offered by VEFF is clicked, VBO's submit handler will also be called (and also the opposite).
I suggest we add on both
viewsFormValidate
andviewsFormSubmit
$trigger = $form_state->getTriggeringElement(); // some logic that will return, if the button that submitted the form was not the VBO one
This of course must be done also on VEFF. And possibly we need to document it on the core's class.
Also, we need to make VBO not change its behaviour based on the theme's or other modules' changes. Thus find a more concrete way to understand the context of the form submission (#tree or not, own container or not).
Before proceeding with the above, I'd like to ask your opinion based on your insight.