Skip to content
Snippets Groups Projects

Issue #3024419: Add support for optional required fields

@@ -2,6 +2,7 @@
namespace Drupal\views_bulk_edit\Form;
use Drupal\Component\Utility\NestedArray;
use Drupal\Core\Entity\Entity\EntityFormDisplay;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\RevisionableEntityBundleInterface;
@@ -94,9 +95,134 @@ trait BulkEditFormTrait {
}
}
$form['#after_build'][] = [static::class, 'buildBundleFormsAfterBuild'];
return $form;
}
/**
* Form #after_build callback for bulk edit bundle forms.
*/
public static function buildBundleFormsAfterBuild(array $element, FormStateInterface $form_state)
{
// Limit validation errors to only those for fields selected to edit in
// bulk.
$triggering_element = &$form_state->getTriggeringElement();
$is_submission = $form_state->isProcessingInput() && !empty($triggering_element['#name']) && $triggering_element['#name'] === 'op';
$storage = $form_state->getStorage();
if (!empty($storage['vbe_entity_bundles_data'])) {
foreach ($storage['vbe_entity_bundles_data'] as $entity_type_id => $bundles) {
foreach (array_keys($bundles) as $bundle) {
$field_selections = NestedArray::getValue($element, [$entity_type_id, $bundle, '_field_selector']);
$selected_fields = Element::children($field_selections);
foreach ($selected_fields as $selected_field) {
$field_selection = $field_selections[$selected_field];
if (empty($field_selection['#type']) || $field_selection['#type'] !== 'checkbox') {
continue;
}
$is_selected = (bool) ($field_selections[$selected_field]['#value'] ?? FALSE);
if ($is_selected) {
// Skip, this field should be processed.
continue;
}
if (empty($element[$entity_type_id][$bundle][$selected_field])) {
// The field doesn't exist. This shouldn't happen.
continue;
}
$state_selector = static::getVisibilityStateSelection($element, [
$entity_type_id,
$bundle,
'_field_selector',
$selected_field,
]);
static::processOptionalFields(
$element[$entity_type_id][$bundle][$selected_field],
$form_state,
$is_submission,
$state_selector
);
}
}
}
}
return $element;
}
/**
* Handles form validation for optional fields.
*
* This is necessary in case the optional fields are required by default, and
* might stop normal form submissions from working.
*/
protected static function processOptionalFields(array &$elements, FormStateInterface $form_state, $is_submission, $state_selector)
{
foreach (Element::children($elements, TRUE) as $key) {
if (isset($elements[$key]) && $elements[$key]) {
static::processOptionalFields($elements[$key], $form_state, $is_submission, $state_selector);
}
}
$is_required = !empty($elements['#required']) || !empty($elements['#states']['required']);
/* @see \Drupal\Core\Form\FormValidator::doValidateForm() */
if ($is_required || !empty($elements['#element_validate']) || !empty($elements['#validate'])) {
if ($is_submission) {
// Automatically skip required validation on unused fields by flagging
// it as not required and disabling access to it during the form
// submission phase.
$elements['#required'] = FALSE;
$elements['#access'] = FALSE;
// Remove validation from inline entity form.
if (!empty($elements['#element_validate'])) {
foreach ($elements['#element_validate'] as $key => $value) {
if (is_array($value) && count($value) >= 2) {
[$class, $method] = $value;
if (is_string($class) && str_contains($class, 'InlineEntityFormComplex') &&
$method === 'requiredField') {
unset($elements['#element_validate'][$key]);
}
}
}
}
}
if ($is_required && !$is_submission && !empty($elements['#type'])) {
// Use the #states API to dynamically mark as required on the frontend
// based on if the field has been selected for bulk editing or not.
$existing_states = NestedArray::getValue($elements, [
'#states',
'required',
]);
$required_states = [
':input[name="' . $state_selector . '"]' => [
['checked' => TRUE],
],
];
if ($existing_states) {
// Merge with existing required states.
$required_states[] = $existing_states;
$required_states = [$required_states];
}
NestedArray::setValue($elements, ['#states', 'required'], $required_states, TRUE);
}
}
}
/**
* Calculate the visibility state selection.
*/
protected static function getVisibilityStateSelection(array $elements, array $field_path)
{
$parents = $elements['#array_parents'];
$parents = array_merge($parents, $field_path);
$selector = $root = array_shift($parents);
if ($parents) {
$selector = $root . '[' . implode('][', $parents) . ']';
}
return $selector;
}
/**
* Gets the form for this entity display.
*
Loading