diff --git a/core/lib/Drupal/Core/Entity/Sql/SqlContentEntityStorage.php b/core/lib/Drupal/Core/Entity/Sql/SqlContentEntityStorage.php index 6920bbb..ea36a87 100644 --- a/core/lib/Drupal/Core/Entity/Sql/SqlContentEntityStorage.php +++ b/core/lib/Drupal/Core/Entity/Sql/SqlContentEntityStorage.php @@ -1077,7 +1077,7 @@ protected function mapToStorageRecord(ContentEntityInterface $entity, $table_nam // @todo Give field types more control over this behavior in // https://drupal.org/node/2232427. if (!$definition->getMainPropertyName() && count($columns) == 1) { - $value = $entity->$field_name->isEmpty() ? array() : $entity->$field_name->first()->getValue(); + $value = ($item = $entity->$field_name->first()) ? $item->getValue() : array(); } else { $value = isset($entity->$field_name->$column_name) ? $entity->$field_name->$column_name : NULL; diff --git a/core/lib/Drupal/Core/Field/BaseFieldDefinition.php b/core/lib/Drupal/Core/Field/BaseFieldDefinition.php index c353be2..9645de2 100644 --- a/core/lib/Drupal/Core/Field/BaseFieldDefinition.php +++ b/core/lib/Drupal/Core/Field/BaseFieldDefinition.php @@ -465,10 +465,18 @@ public function getOptionsProvider($property_name, FieldableEntityInterface $ent // If the field item class implements the interface, proxy it through. if (is_subclass_of($this->getFieldItemClass(), '\Drupal\Core\TypedData\OptionsProviderInterface')) { $items = $entity->get($this->getName()); - // If the field is empty, create an item so that we can use it as the - // "options provider". - // @todo we should clear it afterwards ? - return $items->first() ?: $items->appendItem(); + // We need to return a field item to be used as the "options provider". If + // the current entity already has such an item, use that one. + if ($item = $items->first()) { + return $item; + } + // If not, create a new item that has the entity as the parent, but do not + // modify the actual entity by leaving it there. + else { + $item = $items->appendItem(); + $items->removeItem($item->getName()); + return $item; + } } // @todo: Allow setting custom options provider, see // https://www.drupal.org/node/2002138. diff --git a/core/lib/Drupal/Core/Field/FieldItemList.php b/core/lib/Drupal/Core/Field/FieldItemList.php index bf23cf4..8a903da 100644 --- a/core/lib/Drupal/Core/Field/FieldItemList.php +++ b/core/lib/Drupal/Core/Field/FieldItemList.php @@ -127,14 +127,6 @@ public function setValue($values, $notify = TRUE) { if (!is_array($values) || !is_numeric(current(array_keys($values)))) { $values = array(0 => $values); } - - // Non-numeric keys are not supported. - array_walk($values, function ($value, $index) { - if (!is_numeric($index)) { - throw new \InvalidArgumentException('Unable to set a value with a non-numeric delta in a list.'); - } - }); - // Assign incoming values. Keys are renumbered to ensure 0-based // sequential deltas. If possible, reuse existing items rather than // creating new ones. diff --git a/core/lib/Drupal/Core/TypedData/ListInterface.php b/core/lib/Drupal/Core/TypedData/ListInterface.php index 5c6cf99..2e028fa 100644 --- a/core/lib/Drupal/Core/TypedData/ListInterface.php +++ b/core/lib/Drupal/Core/TypedData/ListInterface.php @@ -80,7 +80,7 @@ public function set($index, $value); public function first(); /** - * Appends a new item to the list + * Appends a new item to the list. * * @param mixed $value * The value of the new item. diff --git a/core/lib/Drupal/Core/TypedData/Plugin/DataType/ItemList.php b/core/lib/Drupal/Core/TypedData/Plugin/DataType/ItemList.php index f5b44c3..ca4131b 100644 --- a/core/lib/Drupal/Core/TypedData/Plugin/DataType/ItemList.php +++ b/core/lib/Drupal/Core/TypedData/Plugin/DataType/ItemList.php @@ -65,12 +65,6 @@ public function setValue($values, $notify = TRUE) { if (!is_array($values)) { throw new \InvalidArgumentException('Cannot set a list with a non-array value.'); } - array_walk($values, function ($value, $index) { - if (!is_numeric($index)) { - throw new \InvalidArgumentException('Unable to set a value with a non-numeric delta in a list.'); - } - }); - // Assign incoming values. Keys are renumbered to ensure 0-based // sequential deltas. If possible, reuse existing items rather than // creating new ones. @@ -113,7 +107,7 @@ public function get($index) { throw new \InvalidArgumentException('Unable to get a value with a non-numeric delta in a list.'); } // Automatically create the first item for computed fields. - if ($index == 0 && !isset($this->list[0]) && ($this->definition->isComputed())) { + if ($index == 0 && !isset($this->list[0]) && $this->definition->isComputed()) { $this->list[0] = $this->createItem(0); } return isset($this->list[$index]) ? $this->list[$index] : NULL; @@ -141,30 +135,32 @@ public function set($index, $value) { * {@inheritdoc} */ public function removeItem($index) { - if (!is_numeric($index)) { - throw new \InvalidArgumentException('Unable to drop a non-numeric delta.'); - } if (isset($this->list) && array_key_exists($index, $this->list)) { // Remove the item, and reassign deltas. unset($this->list[$index]); $this->rekey($index); } + else { + throw new \InvalidArgumentException('Unable to remove item at non-existing index.'); + } return $this; } /** - * @todo + * Renumbers the items in the list. * * @param int $from_index + * Optionally, the index at which to start the renumbering, if it is known + * that items before that can safely be skipped (for example, when removing + * an item at a given index). */ protected function rekey($from_index = 0) { // Re-key the list to maintain consecutive indexes. $this->list = array_values($this->list); - // Each item holds its own delta as a "name". - foreach ($this->list as $delta => $value) { - if ($delta >= $from_index) { - $value->setContext($delta, $this); - } + // Each item holds its own index as a "name", it needs to be updated + // according to the new list indexes. + for ($i = $from_index; $i < count($this->list); $i++) { + $this->list[$i]->setContext($i, $this); } } diff --git a/core/modules/field/src/Entity/FieldStorageConfig.php b/core/modules/field/src/Entity/FieldStorageConfig.php index 99dfe86..b7f9aa1 100644 --- a/core/modules/field/src/Entity/FieldStorageConfig.php +++ b/core/modules/field/src/Entity/FieldStorageConfig.php @@ -602,10 +602,18 @@ public function getOptionsProvider($property_name, FieldableEntityInterface $ent // If the field item class implements the interface, proxy it through. if (is_subclass_of($this->getFieldItemClass(), '\Drupal\Core\TypedData\OptionsProviderInterface')) { $items = $entity->get($this->getName()); - // If the field is empty, create an item so that we can use it as the - // "options provider". - // @todo we should clear it afterwards ? - return $items->first() ?: $items->appendItem(); + // We need to return a field item to be used as the "options provider". If + // the current entity already has such an item, use that one. + if ($item = $items->first()) { + return $item; + } + // If not, create a new item that has the entity as the parent, but do not + // modify the actual entity by leaving it there. + else { + $item = $items->appendItem(); + $items->removeItem($item->getName()); + return $item; + } } // @todo: Allow setting custom options provider, see // https://www.drupal.org/node/2002138. diff --git a/core/modules/field_ui/src/Form/FieldStorageEditForm.php b/core/modules/field_ui/src/Form/FieldStorageEditForm.php index 557ffc8..ff4c76b 100644 --- a/core/modules/field_ui/src/Form/FieldStorageEditForm.php +++ b/core/modules/field_ui/src/Form/FieldStorageEditForm.php @@ -8,7 +8,6 @@ namespace Drupal\field_ui\Form; use Drupal\Core\Entity\EntityManagerInterface; -use Drupal\Core\Field\FieldItemListInterface; use Drupal\Core\Field\FieldStorageDefinitionInterface; use Drupal\Core\Form\FormBase; use Drupal\Core\Form\FormStateInterface; diff --git a/core/modules/hal/src/Normalizer/FieldItemNormalizer.php b/core/modules/hal/src/Normalizer/FieldItemNormalizer.php index b437c1d..53c544c 100644 --- a/core/modules/hal/src/Normalizer/FieldItemNormalizer.php +++ b/core/modules/hal/src/Normalizer/FieldItemNormalizer.php @@ -106,7 +106,7 @@ protected function createTranslatedInstance(FieldItemInterface $item, $langcode) $delta = $item->getName(); unset($items[$delta]); - // instead, create a new item for the entity in the requested language. + // Instead, create a new item for the entity in the requested language. $entity_translation = $item->getEntity()->getTranslation($langcode); $field_name = $item->getFieldDefinition()->getName(); return $entity_translation->get($field_name)->appendItem();