summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLee Rowlands2018-07-13 22:15:53 (GMT)
committerLee Rowlands2018-07-13 22:16:28 (GMT)
commit684b538e99dd6dde604eb6aa8f2c0fcc7db8e978 (patch)
tree2204514b5479fd5ecae7efbacbd6092b3944c7c6
parentab1b9a6c00743f8988e4034d9d0e5979ea00c245 (diff)
Issue #2976244 by Sam152, amateescu, Berdir: The BaseFieldOverride entity fails to normalize default values into the "array keyed by delta" format in the same way BaseFieldDefinition does when a callback is specified
(cherry picked from commit 96c4a7e24fd14e11ffc98649180520ee9878f800)
-rw-r--r--core/lib/Drupal/Core/Field/BaseFieldDefinition.php36
-rw-r--r--core/lib/Drupal/Core/Field/FieldConfigBase.php16
-rw-r--r--core/lib/Drupal/Core/Field/FieldInputValueNormalizerTrait.php42
-rw-r--r--core/tests/Drupal/KernelTests/Core/Field/Entity/BaseFieldOverrideTest.php32
-rw-r--r--core/tests/Drupal/Tests/Core/Entity/BaseFieldDefinitionTest.php4
-rw-r--r--core/tests/Drupal/Tests/Core/Field/FieldInputValueNormalizerTraitTest.php100
6 files changed, 185 insertions, 45 deletions
diff --git a/core/lib/Drupal/Core/Field/BaseFieldDefinition.php b/core/lib/Drupal/Core/Field/BaseFieldDefinition.php
index 2aa402f..0947bb1 100644
--- a/core/lib/Drupal/Core/Field/BaseFieldDefinition.php
+++ b/core/lib/Drupal/Core/Field/BaseFieldDefinition.php
@@ -15,6 +15,7 @@ use Drupal\Core\TypedData\OptionsProviderInterface;
class BaseFieldDefinition extends ListDataDefinition implements FieldDefinitionInterface, FieldStorageDefinitionInterface, RequiredFieldStorageDefinitionInterface {
use UnchangingCacheableDependencyTrait;
+ use FieldInputValueNormalizerTrait;
/**
* The field type.
@@ -470,14 +471,7 @@ class BaseFieldDefinition extends ListDataDefinition implements FieldDefinitionI
else {
$value = $this->getDefaultValueLiteral();
}
- // Normalize into the "array keyed by delta" format.
- if (isset($value) && !is_array($value)) {
- $properties = $this->getPropertyNames();
- $property = reset($properties);
- $value = [
- [$property => $value],
- ];
- }
+ $value = $this->normalizeValue($value, $this->getMainPropertyName());
// Allow the field type to process default values.
$field_item_list_class = $this->getClass();
return $field_item_list_class::processDefaultValue($value, $entity, $this);
@@ -522,16 +516,7 @@ class BaseFieldDefinition extends ListDataDefinition implements FieldDefinitionI
* each item being a property/value array (array() for no default value).
*/
public function getInitialValue() {
- $value = isset($this->definition['initial_value']) ? $this->definition['initial_value'] : [];
-
- // Normalize into the "array keyed by delta" format.
- if (isset($value) && !is_array($value)) {
- $value = [
- [$this->getMainPropertyName() => $value],
- ];
- }
-
- return $value;
+ return $this->normalizeValue($this->definition['initial_value'], $this->getMainPropertyName());
}
/**
@@ -556,20 +541,7 @@ class BaseFieldDefinition extends ListDataDefinition implements FieldDefinitionI
throw new FieldException('Multi-value fields can not have an initial value.');
}
- if ($value === NULL) {
- $value = [];
- }
- // Unless the value is an empty array, we may need to transform it.
- if (!is_array($value) || !empty($value)) {
- if (!is_array($value)) {
- $value = [[$this->getMainPropertyName() => $value]];
- }
- elseif (is_array($value) && !is_numeric(array_keys($value)[0])) {
- $value = [0 => $value];
- }
- }
- $this->definition['initial_value'] = $value;
-
+ $this->definition['initial_value'] = $this->normalizeValue($value, $this->getMainPropertyName());
return $this;
}
diff --git a/core/lib/Drupal/Core/Field/FieldConfigBase.php b/core/lib/Drupal/Core/Field/FieldConfigBase.php
index 9bb5228..0d58b65 100644
--- a/core/lib/Drupal/Core/Field/FieldConfigBase.php
+++ b/core/lib/Drupal/Core/Field/FieldConfigBase.php
@@ -12,6 +12,8 @@ use Drupal\Core\Field\TypedData\FieldItemDataDefinition;
*/
abstract class FieldConfigBase extends ConfigEntityBase implements FieldConfigInterface {
+ use FieldInputValueNormalizerTrait;
+
/**
* The field ID.
*
@@ -393,6 +395,7 @@ abstract class FieldConfigBase extends ConfigEntityBase implements FieldConfigIn
// Allow custom default values function.
if ($callback = $this->getDefaultValueCallback()) {
$value = call_user_func($callback, $entity, $this);
+ $value = $this->normalizeValue($value, $this->getFieldStorageDefinition()->getMainPropertyName());
}
else {
$value = $this->getDefaultValueLiteral();
@@ -413,18 +416,7 @@ abstract class FieldConfigBase extends ConfigEntityBase implements FieldConfigIn
* {@inheritdoc}
*/
public function setDefaultValue($value) {
- if (!is_array($value)) {
- if ($value === NULL) {
- $value = [];
- }
- $key = $this->getFieldStorageDefinition()->getPropertyNames()[0];
- // Convert to the multi value format to support fields with a cardinality
- // greater than 1.
- $value = [
- [$key => $value],
- ];
- }
- $this->default_value = $value;
+ $this->default_value = $this->normalizeValue($value, $this->getFieldStorageDefinition()->getMainPropertyName());
return $this;
}
diff --git a/core/lib/Drupal/Core/Field/FieldInputValueNormalizerTrait.php b/core/lib/Drupal/Core/Field/FieldInputValueNormalizerTrait.php
new file mode 100644
index 0000000..f4a7c5d
--- /dev/null
+++ b/core/lib/Drupal/Core/Field/FieldInputValueNormalizerTrait.php
@@ -0,0 +1,42 @@
+<?php
+
+namespace Drupal\Core\Field;
+
+/**
+ * A trait used to assist in the normalization of raw input field values.
+ *
+ * @internal
+ *
+ * @see \Drupal\Core\Field\FieldConfigBase
+ * @see \Drupal\Core\Field\BaseFieldDefinition
+ */
+trait FieldInputValueNormalizerTrait {
+
+ /**
+ * Ensure a field value is transformed into a format keyed by delta.
+ *
+ * @param mixed $value
+ * The raw field value to normalize.
+ * @param string $main_property_name
+ * The main field property name.
+ *
+ * @return array
+ * A field value normalized into a format keyed by delta.
+ */
+ protected static function normalizeValue(&$value, $main_property_name) {
+ if (!isset($value) || $value === NULL) {
+ return [];
+ }
+ if (!is_array($value)) {
+ if ($main_property_name === NULL) {
+ throw new \InvalidArgumentException('A main property is required when normalizing scalar field values.');
+ }
+ return [[$main_property_name => $value]];
+ }
+ if (!empty($value) && !is_numeric(array_keys($value)[0])) {
+ return [0 => $value];
+ }
+ return $value;
+ }
+
+}
diff --git a/core/tests/Drupal/KernelTests/Core/Field/Entity/BaseFieldOverrideTest.php b/core/tests/Drupal/KernelTests/Core/Field/Entity/BaseFieldOverrideTest.php
index 404c2f0..6430cb1 100644
--- a/core/tests/Drupal/KernelTests/Core/Field/Entity/BaseFieldOverrideTest.php
+++ b/core/tests/Drupal/KernelTests/Core/Field/Entity/BaseFieldOverrideTest.php
@@ -5,6 +5,7 @@ namespace Drupal\KernelTests\Core\Field\Entity;
use Drupal\Core\Field\BaseFieldDefinition;
use Drupal\Core\Field\Entity\BaseFieldOverride;
use Drupal\Core\Field\FieldItemList;
+use Drupal\entity_test\Entity\EntityTest;
use Drupal\KernelTests\KernelTestBase;
/**
@@ -18,7 +19,11 @@ class BaseFieldOverrideTest extends KernelTestBase {
*
* @var array
*/
- public static $modules = ['system'];
+ public static $modules = [
+ 'system',
+ 'user',
+ 'entity_test',
+ ];
/**
* {@inheritdoc}
@@ -62,4 +67,29 @@ class BaseFieldOverrideTest extends KernelTestBase {
];
}
+ /**
+ * Test the default value callback.
+ */
+ public function testDefaultValueCallback() {
+ $base_field = BaseFieldDefinition::create('entity_reference')
+ ->setName('Test Field')
+ ->setTargetEntityTypeId('entity_test')
+ ->setDefaultValueCallback(static::class . '::defaultValueCallbackPrimitive');
+ $base_field_override = BaseFieldOverride::createFromBaseFieldDefinition($base_field, 'test_bundle');
+ $entity = EntityTest::create([]);
+
+ $this->assertEquals([['target_id' => 99]], $base_field->getDefaultValue($entity));
+ $this->assertEquals([['target_id' => 99]], $base_field_override->getDefaultValue($entity));
+ }
+
+ /**
+ * A default value callback which returns a primitive value.
+ *
+ * @return int
+ * A primitive default value.
+ */
+ public static function defaultValueCallbackPrimitive() {
+ return 99;
+ }
+
}
diff --git a/core/tests/Drupal/Tests/Core/Entity/BaseFieldDefinitionTest.php b/core/tests/Drupal/Tests/Core/Entity/BaseFieldDefinitionTest.php
index eeba7e5..49b80dc 100644
--- a/core/tests/Drupal/Tests/Core/Entity/BaseFieldDefinitionTest.php
+++ b/core/tests/Drupal/Tests/Core/Entity/BaseFieldDefinitionTest.php
@@ -4,7 +4,9 @@ namespace Drupal\Tests\Core\Entity;
use Drupal\Core\DependencyInjection\ContainerBuilder;
use Drupal\Core\Field\BaseFieldDefinition;
+use Drupal\Core\Field\FieldItemBase;
use Drupal\Core\Field\FieldStorageDefinitionInterface;
+use Drupal\Core\TypedData\DataDefinition;
use Drupal\Tests\UnitTestCase;
/**
@@ -166,6 +168,7 @@ class BaseFieldDefinitionTest extends UnitTestCase {
// Set the field item list class to be used to avoid requiring the typed
// data manager to retrieve it.
$definition->setClass('Drupal\Core\Field\FieldItemList');
+ $definition->setItemDefinition(DataDefinition::createFromDataType('string')->setClass(FieldItemBase::class));
$this->assertEquals($expected_default_value, $definition->getDefaultValue($entity));
$data_definition = $this->getMockBuilder('Drupal\Core\TypedData\DataDefinition')
@@ -201,6 +204,7 @@ class BaseFieldDefinitionTest extends UnitTestCase {
*/
public function testFieldInitialValue() {
$definition = BaseFieldDefinition::create($this->fieldType);
+ $definition->setItemDefinition(DataDefinition::createFromDataType('string')->setClass(FieldItemBase::class));
$default_value = [
'value' => $this->randomMachineName(),
];
diff --git a/core/tests/Drupal/Tests/Core/Field/FieldInputValueNormalizerTraitTest.php b/core/tests/Drupal/Tests/Core/Field/FieldInputValueNormalizerTraitTest.php
new file mode 100644
index 0000000..2d85b3a
--- /dev/null
+++ b/core/tests/Drupal/Tests/Core/Field/FieldInputValueNormalizerTraitTest.php
@@ -0,0 +1,100 @@
+<?php
+
+namespace Drupal\Tests\Core\Field;
+
+use Drupal\Core\Field\FieldInputValueNormalizerTrait;
+use Drupal\Tests\UnitTestCase;
+
+/**
+ * @coversDefaultClass \Drupal\Core\Field\FieldInputValueNormalizerTrait
+ * @group Field
+ */
+class FieldInputValueNormalizerTraitTest extends UnitTestCase {
+
+ use FieldInputValueNormalizerTrait;
+
+ /**
+ * @dataProvider keyValueByDeltaTestCases
+ * @covers ::normalizeValue
+ */
+ public function testKeyValueByDelta($input_value, $expected_value, $main_property_name = 'value') {
+ $this->assertEquals($expected_value, $this->normalizeValue($input_value, $main_property_name));
+ }
+
+ /**
+ * Test cases for ::testKeyValueByDelta.
+ */
+ public function keyValueByDeltaTestCases() {
+ return [
+ 'Integer' => [
+ 1,
+ [['value' => 1]],
+ ],
+ 'Falsey integer' => [
+ 0,
+ [['value' => 0]],
+ ],
+ 'String' => [
+ 'foo',
+ [['value' => 'foo']],
+ ],
+ 'Empty string' => [
+ '',
+ [['value' => '']],
+ ],
+ 'Null' => [
+ NULL,
+ [],
+ ],
+ 'Empty field value' => [
+ [],
+ [],
+ ],
+ 'Single delta' => [
+ ['value' => 'foo'],
+ [['value' => 'foo']],
+ ],
+ 'Keyed delta' => [
+ [['value' => 'foo']],
+ [['value' => 'foo']],
+ ],
+ 'Multiple keyed deltas' => [
+ [['value' => 'foo'], ['value' => 'bar']],
+ [['value' => 'foo'], ['value' => 'bar']],
+ ],
+ 'No main property with keyed delta' => [
+ [['foo' => 'bar']],
+ [['foo' => 'bar']],
+ NULL,
+ ],
+ 'No main property with single delta' => [
+ ['foo' => 'bar'],
+ [['foo' => 'bar']],
+ NULL,
+ ],
+ 'No main property with empty array' => [
+ [],
+ [],
+ NULL,
+ ],
+ ];
+ }
+
+ /**
+ * @covers ::normalizeValue
+ */
+ public function testScalarWithNoMainProperty() {
+ $this->setExpectedException(\InvalidArgumentException::class, 'A main property is required when normalizing scalar field values.');
+ $value = 'foo';
+ $this->normalizeValue($value, NULL);
+ }
+
+ /**
+ * @covers ::normalizeValue
+ */
+ public function testKeyValueByDeltaUndefinedVariables() {
+ $this->assertEquals([], $this->normalizeValue($undefined_variable, 'value'));
+ $this->assertEquals([], $this->normalizeValue($undefined_variable['undefined_key'], 'value'));
+ }
+
+}