Skip to content
Snippets Groups Projects
Commit 2dc4a8ac authored by catch's avatar catch
Browse files

Issue #3112229 by mglaman, johnwebdev, jibran, Wim Leers: FieldItemNormalizer...

Issue #3112229 by mglaman, johnwebdev, jibran, Wim Leers: FieldItemNormalizer to not flatten if one property and getMainPropertyName is NULL

(cherry picked from commit 6b9f37f4)
parent fab2dc6a
No related branches found
No related tags found
No related merge requests found
...@@ -98,6 +98,9 @@ public function testMainProperty() { ...@@ -98,6 +98,9 @@ public function testMainProperty() {
foreach ($field_type_manager->getDefinitions() as $plugin_id => $definition) { foreach ($field_type_manager->getDefinitions() as $plugin_id => $definition) {
$class = $definition['class']; $class = $definition['class'];
$property = $class::mainPropertyName(); $property = $class::mainPropertyName();
if ($property === NULL) {
continue;
}
$storage_definition = BaseFieldDefinition::create($plugin_id); $storage_definition = BaseFieldDefinition::create($plugin_id);
$property_definitions = $class::propertyDefinitions($storage_definition); $property_definitions = $class::propertyDefinitions($storage_definition);
$properties = implode(', ', array_keys($property_definitions)); $properties = implode(', ', array_keys($property_definitions));
......
...@@ -60,6 +60,7 @@ public function __construct(EntityTypeManagerInterface $entity_type_manager) { ...@@ -60,6 +60,7 @@ public function __construct(EntityTypeManagerInterface $entity_type_manager) {
* catch it, and pass it to the value object that JSON:API uses. * catch it, and pass it to the value object that JSON:API uses.
*/ */
public function normalize($field_item, $format = NULL, array $context = []) { public function normalize($field_item, $format = NULL, array $context = []) {
assert($field_item instanceof FieldItemInterface);
/** @var \Drupal\Core\TypedData\TypedDataInterface $property */ /** @var \Drupal\Core\TypedData\TypedDataInterface $property */
$values = []; $values = [];
$context[CacheableNormalizerInterface::SERIALIZATION_CONTEXT_CACHEABILITY] = new CacheableMetadata(); $context[CacheableNormalizerInterface::SERIALIZATION_CONTEXT_CACHEABILITY] = new CacheableMetadata();
...@@ -71,7 +72,8 @@ public function normalize($field_item, $format = NULL, array $context = []) { ...@@ -71,7 +72,8 @@ public function normalize($field_item, $format = NULL, array $context = []) {
$values[$property_name] = $this->serializer->normalize($property, $format, $context); $values[$property_name] = $this->serializer->normalize($property, $format, $context);
} }
// Flatten if there is only a single property to normalize. // Flatten if there is only a single property to normalize.
$values = static::rasterizeValueRecursive(count($field_properties) == 1 ? reset($values) : $values); $flatten = count($field_properties) === 1 && $field_item::mainPropertyName() !== NULL;
$values = static::rasterizeValueRecursive($flatten ? reset($values) : $values);
} }
else { else {
$values = $field_item->getValue(); $values = $field_item->getValue();
......
<?php
namespace Drupal\Tests\jsonapi\Kernel\Normalizer;
use Drupal\Core\Field\BaseFieldDefinition;
use Drupal\Core\Field\FieldItemInterface;
use Drupal\entity_test\Entity\EntityTest;
use Drupal\jsonapi\Normalizer\FieldItemNormalizer;
use Drupal\jsonapi\Normalizer\Value\CacheableNormalization;
use Drupal\Tests\jsonapi\Kernel\JsonapiKernelTestBase;
/**
* @coversDefaultClass \Drupal\jsonapi\Normalizer\FieldItemNormalizer
* @group jsonapi
*
* @internal
*/
class FieldItemNormalizerTest extends JsonapiKernelTestBase {
/**
* {@inheritdoc}
*/
protected static $modules = [
'system',
'user',
'link',
'entity_test',
'serialization',
];
/**
* The normalizer.
*
* @var \Drupal\jsonapi\Normalizer\FieldItemNormalizer
*/
private $normalizer;
/**
* {@inheritdoc}
*/
protected function setUp(): void {
parent::setUp();
$etm = $this->container->get('entity_type.manager');
$this->normalizer = new FieldItemNormalizer($etm);
$this->normalizer->setSerializer($this->container->get('jsonapi.serializer'));
$definitions = [];
$definitions['links'] = BaseFieldDefinition::create('link')->setLabel('Links');
$definitions['internal_property_value'] = BaseFieldDefinition::create('single_internal_property_test')->setLabel('Internal property');
$definitions['no_main_property_value'] = BaseFieldDefinition::create('map')->setLabel('No main property');
$this->container->get('state')->set('entity_test.additional_base_field_definitions', $definitions);
$etm->clearCachedDefinitions();
}
/**
* Tests a field item that has no properties.
*
* @covers ::normalize
*/
public function testNormalizeFieldItemWithoutProperties(): void {
$item = $this->prophesize(FieldItemInterface::class);
$item->getProperties(TRUE)->willReturn([]);
$item->getValue()->willReturn('Direct call to getValue');
$result = $this->normalizer->normalize($item->reveal(), 'api_json');
assert($result instanceof CacheableNormalization);
$this->assertSame('Direct call to getValue', $result->getNormalization());
}
/**
* Tests normalizing field item.
*/
public function testNormalizeFieldItem(): void {
$entity = EntityTest::create([
'name' => 'Test entity',
'links' => [
[
'uri' => 'https://www.drupal.org',
'title' => 'Drupal.org',
'options' => [
'query' => 'foo=bar',
],
],
],
'internal_property_value' => [
[
'value' => 'Internal property testing!',
],
],
'no_main_property_value' => [
[
'value' => 'No main property testing!',
],
],
]);
// Verify a field with one property is flattened.
$result = $this->normalizer->normalize($entity->get('name')->first());
assert($result instanceof CacheableNormalization);
$this->assertEquals('Test entity', $result->getNormalization());
// Verify a field with multiple public properties has all of them returned.
$result = $this->normalizer->normalize($entity->get('links')->first());
assert($result instanceof CacheableNormalization);
$this->assertEquals([
'uri' => 'https://www.drupal.org',
'title' => 'Drupal.org',
'options' => [
'query' => 'foo=bar',
],
], $result->getNormalization());
// Verify a field with one public property and one internal only returns the
// public property, and is flattened.
$result = $this->normalizer->normalize($entity->get('internal_property_value')->first());
assert($result instanceof CacheableNormalization);
// Property `internal_value` will not exist.
$this->assertEquals('Internal property testing!', $result->getNormalization());
// Verify a field with one public property but no main property is not
// flattened.
$result = $this->normalizer->normalize($entity->get('no_main_property_value')->first());
assert($result instanceof CacheableNormalization);
$this->assertEquals([
'value' => 'No main property testing!',
], $result->getNormalization());
}
}
<?php
namespace Drupal\entity_test\Plugin\Field\FieldType;
use Drupal\Core\Field\FieldStorageDefinitionInterface;
use Drupal\Core\Field\Plugin\Field\FieldType\StringItem;
use Drupal\Core\StringTranslation\TranslatableMarkup;
use Drupal\Core\TypedData\DataDefinition;
use Drupal\entity_test\TypedData\ComputedString;
/**
* Defines the 'Single Internal Property' entity test field type.
*
* This is based off of the InternalPropertyTestFieldItem test field item type,
* but only adds a single computed property. This tests that fields with a main
* property name and one internal value are flattened.
*
* @see \Drupal\entity_test\Plugin\Field\FieldType\InternalPropertyTestFieldItem
*
* @FieldType(
* id = "single_internal_property_test",
* label = @Translation("Single Internal Property (test)"),
* description = @Translation("A field containing one string, from which one internal string is computed."),
* category = @Translation("Test"),
* default_widget = "string_textfield",
* default_formatter = "string"
* )
*/
class SingleInternalPropertyTestFieldItem extends StringItem {
/**
* {@inheritdoc}
*/
public static function propertyDefinitions(FieldStorageDefinitionInterface $field_definition) {
$properties = parent::propertyDefinitions($field_definition);
// Add a computed property that is internal.
$properties['internal_value'] = DataDefinition::create('string')
->setLabel(new TranslatableMarkup('Computed string, internal property'))
->setComputed(TRUE)
->setClass(ComputedString::class);
return $properties;
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment