Issue #2940201 by xjm, amateescu, benjifisher, tedbow, tim.plunkett, Berdir: hook_field_widget_form_alter() can no longer affect the whole widget for multi-value fields
parent
17a47b8e93
commit
c924020c45
|
@ -241,6 +241,19 @@ abstract class WidgetBase extends PluginSettingsBase implements WidgetInterface
|
||||||
],
|
],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Allow modules to alter the field multi-value widget form element.
|
||||||
|
// This hook can also be used for single-value fields.
|
||||||
|
$context = [
|
||||||
|
'form' => $form,
|
||||||
|
'widget' => $this,
|
||||||
|
'items' => $items,
|
||||||
|
'default' => $this->isDefaultValueWidget($form_state),
|
||||||
|
];
|
||||||
|
\Drupal::moduleHandler()->alter([
|
||||||
|
'field_widget_multivalue_form',
|
||||||
|
'field_widget_multivalue_' . $this->getPluginId() . '_form',
|
||||||
|
], $elements, $form_state, $context);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $elements;
|
return $elements;
|
||||||
|
|
|
@ -164,6 +164,11 @@ function hook_field_widget_info_alter(array &$info) {
|
||||||
/**
|
/**
|
||||||
* Alter forms for field widgets provided by other modules.
|
* Alter forms for field widgets provided by other modules.
|
||||||
*
|
*
|
||||||
|
* This hook can only modify individual elements within a field widget and
|
||||||
|
* cannot alter the top level (parent element) for multi-value fields. In most
|
||||||
|
* cases, you should use hook_field_widget_multivalue_form_alter() instead and
|
||||||
|
* loop over the elements.
|
||||||
|
*
|
||||||
* @param $element
|
* @param $element
|
||||||
* The field widget form element as constructed by
|
* The field widget form element as constructed by
|
||||||
* \Drupal\Core\Field\WidgetBaseInterface::form().
|
* \Drupal\Core\Field\WidgetBaseInterface::form().
|
||||||
|
@ -183,6 +188,7 @@ function hook_field_widget_info_alter(array &$info) {
|
||||||
* @see \Drupal\Core\Field\WidgetBaseInterface::form()
|
* @see \Drupal\Core\Field\WidgetBaseInterface::form()
|
||||||
* @see \Drupal\Core\Field\WidgetBase::formSingleElement()
|
* @see \Drupal\Core\Field\WidgetBase::formSingleElement()
|
||||||
* @see hook_field_widget_WIDGET_TYPE_form_alter()
|
* @see hook_field_widget_WIDGET_TYPE_form_alter()
|
||||||
|
* @see hook_field_widget_multivalue_form_alter()
|
||||||
*/
|
*/
|
||||||
function hook_field_widget_form_alter(&$element, \Drupal\Core\Form\FormStateInterface $form_state, $context) {
|
function hook_field_widget_form_alter(&$element, \Drupal\Core\Form\FormStateInterface $form_state, $context) {
|
||||||
// Add a css class to widget form elements for all fields of type mytype.
|
// Add a css class to widget form elements for all fields of type mytype.
|
||||||
|
@ -200,6 +206,11 @@ function hook_field_widget_form_alter(&$element, \Drupal\Core\Form\FormStateInte
|
||||||
* specific widget form, rather than using hook_field_widget_form_alter() and
|
* specific widget form, rather than using hook_field_widget_form_alter() and
|
||||||
* checking the widget type.
|
* checking the widget type.
|
||||||
*
|
*
|
||||||
|
* This hook can only modify individual elements within a field widget and
|
||||||
|
* cannot alter the top level (parent element) for multi-value fields. In most
|
||||||
|
* cases, you should use hook_field_widget_multivalue_WIDGET_TYPE_form_alter()
|
||||||
|
* instead and loop over the elements.
|
||||||
|
*
|
||||||
* @param $element
|
* @param $element
|
||||||
* The field widget form element as constructed by
|
* The field widget form element as constructed by
|
||||||
* \Drupal\Core\Field\WidgetBaseInterface::form().
|
* \Drupal\Core\Field\WidgetBaseInterface::form().
|
||||||
|
@ -212,6 +223,7 @@ function hook_field_widget_form_alter(&$element, \Drupal\Core\Form\FormStateInte
|
||||||
* @see \Drupal\Core\Field\WidgetBaseInterface::form()
|
* @see \Drupal\Core\Field\WidgetBaseInterface::form()
|
||||||
* @see \Drupal\Core\Field\WidgetBase::formSingleElement()
|
* @see \Drupal\Core\Field\WidgetBase::formSingleElement()
|
||||||
* @see hook_field_widget_form_alter()
|
* @see hook_field_widget_form_alter()
|
||||||
|
* @see hook_field_widget_multivalue_WIDGET_TYPE_form_alter()
|
||||||
*/
|
*/
|
||||||
function hook_field_widget_WIDGET_TYPE_form_alter(&$element, \Drupal\Core\Form\FormStateInterface $form_state, $context) {
|
function hook_field_widget_WIDGET_TYPE_form_alter(&$element, \Drupal\Core\Form\FormStateInterface $form_state, $context) {
|
||||||
// Code here will only act on widgets of type WIDGET_TYPE. For example,
|
// Code here will only act on widgets of type WIDGET_TYPE. For example,
|
||||||
|
@ -220,6 +232,74 @@ function hook_field_widget_WIDGET_TYPE_form_alter(&$element, \Drupal\Core\Form\F
|
||||||
$element['#autocomplete_route_name'] = 'mymodule.autocomplete_route';
|
$element['#autocomplete_route_name'] = 'mymodule.autocomplete_route';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Alter forms for multi-value field widgets provided by other modules.
|
||||||
|
*
|
||||||
|
* To alter the individual elements within the widget, loop over
|
||||||
|
* \Drupal\Core\Render\Element::children($elements).
|
||||||
|
*
|
||||||
|
* @param array $elements
|
||||||
|
* The field widget form elements as constructed by
|
||||||
|
* \Drupal\Core\Field\WidgetBase::formMultipleElements().
|
||||||
|
* @param \Drupal\Core\Form\FormStateInterface $form_state
|
||||||
|
* The current state of the form.
|
||||||
|
* @param array $context
|
||||||
|
* An associative array containing the following key-value pairs:
|
||||||
|
* - form: The form structure to which widgets are being attached. This may be
|
||||||
|
* a full form structure, or a sub-element of a larger form.
|
||||||
|
* - widget: The widget plugin instance.
|
||||||
|
* - items: The field values, as a
|
||||||
|
* \Drupal\Core\Field\FieldItemListInterface object.
|
||||||
|
* - default: A boolean indicating whether the form is being shown as a dummy
|
||||||
|
* form to set default values.
|
||||||
|
*
|
||||||
|
* @see \Drupal\Core\Field\WidgetBaseInterface::form()
|
||||||
|
* @see \Drupal\Core\Field\WidgetBase::formMultipleElements()
|
||||||
|
* @see hook_field_widget_multivalue_WIDGET_TYPE_form_alter()
|
||||||
|
*/
|
||||||
|
function hook_field_widget_multivalue_form_alter(array &$elements, \Drupal\Core\Form\FormStateInterface $form_state, array $context) {
|
||||||
|
// Add a css class to widget form elements for all fields of type mytype.
|
||||||
|
$field_definition = $context['items']->getFieldDefinition();
|
||||||
|
if ($field_definition->getType() == 'mytype') {
|
||||||
|
// Be sure not to overwrite existing attributes.
|
||||||
|
$elements['#attributes']['class'][] = 'myclass';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Alter multi-value widget forms for a widget provided by another module.
|
||||||
|
*
|
||||||
|
* Modules can implement hook_field_widget_multivalue_WIDGET_TYPE_form_alter() to
|
||||||
|
* modify a specific widget form, rather than using
|
||||||
|
* hook_field_widget_form_alter() and checking the widget type.
|
||||||
|
*
|
||||||
|
* To alter the individual elements within the widget, loop over
|
||||||
|
* \Drupal\Core\Render\Element::children($elements).
|
||||||
|
*
|
||||||
|
* @param array $elements
|
||||||
|
* The field widget form elements as constructed by
|
||||||
|
* \Drupal\Core\Field\WidgetBase::formMultipleElements().
|
||||||
|
* @param \Drupal\Core\Form\FormStateInterface $form_state
|
||||||
|
* The current state of the form.
|
||||||
|
* @param array $context
|
||||||
|
* An associative array. See hook_field_widget_multivalue_form_alter() for
|
||||||
|
* the structure and content of the array.
|
||||||
|
*
|
||||||
|
* @see \Drupal\Core\Field\WidgetBaseInterface::form()
|
||||||
|
* @see \Drupal\Core\Field\WidgetBase::formMultipleElements()
|
||||||
|
* @see hook_field_widget_multivalue_form_alter()
|
||||||
|
*/
|
||||||
|
function hook_field_widget_multivalue_WIDGET_TYPE_form_alter(array &$elements, \Drupal\Core\Form\FormStateInterface $form_state, array $context) {
|
||||||
|
// Code here will only act on widgets of type WIDGET_TYPE. For example,
|
||||||
|
// hook_field_widget_multivalue_mymodule_autocomplete_form_alter() will only
|
||||||
|
// act on widgets of type 'mymodule_autocomplete'.
|
||||||
|
// Change the autcomplete route for each autocomplete element within the
|
||||||
|
// multivalue widget.
|
||||||
|
foreach (Element::children($elements) as $delta => $element) {
|
||||||
|
$elements[$delta]['#autocomplete_route_name'] = 'mymodule.autocomplete_route';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @} End of "defgroup field_widget".
|
* @} End of "defgroup field_widget".
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -113,6 +113,9 @@ class FormTest extends FieldTestBase {
|
||||||
// Check that hook_field_widget_form_alter() does not believe this is the
|
// Check that hook_field_widget_form_alter() does not believe this is the
|
||||||
// default value form.
|
// default value form.
|
||||||
$this->assertNoText('From hook_field_widget_form_alter(): Default form is true.', 'Not default value form in hook_field_widget_form_alter().');
|
$this->assertNoText('From hook_field_widget_form_alter(): Default form is true.', 'Not default value form in hook_field_widget_form_alter().');
|
||||||
|
// Check that hook_field_widget_form_alter() does not believe this is the
|
||||||
|
// default value form.
|
||||||
|
$this->assertNoText('From hook_field_widget_multivalue_form_alter(): Default form is true.', 'Not default value form in hook_field_widget_form_alter().');
|
||||||
|
|
||||||
// Submit with invalid value (field-level validation).
|
// Submit with invalid value (field-level validation).
|
||||||
$edit = [
|
$edit = [
|
||||||
|
@ -634,4 +637,44 @@ class FormTest extends FieldTestBase {
|
||||||
$this->assertEscaped("<script>alert('a configurable field');</script>");
|
$this->assertEscaped("<script>alert('a configurable field');</script>");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests hook_field_widget_multivalue_form_alter().
|
||||||
|
*/
|
||||||
|
public function testFieldFormMultipleWidgetAlter() {
|
||||||
|
$this->widgetAlterTest('hook_field_widget_multivalue_form_alter');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests hook_field_widget_multivalue_WIDGET_TYPE_form_alter().
|
||||||
|
*/
|
||||||
|
public function testFieldFormMultipleWidgetTypeAlter() {
|
||||||
|
$this->widgetAlterTest('hook_field_widget_multivalue_WIDGET_TYPE_form_alter');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests widget alter hooks for a given hook name.
|
||||||
|
*/
|
||||||
|
protected function widgetAlterTest($hook) {
|
||||||
|
// Set a flag in state so that the hook implementations will run.
|
||||||
|
\Drupal::state()->set("field_test.$hook", TRUE);
|
||||||
|
|
||||||
|
// Create a field with fixed cardinality, configure the form to use a
|
||||||
|
// "multiple" widget.
|
||||||
|
$field_storage = $this->fieldStorageMultiple;
|
||||||
|
$field_name = $field_storage['field_name'];
|
||||||
|
$this->field['field_name'] = $field_name;
|
||||||
|
FieldStorageConfig::create($field_storage)->save();
|
||||||
|
FieldConfig::create($this->field)->save();
|
||||||
|
entity_get_form_display($this->field['entity_type'], $this->field['bundle'], 'default')
|
||||||
|
->setComponent($field_name, [
|
||||||
|
'type' => 'test_field_widget_multiple',
|
||||||
|
])
|
||||||
|
->save();
|
||||||
|
|
||||||
|
$this->drupalGet('entity_test/add');
|
||||||
|
$this->assertUniqueText("From $hook(): prefix on field_test_text parent element.");
|
||||||
|
$this->assertText("From $hook(): description on field_test_text child element.");
|
||||||
|
$this->drupalGet('entity_test/add');
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
|
|
||||||
use Drupal\Core\Entity\EntityTypeInterface;
|
use Drupal\Core\Entity\EntityTypeInterface;
|
||||||
use Drupal\Core\Form\FormStateInterface;
|
use Drupal\Core\Form\FormStateInterface;
|
||||||
|
use Drupal\Core\Render\Element;
|
||||||
use Drupal\field\FieldStorageConfigInterface;
|
use Drupal\field\FieldStorageConfigInterface;
|
||||||
|
|
||||||
require_once __DIR__ . '/field_test.entity.inc';
|
require_once __DIR__ . '/field_test.entity.inc';
|
||||||
|
@ -100,16 +101,6 @@ function field_test_entity_display_build_alter(&$output, $context) {
|
||||||
* Implements hook_field_widget_form_alter().
|
* Implements hook_field_widget_form_alter().
|
||||||
*/
|
*/
|
||||||
function field_test_field_widget_form_alter(&$element, FormStateInterface $form_state, $context) {
|
function field_test_field_widget_form_alter(&$element, FormStateInterface $form_state, $context) {
|
||||||
$field_definition = $context['items']->getFieldDefinition();
|
|
||||||
switch ($field_definition->getName()) {
|
|
||||||
case 'alter_test_text':
|
|
||||||
drupal_set_message('Field size: ' . $context['widget']->getSetting('size'));
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'alter_test_options':
|
|
||||||
drupal_set_message('Widget type: ' . $context['widget']->getPluginId());
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
// Set a message if this is for the form displayed to set default value for
|
// Set a message if this is for the form displayed to set default value for
|
||||||
// the field.
|
// the field.
|
||||||
if ($context['default']) {
|
if ($context['default']) {
|
||||||
|
@ -117,6 +108,43 @@ function field_test_field_widget_form_alter(&$element, FormStateInterface $form_
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements hook_field_widget_multivalue_form_alter().
|
||||||
|
*/
|
||||||
|
function field_test_field_widget_multivalue_form_alter(array &$elements, FormStateInterface $form_state, array $context) {
|
||||||
|
_field_test_alter_widget("hook_field_widget_multivalue_form_alter", $elements, $form_state, $context);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements hook_field_widget_multivalue_form_alter().
|
||||||
|
*/
|
||||||
|
function field_test_field_widget_multivalue_text_textfield_form_alter(array &$elements, FormStateInterface $form_state, array $context) {
|
||||||
|
_field_test_alter_widget("hook_field_widget_multivalue_WIDGET_TYPE_form_alter", $elements, $form_state, $context);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets up alterations for widget alter tests.
|
||||||
|
*
|
||||||
|
* @see \Drupal\field\Tests\FormTest::widgetAlterTest()
|
||||||
|
*/
|
||||||
|
function _field_test_alter_widget($hook, array &$elements, FormStateInterface $form_state, array $context) {
|
||||||
|
|
||||||
|
// Set a message if this is for the form displayed to set default value for
|
||||||
|
// the field.
|
||||||
|
if ($context['default']) {
|
||||||
|
drupal_set_message("From $hook(): Default form is true.");
|
||||||
|
}
|
||||||
|
if (\Drupal::state()->get("field_test.$hook")) {
|
||||||
|
$name = $context['items']->getFieldDefinition()->getName();
|
||||||
|
if ($name === 'field_test_text') {
|
||||||
|
$elements['#prefix'] = "From $hook(): prefix on $name parent element.";
|
||||||
|
foreach (Element::children($elements) as $delta => $element) {
|
||||||
|
$elements[$delta]['#description'] = "From $hook(): description on $name child element.";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implements hook_query_TAG_alter() for tag 'efq_table_prefixing_test'.
|
* Implements hook_query_TAG_alter() for tag 'efq_table_prefixing_test'.
|
||||||
*
|
*
|
||||||
|
|
Loading…
Reference in New Issue