Skip to content
Snippets Groups Projects
Commit 6b9f37f4 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
parent 4118ac6c
No related branches found
No related tags found
7 merge requests!7452Issue #1797438. HTML5 validation is preventing form submit and not fully...,!1012Issue #3226887: Hreflang on non-canonical content pages,!789Issue #3210310: Adjust Database API to remove deprecated Drupal 9 code in Drupal 10,!596Issue #3046532: deleting an entity reference field, used in a contextual view, makes the whole site unrecoverable,!496Issue #2463967: Use .user.ini file for PHP settings,!144Issue #2666286: Clean up menu_ui to conform to Drupal coding standards,!16Draft: Resolve #2081585 "History storage"
......@@ -98,6 +98,9 @@ public function testMainProperty() {
foreach ($field_type_manager->getDefinitions() as $plugin_id => $definition) {
$class = $definition['class'];
$property = $class::mainPropertyName();
if ($property === NULL) {
continue;
}
$storage_definition = BaseFieldDefinition::create($plugin_id);
$property_definitions = $class::propertyDefinitions($storage_definition);
$properties = implode(', ', array_keys($property_definitions));
......
......@@ -60,6 +60,7 @@ public function __construct(EntityTypeManagerInterface $entity_type_manager) {
* catch it, and pass it to the value object that JSON:API uses.
*/
public function normalize($field_item, $format = NULL, array $context = []) {
assert($field_item instanceof FieldItemInterface);
/** @var \Drupal\Core\TypedData\TypedDataInterface $property */
$values = [];
$context[CacheableNormalizerInterface::SERIALIZATION_CONTEXT_CACHEABILITY] = new CacheableMetadata();
......@@ -71,7 +72,8 @@ public function normalize($field_item, $format = NULL, array $context = []) {
$values[$property_name] = $this->serializer->normalize($property, $format, $context);
}
// 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 {
$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