diff --git a/core/lib/Drupal/Core/Render/Element/MachineName.php b/core/lib/Drupal/Core/Render/Element/MachineName.php index 0066a91..65f2f59 100644 --- a/core/lib/Drupal/Core/Render/Element/MachineName.php +++ b/core/lib/Drupal/Core/Render/Element/MachineName.php @@ -170,8 +170,24 @@ public static function processMachineName(&$element, FormStateInterface $form_st $key_exists = NULL; $source = NestedArray::getValue($form_state->getCompleteForm(), $element['#machine_name']['source'], $key_exists); if (!$key_exists) { + // Try to find the source element by looking up the parents of the machine + // name element. + $element_parents = $element['#array_parents']; + + // Remove the element itself from the array. + array_pop($element_parents); + $source_parents = array_merge($element_parents, $element['#machine_name']['source']); + $source = NestedArray::getValue($form_state->getCompleteForm(), $source_parents, $key_exists); + } + + if (!$key_exists) { return $element; } + elseif (isset($source_parents)) { + // The source element was found in the form in a different location than + // initially specified so we need to update it. + $element['#machine_name']['source'] = $source_parents; + } $suffix_id = $source['#id'] . '-machine-name-suffix'; $element['#machine_name']['suffix'] = '#' . $suffix_id; diff --git a/core/modules/comment/src/Tests/CommentFieldsTest.php b/core/modules/comment/src/Tests/CommentFieldsTest.php index 408b782..70daea1 100644 --- a/core/modules/comment/src/Tests/CommentFieldsTest.php +++ b/core/modules/comment/src/Tests/CommentFieldsTest.php @@ -156,14 +156,17 @@ public function testCommentFieldCreate() { // Create comment field in account settings. $edit = array( 'new_storage_type' => 'comment', + ); + $this->drupalPostAjaxForm('admin/config/people/accounts/fields/add-field', $edit, 'new_storage_type'); + + // Try to save the comment field without selecting a comment type. + $edit = array( + 'new_storage_type' => 'comment', 'label' => 'User comment', 'field_name' => 'user_comment', ); - $this->drupalPostForm('admin/config/people/accounts/fields/add-field', $edit, 'Save and continue'); + $this->drupalPostForm(NULL, $edit, 'Save and continue'); - // Try to save the comment field without selecting a comment type. - $edit = array(); - $this->drupalPostForm('admin/config/people/accounts/fields/user.user.field_user_comment/storage', $edit, t('Save field settings')); // We should get an error message. $this->assertText(t('An illegal choice has been detected. Please contact the site administrator.')); @@ -176,11 +179,19 @@ public function testCommentFieldCreate() { )); $bundle->save(); + $edit = array( + 'new_storage_type' => 'comment', + ); + $this->drupalPostAjaxForm('admin/config/people/accounts/fields/add-field', $edit, 'new_storage_type'); + // Select a comment type and try to save again. $edit = array( + 'new_storage_type' => 'comment', + 'label' => 'User comment', + 'field_name' => 'user_comment', 'settings[comment_type]' => 'user_comment_type', ); - $this->drupalPostForm('admin/config/people/accounts/fields/user.user.field_user_comment/storage', $edit, t('Save field settings')); + $this->drupalPostForm(NULL, $edit, t('Save and continue')); // We shouldn't get an error message. $this->assertNoText(t('An illegal choice has been detected. Please contact the site administrator.')); } diff --git a/core/modules/content_translation/src/Tests/ContentTranslationSettingsTest.php b/core/modules/content_translation/src/Tests/ContentTranslationSettingsTest.php index f1ddd4a..dc46d69 100644 --- a/core/modules/content_translation/src/Tests/ContentTranslationSettingsTest.php +++ b/core/modules/content_translation/src/Tests/ContentTranslationSettingsTest.php @@ -254,12 +254,13 @@ function testFieldTranslatableSettingsUI() { // At least one field needs to be translatable to enable article for // translation. Create an extra field to be used for this purpose. We use // the UI to test our form alterations. + $this->drupalPostAjaxForm('admin/structure/types/manage/article/fields/add-field', ['new_storage_type' => 'text'], 'new_storage_type'); $edit = array( 'new_storage_type' => 'text', 'label' => 'Test', 'field_name' => 'article_text', ); - $this->drupalPostForm('admin/structure/types/manage/article/fields/add-field', $edit, 'Save and continue'); + $this->drupalPostForm(NULL, $edit, 'Save and continue'); // Tests that field doesn't have translatable setting if bundle is not // translatable. diff --git a/core/modules/field/src/Entity/FieldStorageConfig.php b/core/modules/field/src/Entity/FieldStorageConfig.php index a2117f8..3a0dea7 100644 --- a/core/modules/field/src/Entity/FieldStorageConfig.php +++ b/core/modules/field/src/Entity/FieldStorageConfig.php @@ -245,12 +245,6 @@ class FieldStorageConfig extends ConfigEntityBase implements FieldStorageConfigI */ public function __construct(array $values, $entity_type = 'field_storage_config') { // Check required properties. - if (empty($values['field_name'])) { - throw new FieldException('Attempt to create a field storage without a field name.'); - } - if (!preg_match('/^[_a-z]+[_a-z0-9]*$/', $values['field_name'])) { - throw new FieldException("Attempt to create a field storage {$values['field_name']} with invalid characters. Only lowercase alphanumeric characters and underscores are allowed, and only lowercase letters and underscore are allowed as the first character"); - } if (empty($values['type'])) { throw new FieldException("Attempt to create a field storage {$values['field_name']} with no type."); } @@ -312,6 +306,13 @@ protected function preSaveNew(EntityStorageInterface $storage) { // Assign the ID. $this->id = $this->id(); + // Validate that we have a field name and that it is valid. + if (!$this->getName()) { + throw new FieldException('Attempt to create a field storage without a field name.'); + } + if (!preg_match('/^[_a-z]+[_a-z0-9]*$/', $this->getName())) { + throw new FieldException("Attempt to create a field storage {$this->getName()} with invalid characters. Only lowercase alphanumeric characters and underscores are allowed, and only lowercase letters and underscore are allowed as the first character"); + } // Field name cannot be longer than FieldStorageConfig::NAME_MAX_LENGTH characters. // We use Unicode::strlen() because the DB layer assumes that column widths // are given in characters rather than bytes. @@ -365,6 +366,9 @@ protected function preSaveUpdated(EntityStorageInterface $storage) { $entity_manager = \Drupal::entityManager(); // Some updates are always disallowed. + if ($this->getName() != $this->original->getName()) { + throw new FieldException("Cannot change the field name for an existing field storage."); + } if ($this->getType() != $this->original->getType()) { throw new FieldException("Cannot change the field type for an existing field storage."); } @@ -701,7 +705,7 @@ public function isQueryable() { * TRUE if the field has data for any entity; FALSE otherwise. */ public function hasData() { - return \Drupal::entityManager()->getStorage($this->entity_type)->countFieldData($this, TRUE); + return !$this->isNew() && \Drupal::entityManager()->getStorage($this->entity_type)->countFieldData($this, TRUE); } /** diff --git a/core/modules/field/src/Tests/EntityReference/EntityReferenceAdminTest.php b/core/modules/field/src/Tests/EntityReference/EntityReferenceAdminTest.php index 3376046..26a0e5e 100644 --- a/core/modules/field/src/Tests/EntityReference/EntityReferenceAdminTest.php +++ b/core/modules/field/src/Tests/EntityReference/EntityReferenceAdminTest.php @@ -79,11 +79,7 @@ public function testFieldAdminHandler() { $this->assertOption('edit-new-storage-type', 'field_ui:entity_reference:node'); $this->assertOption('edit-new-storage-type', 'field_ui:entity_reference:user'); - $this->drupalPostForm(NULL, array( - 'label' => 'Test label', - 'field_name' => 'test', - 'new_storage_type' => 'entity_reference', - ), t('Save and continue')); + $this->drupalPostAjaxForm(NULL, ['new_storage_type' => 'entity_reference'], 'new_storage_type'); // Node should be selected by default. $this->assertFieldByName('settings[target_type]', 'node'); @@ -92,7 +88,11 @@ public function testFieldAdminHandler() { $this->assertFieldSelectOptions('settings[target_type]', array_keys(\Drupal::entityManager()->getDefinitions())); // Second step: 'Field settings' form. - $this->drupalPostForm(NULL, array(), t('Save field settings')); + $this->drupalPostForm(NULL, array( + 'label' => 'Test label', + 'field_name' => 'test', + 'new_storage_type' => 'entity_reference', + ), t('Save and continue')); // The base handler should be selected by default. $this->assertFieldByName('settings[handler]', 'default:node'); @@ -230,19 +230,17 @@ public function testFieldAdminHandler() { $this->clickLink(t('Settings')); // Create a test entity reference field. + $this->drupalPostAjaxForm($bundle_path . '/fields/add-field', ['new_storage_type' => 'field_ui:entity_reference:node'], 'new_storage_type'); + $field_name = 'test_entity_ref_field'; $edit = array( 'new_storage_type' => 'field_ui:entity_reference:node', 'label' => 'Test Entity Reference Field', 'field_name' => $field_name, - ); - $this->drupalPostForm($bundle_path . '/fields/add-field', $edit, t('Save and continue')); - - // Set to unlimited. - $edit = array( + // Set to unlimited cardinality. 'cardinality' => FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED, ); - $this->drupalPostForm(NULL, $edit, t('Save field settings')); + $this->drupalPostForm(NULL, $edit, t('Save and continue')); // Add the view to the test field. $edit = array( diff --git a/core/modules/field_ui/src/Form/FieldStorageAddForm.php b/core/modules/field_ui/src/Form/FieldStorageAddForm.php index 5f087a6..064e54f 100644 --- a/core/modules/field_ui/src/Form/FieldStorageAddForm.php +++ b/core/modules/field_ui/src/Form/FieldStorageAddForm.php @@ -7,12 +7,14 @@ namespace Drupal\field_ui\Form; -use Drupal\Core\Config\ConfigFactoryInterface; +use Drupal\Core\Entity\EntityFormBuilderInterface; use Drupal\Core\Entity\Query\QueryFactory; use Drupal\Core\Entity\EntityManagerInterface; use Drupal\Core\Field\FieldTypePluginManagerInterface; use Drupal\Core\Form\FormBase; +use Drupal\Core\Form\FormState; use Drupal\Core\Form\FormStateInterface; +use Drupal\Core\Render\Element; use Drupal\field\Entity\FieldStorageConfig; use Drupal\field\FieldStorageConfigInterface; use Drupal\field_ui\FieldUI; @@ -59,11 +61,11 @@ class FieldStorageAddForm extends FormBase { public $queryFactory; /** - * The configuration factory. + * The entity form builder. * - * @var \Drupal\Core\Config\ConfigFactoryInterface + * @var \Drupal\Core\Entity\EntityFormBuilderInterface */ - protected $configFactory; + protected $entityFormBuilder; /** * Constructs a new FieldStorageAddForm object. @@ -74,14 +76,14 @@ class FieldStorageAddForm extends FormBase { * The field type plugin manager. * @param \Drupal\Core\Entity\Query\QueryFactory $query_factory * The entity query factory. - * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory - * The configuration factory. + * @param \Drupal\Core\Entity\EntityFormBuilderInterface $entity_form_builder + * The entity form builder. */ - public function __construct(EntityManagerInterface $entity_manager, FieldTypePluginManagerInterface $field_type_plugin_manager, QueryFactory $query_factory, ConfigFactoryInterface $config_factory) { + public function __construct(EntityManagerInterface $entity_manager, FieldTypePluginManagerInterface $field_type_plugin_manager, QueryFactory $query_factory, EntityFormBuilderInterface $entity_form_builder) { $this->entityManager = $entity_manager; $this->fieldTypePluginManager = $field_type_plugin_manager; $this->queryFactory = $query_factory; - $this->configFactory = $config_factory; + $this->entityFormBuilder = $entity_form_builder; } /** @@ -99,7 +101,7 @@ public static function create(ContainerInterface $container) { $container->get('entity.manager'), $container->get('plugin.manager.field.field_type'), $container->get('entity.query'), - $container->get('config.factory') + $container->get('entity.form_builder') ); } @@ -135,6 +137,10 @@ public function buildForm(array $form, FormStateInterface $form_state, $entity_t '#title' => $this->t('Add a new field'), '#options' => $field_type_options, '#empty_option' => $this->t('- Select a field type -'), + '#ajax' => array( + 'callback' => '::buildAjaxFieldStorageConfig', + 'wrapper' => 'new-storage-wrapper', + ), ); // Re-use existing field. @@ -160,38 +166,80 @@ public function buildForm(array $form, FormStateInterface $form_state, $entity_t ); } - // Field label and field_name. - $form['new_storage_wrapper'] = array( + // New field storage subform wrapper. + $form['new_storage_wrapper'] = [ '#type' => 'container', - '#states' => array( - '!visible' => array( - ':input[name="new_storage_type"]' => array('value' => ''), + '#attributes' => [ + 'id' => 'new-storage-wrapper', + ], + ]; + + if ($form_state->isRebuilding() && ($new_storage_type = $form_state->getValue('new_storage_type'))) { + // Field label and field_name. + $form['new_storage_wrapper']['label'] = array( + '#type' => 'textfield', + '#title' => $this->t('Label'), + '#size' => 15, + '#weight' => -15, + ); + + $field_prefix = $this->config('field_ui.settings')->get('field_prefix'); + $form['new_storage_wrapper']['field_name'] = array( + '#type' => 'machine_name', + // This field should stay LTR even for RTL languages. + '#field_prefix' => '' . $field_prefix, + '#field_suffix' => '‎', + '#size' => 15, + '#description' => $this->t('A unique machine-readable name containing letters, numbers, and underscores.'), + // Calculate characters depending on the length of the field prefix + // setting. Maximum length is 32. + '#maxlength' => FieldStorageConfig::NAME_MAX_LENGTH - strlen($field_prefix), + '#machine_name' => array( + 'source' => array('label'), + 'exists' => array($this, 'fieldNameExists'), ), - ), - ); - $form['new_storage_wrapper']['label'] = array( - '#type' => 'textfield', - '#title' => $this->t('Label'), - '#size' => 15, - ); + '#required' => FALSE, + '#weight' => -14, + ); - $field_prefix = $this->config('field_ui.settings')->get('field_prefix'); - $form['new_storage_wrapper']['field_name'] = array( - '#type' => 'machine_name', - // This field should stay LTR even for RTL languages. - '#field_prefix' => '' . $field_prefix, - '#field_suffix' => '‎', - '#size' => 15, - '#description' => $this->t('A unique machine-readable name containing letters, numbers, and underscores.'), - // Calculate characters depending on the length of the field prefix - // setting. Maximum length is 32. - '#maxlength' => FieldStorageConfig::NAME_MAX_LENGTH - strlen($field_prefix), - '#machine_name' => array( - 'source' => array('new_storage_wrapper', 'label'), - 'exists' => array($this, 'fieldNameExists'), - ), - '#required' => FALSE, - ); + // Instantiate the field storage config and field config objects that we + // will work with throughout the life-cycle of this form. + $preconfigured_values = $this->getPreconfiguredValues($new_storage_type); + + $field_storage_values = [ + 'entity_type' => $this->entityTypeId, + 'type' => $preconfigured_values['type'], + ] + $preconfigured_values['field_storage']; + $field_storage_config = $this->entityManager->getStorage('field_storage_config')->create($field_storage_values); + $form_state->set('field_storage_config', $field_storage_config); + + $field_values = [ + 'entity_type' => $this->entityTypeId, + 'bundle' => $this->bundle, + 'field_storage' => $field_storage_config, + ] + $preconfigured_values['field']; + $field_config = $this->entityManager->getStorage('field_config')->create($field_values); + $form_state->set('field_config', $field_config); + + $form_state->set('preconfigured_widget_id', $preconfigured_values['widget_id']); + $form_state->set('preconfigured_formatter_id', $preconfigured_values['formatter_id']); + + // Instantiate the field storage config entity form. + $form_object = $this->entityManager->getFormObject('field_storage_config', 'edit'); + $form_object->setEntity($field_storage_config); + + $form_state = (new FormState())->setFormState([ + 'entity_type_id' => $form_state->get('entity_type_id'), + 'bundle' => $form_state->get('bundle'), + 'field_config' => $field_config, + ]); + + $field_storage_form = $form_object->form([], $form_state); + unset($field_storage_form['#process'], $field_storage_form['#after_build']); + $field_storage_form['cardinality_container']['#parents'] = ['new_storage_wrapper']; + + $form['new_storage_wrapper'] += $field_storage_form; + } // Provide a separate label element for the "Re-use existing field" case // and place it outside the $form['add'] wrapper because those elements @@ -231,6 +279,22 @@ public function buildForm(array $form, FormStateInterface $form_state, $entity_t } /** + * Form submission handler for the 'new field type' element. + */ + public function newStorageTypeSubmit($form, FormStateInterface $form_state) { + // @todo Set some property on $form_state to indicate that we need to + // display the field storage config subform. + $form_state->setRebuild(); + } + + /** + * Handles changes to the selected field storage type. + */ + public function buildAjaxFieldStorageConfig(array $form, FormStateInterface $form_state) { + return $form['new_storage_wrapper']; + } + + /** * {@inheritdoc} */ public function validateForm(array &$form, FormStateInterface $form_state) { @@ -261,7 +325,7 @@ public function validateForm(array &$form, FormStateInterface $form_state) { */ protected function validateAddNew(array $form, FormStateInterface $form_state) { // Validate if any information was provided in the 'add new field' case. - if ($form_state->getValue('new_storage_type')) { + if ($form_state->isSubmitted() && $form_state->getValue('new_storage_type')) { // Missing label. if (!$form_state->getValue('label')) { $form_state->setErrorByName('label', $this->t('Add new field: you need to provide a label.')); @@ -279,6 +343,11 @@ protected function validateAddNew(array $form, FormStateInterface $form_state) { $field_name = $this->configFactory->get('field_ui.settings')->get('field_prefix') . $field_name; $form_state->setValueForElement($form['new_storage_wrapper']['field_name'], $field_name); } + + // Also run the field storage config form validation. + $form_object = $this->entityManager->getFormObject('field_storage_config', 'edit'); + $form_object->setEntity($form_state->get('field_storage_config')); + $form_object->validateForm($form['new_storage_wrapper'], $form_state); } } @@ -312,67 +381,37 @@ public function submitForm(array &$form, FormStateInterface $form_state) { // Create new field. if ($values['new_storage_type']) { - $field_storage_values = [ - 'field_name' => $values['field_name'], - 'entity_type' => $this->entityTypeId, - 'type' => $values['new_storage_type'], - 'translatable' => $values['translatable'], - ]; - $field_values = [ - 'field_name' => $values['field_name'], - 'entity_type' => $this->entityTypeId, - 'bundle' => $this->bundle, - 'label' => $values['label'], - // Field translatability should be explicitly enabled by the users. - 'translatable' => FALSE, - ]; - $widget_id = $formatter_id = NULL; - - // Check if we're dealing with a preconfigured field. - if (strpos($field_storage_values['type'], 'field_ui:') !== FALSE) { - list(, $field_type, $option_key) = explode(':', $field_storage_values['type'], 3); - $field_storage_values['type'] = $field_type; - - $field_type_class = $this->fieldTypePluginManager->getDefinition($field_type)['class']; - $field_options = $field_type_class::getPreconfiguredOptions()[$option_key]; - - // Merge in preconfigured field storage options. - if (isset($field_options['field_storage_config'])) { - foreach (array('cardinality', 'settings') as $key) { - if (isset($field_options['field_storage_config'][$key])) { - $field_storage_values[$key] = $field_options['field_storage_config'][$key]; - } - } - } + $field_storage_config = $form_state->get('field_storage_config'); + $field_config = $form_state->get('field_config'); - // Merge in preconfigured field options. - if (isset($field_options['field_config'])) { - foreach (array('required', 'settings') as $key) { - if (isset($field_options['field_config'][$key])) { - $field_values[$key] = $field_options['field_config'][$key]; - } - } - } + $field_storage_config + ->set('field_name', $values['field_name']) + ->set('translatable', $values['translatable']); - $widget_id = isset($field_options['entity_form_display']['type']) ? $field_options['entity_form_display']['type'] : NULL; - $formatter_id = isset($field_options['entity_view_display']['type']) ? $field_options['entity_view_display']['type'] : NULL; - } + $field_config + ->set('field_name', $values['field_name']) + ->set('label', $values['label']) + // Field translatability should be explicitly enabled by the users. + ->set('translatable', FALSE); // Create the field storage and field. try { - $this->entityManager->getStorage('field_storage_config')->create($field_storage_values)->save(); - $field = $this->entityManager->getStorage('field_config')->create($field_values); - $field->save(); + // Allow the field storage edit subform to also set its values. + $form_object = $this->entityManager->getFormObject('field_storage_config', 'edit'); + $form_object->setEntity($field_storage_config); + $form_object->submitForm($form['new_storage_wrapper'], $form_state); + + $field_storage_config = $form_object->getEntity(); + $field_storage_config->save(); + $field_config->save(); - $this->configureEntityFormDisplay($values['field_name'], $widget_id); - $this->configureEntityViewDisplay($values['field_name'], $formatter_id); + $this->configureEntityFormDisplay($values['field_name'], $form_state->get('preconfigured_widget_id')); + $this->configureEntityViewDisplay($values['field_name'], $form_state->get('preconfigured_formatter_id')); - // Always show the field settings step, as the cardinality needs to be - // configured for new fields. + // Always show the field settings step. $route_parameters = array( - 'field_config' => $field->id(), + 'field_config' => $field_config->id(), ) + FieldUI::getRouteBundleParameter($entity_type, $this->bundle); - $destinations[] = array('route_name' => "entity.field_config.{$this->entityTypeId}_storage_edit_form", 'route_parameters' => $route_parameters); $destinations[] = array('route_name' => "entity.field_config.{$this->entityTypeId}_field_edit_form", 'route_parameters' => $route_parameters); $destinations[] = array('route_name' => "entity.{$this->entityTypeId}.field_ui_fields", 'route_parameters' => $route_parameters); @@ -427,6 +466,62 @@ public function submitForm(array &$form, FormStateInterface $form_state) { } /** + * Gets the default properties for the config entities created by this form. + * + * @param string $field_type + * The value selected in the 'new storage type' form element. + * + * @return array + * An array with the following structure: + * - type: (string) The field type plugin ID. + * - field_storage: (array) The default properties of a field storage + * config. + * - field: (array) The default properties of a field config. + * - widget_id: (string) The default widget plugin ID. + * - formatter_id: (string) The default formatter plugin ID. + */ + protected function getPreconfiguredValues($field_type) { + $preconfigured_values = [ + 'type' => $field_type, + 'field_storage' => [], + 'field' => [], + 'widget_id' => NULL, + 'formatter_id' => NULL, + ]; + + if (strpos($field_type, 'field_ui:') !== FALSE) { + list(, $field_type, $option_key) = explode(':', $field_type, 3); + $preconfigured_values['type'] = $field_type; + + $field_type_class = $this->fieldTypePluginManager->getDefinition($field_type)['class']; + $field_options = $field_type_class::getPreconfiguredOptions()[$option_key]; + + // Add pre-configured field storage options. + if (isset($field_options['field_storage_config'])) { + foreach (array('cardinality', 'settings') as $key) { + if (isset($field_options['field_storage_config'][$key])) { + $preconfigured_values['field_storage'][$key] = $field_options['field_storage_config'][$key]; + } + } + } + + // Add pre-configured field options. + if (isset($field_options['field_config'])) { + foreach (array('required', 'settings') as $key) { + if (isset($field_options['field_config'][$key])) { + $preconfigured_values['field'][$key] = $field_options['field_config'][$key]; + } + } + } + + $preconfigured_values['widget_id'] = isset($field_options['entity_form_display']['type']) ? $field_options['entity_form_display']['type'] : NULL; + $preconfigured_values['formatter_id'] = isset($field_options['entity_view_display']['type']) ? $field_options['entity_view_display']['type'] : NULL; + } + + return $preconfigured_values; + } + + /** * Configures the newly created field for the default view and form modes. * * @param string $field_name diff --git a/core/modules/field_ui/src/Form/FieldStorageConfigEditForm.php b/core/modules/field_ui/src/Form/FieldStorageConfigEditForm.php index 4b5b3ca..6511d47 100644 --- a/core/modules/field_ui/src/Form/FieldStorageConfigEditForm.php +++ b/core/modules/field_ui/src/Form/FieldStorageConfigEditForm.php @@ -8,6 +8,8 @@ namespace Drupal\field_ui\Form; use Drupal\Core\Entity\EntityForm; +use Drupal\Core\Field\BaseFieldDefinition; +use Drupal\Core\Field\FieldItemList; use Drupal\Core\Field\FieldStorageDefinitionInterface; use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Routing\RouteMatchInterface; @@ -65,14 +67,18 @@ public function buildForm(array $form, FormStateInterface $form_state, $field_co public function form(array $form, FormStateInterface $form_state) { $form = parent::form($form, $form_state); - $field_label = $form_state->get('field_config')->label(); - $form['#title'] = $field_label; - $form['#prefix'] = '

' . $this->t('These settings apply to the %field field everywhere it is used. These settings impact the way that data is stored in the database and cannot be changed once data has been created.', array('%field' => $field_label)) . '

'; - - // See if data already exists for this field. - // If so, prevent changes to the field settings. - if ($this->entity->hasData()) { - $form['#prefix'] = '
' . $this->t('There is data for this field in the database. The field settings can no longer be changed.') . '
' . $form['#prefix']; + // We cannot use an 'add' entity form operation because this form is also + // used as an AJAX subform of \Drupal\field_ui\Form\FieldStorageAddForm. + if (!$this->entity->isNew()) { + $field_label = $form_state->get('field_config')->label(); + $form['#title'] = $field_label; + $form['#prefix'] = '

' . $this->t('These settings apply to the %field field everywhere it is used. These settings impact the way that data is stored in the database and cannot be changed once data has been created.', array('%field' => $field_label)) . '

'; + + // See if data already exists for this field. + // If so, prevent changes to the field settings. + if ($this->entity->hasData()) { + $form['#prefix'] = '
' . $this->t('There is data for this field in the database. The field settings can no longer be changed.') . '
' . $form['#prefix']; + } } // Add settings provided by the field module. The field module is @@ -90,7 +96,7 @@ public function form(array $form, FormStateInterface $form_state) { 'entity_id' => NULL ); $entity = _field_create_entity_from_ids($ids); - $items = $entity->get($this->entity->getName()); + $items = FieldItemList::createInstance($form_state->get('field_config'), NULL, $entity->getTypedData()); $item = $items->first() ?: $items->appendItem(); $form['settings'] += $item->storageSettingsForm($form, $form_state, $this->entity->hasData()); diff --git a/core/modules/field_ui/src/Tests/FieldUiTestTrait.php b/core/modules/field_ui/src/Tests/FieldUiTestTrait.php index ba70401..d8df88a 100644 --- a/core/modules/field_ui/src/Tests/FieldUiTestTrait.php +++ b/core/modules/field_ui/src/Tests/FieldUiTestTrait.php @@ -45,17 +45,21 @@ public function fieldUIAddNewField($bundle_path, $field_name, $label = NULL, $fi $bundle_path = "$bundle_path/fields/add-field"; } - // First step: 'Add field' page. - $this->drupalPostForm($bundle_path, $initial_edit, t('Save and continue')); - $this->assertRaw(t('These settings apply to the %label field everywhere it is used.', array('%label' => $label)), 'Storage settings page was displayed.'); - // Test Breadcrumbs. - $this->assertLink($label, 0, 'Field label is correct in the breadcrumb of the storage settings page.'); + // First step: 'Add field' page. Get the field storage edit form through + // AJAX by selecting the field type. + $this->drupalPostAjaxForm($bundle_path, ['new_storage_type' => $field_type], 'new_storage_type'); + + // Check that the field storage form is displayed. + $this->assertFieldByName('label', NULL, 'Storage settings form was displayed.'); - // Second step: 'Storage settings' form. - $this->drupalPostForm(NULL, $storage_edit, t('Save field settings')); - $this->assertRaw(t('Updated field %label field settings.', array('%label' => $label)), 'Redirected to field settings page.'); + // Now fill the field storage values and post the form. + $this->drupalPostForm(NULL, $initial_edit + $storage_edit, t('Save and continue')); - // Third step: 'Field settings' form. + // Check that we have been redirected to the field settings page by looking + // up the 'required' form element. + $this->assertFieldByName('required', NULL, 'Redirected to field settings page.'); + + // Second step: 'Field settings' form. $this->drupalPostForm(NULL, $field_edit, t('Save settings')); $this->assertRaw(t('Saved %label configuration.', array('%label' => $label)), 'Redirected to "Manage fields" page.');