Unverified Commit 96c4a7e2 authored by larowlan's avatar larowlan

Issue #2976244 by Sam152, amateescu, Berdir: The BaseFieldOverride entity...

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
parent ac310db4
......@@ -15,6 +15,7 @@
class BaseFieldDefinition extends ListDataDefinition implements FieldDefinitionInterface, FieldStorageDefinitionInterface, RequiredFieldStorageDefinitionInterface {
use UnchangingCacheableDependencyTrait;
use FieldInputValueNormalizerTrait;
/**
* The field type.
......@@ -470,14 +471,7 @@ public function getDefaultValue(FieldableEntityInterface $entity) {
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 @@ public function setDefaultValueCallback($callback) {
* 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 @@ public function setInitialValue($value) {
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;
}
......
......@@ -12,6 +12,8 @@
*/
abstract class FieldConfigBase extends ConfigEntityBase implements FieldConfigInterface {
use FieldInputValueNormalizerTrait;
/**
* The field ID.
*
......@@ -393,6 +395,7 @@ public function getDefaultValue(FieldableEntityInterface $entity) {
// 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 @@ public function getDefaultValueLiteral() {
* {@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;
}
......
<?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;
}
}
......@@ -5,6 +5,7 @@
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 @@ public function getClassTestCases() {
];
}
/**
* 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;
}
}
......@@ -4,7 +4,9 @@
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 @@ public function testFieldDefaultValue() {
// 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 @@ public function testFieldDefaultValue() {
*/
public function testFieldInitialValue() {
$definition = BaseFieldDefinition::create($this->fieldType);
$definition->setItemDefinition(DataDefinition::createFromDataType('string')->setClass(FieldItemBase::class));
$default_value = [
'value' => $this->randomMachineName(),
];
......
<?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'));
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment