diff --git a/core/modules/field/src/Entity/FieldConfig.php b/core/modules/field/src/Entity/FieldConfig.php index f8a976a..70b03b6 100644 --- a/core/modules/field/src/Entity/FieldConfig.php +++ b/core/modules/field/src/Entity/FieldConfig.php @@ -215,11 +215,12 @@ public static function postDelete(EntityStorageInterface $storage, array $fields return; } - // Delete field storages that have no more fields. + // Delete field storages that have no more fields if the field storage is + // deletable. $storages_to_delete = array(); foreach ($fields as $field) { $storage_definition = $field->getFieldStorageDefinition(); - if (!$field->deleted && empty($field->noFieldDelete) && !$field->isUninstalling() && $storage_definition->isDeleteable()) { + if (!$field->deleted && empty($field->noFieldDelete) && !$field->isUninstalling() && $storage_definition->isDeletable()) { // Key by field UUID to avoid deleting the same storage twice. $storages_to_delete[$storage_definition->uuid()] = $storage_definition; } diff --git a/core/modules/field/src/Entity/FieldStorageConfig.php b/core/modules/field/src/Entity/FieldStorageConfig.php index 48c8068..a889bb7 100644 --- a/core/modules/field/src/Entity/FieldStorageConfig.php +++ b/core/modules/field/src/Entity/FieldStorageConfig.php @@ -134,8 +134,14 @@ class FieldStorageConfig extends ConfigEntityBase implements FieldStorageConfigI public $locked = FALSE; /** - * Flag indicating whether the field storage should be removed when there are - * no fields. + * Flag indicating whether the field storage should be deleted when orphaned. + * + * By default field storages are removed when there are no remaining fields + * using them. If multiple modules provide bundles which need to use the same + * field storage then setting this to TRUE will preserve the field storage + * regardless of what happens to the bundles. The classic use case for this + * is node body field storage since Book, Forum, the Standard profile and + * bundle (node type) creation through the UI all use same field storage. * * @var bool */ @@ -744,7 +750,7 @@ public static function loadByName($entity_type_id, $field_name) { /** * {@inheritdoc} */ - public function isDeleteable() { + public function isDeletable() { // The field storage is not deleted, is configured to be removed when there // are no fields and the field storage has no bundles. return !$this->deleted && !$this->persist_with_no_fields && count($this->getBundles()) == 0; diff --git a/core/modules/field/src/FieldStorageConfigInterface.php b/core/modules/field/src/FieldStorageConfigInterface.php index 45459bb..de0406b 100644 --- a/core/modules/field/src/FieldStorageConfigInterface.php +++ b/core/modules/field/src/FieldStorageConfigInterface.php @@ -37,6 +37,6 @@ public function isLocked(); * @return bool * TRUE if the field storage can be deleted. */ - public function isDeleteable(); + public function isDeletable(); } diff --git a/core/modules/field_ui/src/FieldOverview.php b/core/modules/field_ui/src/FieldOverview.php index 5810cdf..3c6cf04 100644 --- a/core/modules/field_ui/src/FieldOverview.php +++ b/core/modules/field_ui/src/FieldOverview.php @@ -16,6 +16,7 @@ use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Render\Element; use Drupal\Core\Url; +use Drupal\field\FieldStorageConfigInterface; use Drupal\field_ui\OverviewBase; use Symfony\Component\DependencyInjection\ContainerInterface; use Drupal\field\Entity\FieldStorageConfig; @@ -218,8 +219,8 @@ public function buildForm(array $form, FormStateInterface $form_state, $entity_t ); } - // Additional row: re-use existing field. - $existing_fields = $this->getExistingFieldOptions(); + // Additional row: re-use existing field storages. + $existing_fields = $this->getExistingFieldStorageOptions(); if ($existing_fields) { // Build list of options. $existing_field_options = array(); @@ -485,22 +486,24 @@ public function submitForm(array &$form, FormStateInterface $form_state) { } /** - * Returns an array of existing fields to be added to a bundle. + * Returns an array of existing field storages that can be added to a bundle. * * @return array - * An array of existing fields keyed by field name. + * An array of existing field storages keyed by name. */ - protected function getExistingFieldOptions() { + protected function getExistingFieldStorageOptions() { $options = array(); // Load the field_storages and build the list of options. $field_types = $this->fieldTypeManager->getDefinitions(); - foreach ($this->entityManager->getStorage('field_storage_config')->loadByProperties(array('entity_type' => $this->entity_type)) as $field_storage) { + foreach ($this->entityManager->getFieldStorageDefinitions($this->entity_type) as $field_storage) { // Do not show: - // - locked field_storages, + // - non-configurable field storages. + // - locked field_storages. // - field_storages that should not be added via user interface. - // - where the field is already on the bundle. + // - field_storages that already have a field in the bundle. $field_type = $field_storage->getType(); - if (!$field_storage->isLocked() + if ($field_storage instanceof FieldStorageConfigInterface + && !$field_storage->isLocked() && empty($field_types[$field_type]['no_ui']) && !in_array($this->bundle, $field_storage->getBundles(), TRUE)) { $options[$field_storage->getName()] = array( diff --git a/core/modules/field_ui/src/Tests/EntityFormDisplayTest.php b/core/modules/field_ui/src/Tests/EntityFormDisplayTest.php index f9a4850..057d575 100644 --- a/core/modules/field_ui/src/Tests/EntityFormDisplayTest.php +++ b/core/modules/field_ui/src/Tests/EntityFormDisplayTest.php @@ -9,7 +9,6 @@ use Drupal\Core\Entity\Entity\EntityFormMode; use Drupal\simpletest\KernelTestBase; -use Drupal\system\Tests\Entity\EntityUnitTestBase; /** * Tests the entity display configuration entities. diff --git a/core/modules/field_ui/src/Tests/ManageFieldsTest.php b/core/modules/field_ui/src/Tests/ManageFieldsTest.php index 7b92b78..be5226d 100644 --- a/core/modules/field_ui/src/Tests/ManageFieldsTest.php +++ b/core/modules/field_ui/src/Tests/ManageFieldsTest.php @@ -12,7 +12,6 @@ use Drupal\Core\Language\LanguageInterface; use Drupal\field\Entity\FieldConfig; use Drupal\field\Entity\FieldStorageConfig; -use Drupal\node\Entity\NodeType; /** * Tests the Field UI "Manage fields" screen. @@ -73,7 +72,7 @@ function testCRUDFields() { $this->cardinalitySettings(); $this->fieldListAdminPage(); $this->deleteField(); - $this->addingPersistentFieldStorage(); + $this->addPersistentFieldStorage(); } /** @@ -230,7 +229,7 @@ protected function deleteField() { /** * Tests that persistent field storage appears in the field UI. */ - protected function addingPersistentFieldStorage() { + protected function addPersistentFieldStorage() { $field_storage = FieldStorageConfig::loadByName('node', $this->field_name); // Persist the field storage even if there are no fields. $field_storage->set('persist_with_no_fields', TRUE)->save(); @@ -243,7 +242,7 @@ protected function addingPersistentFieldStorage() { // Check "Re-use existing field" appears. $this->drupalGet('admin/structure/types/manage/page/fields'); $this->assertRaw(t('Re-use existing field'), '"Re-use existing field" was found.'); - // Add a new field based on an existing field. + // Add a new field for the orphaned storage. $edit = array( 'fields[_add_existing_field][label]' => $this->randomMachineName(), 'fields[_add_existing_field][field_name]' => $this->field_name,