diff --git a/core/modules/hal/src/Normalizer/EntityReferenceItemNormalizer.php b/core/modules/hal/src/Normalizer/EntityReferenceItemNormalizer.php index 324e7f9098..671a1eed3b 100644 --- a/core/modules/hal/src/Normalizer/EntityReferenceItemNormalizer.php +++ b/core/modules/hal/src/Normalizer/EntityReferenceItemNormalizer.php @@ -8,12 +8,15 @@ use Drupal\hal\LinkManager\LinkManagerInterface; use Drupal\serialization\EntityResolver\EntityResolverInterface; use Drupal\serialization\EntityResolver\UuidReferenceInterface; +use Drupal\serialization\Normalizer\EntityReferenceFieldItemNormalizerTrait; /** * Converts the Drupal entity reference item object to HAL array structure. */ class EntityReferenceItemNormalizer extends FieldItemNormalizer implements UuidReferenceInterface { + use EntityReferenceFieldItemNormalizerTrait; + /** * The interface or class that this Normalizer supports. * @@ -65,7 +68,11 @@ public function normalize($field_item, $format = NULL, array $context = []) { // If this is not a fieldable entity, let the parent implementation handle // it, only fieldable entities are supported as embedded resources. if (!$this->targetEntityIsFieldable($field_item)) { - return parent::normalize($field_item, $format, $context); + $normalized = parent::normalize($field_item, $format, $context); + + $this->normalizeRootReferenceValue($normalized, $field_item); + + return $normalized; } /** @var $field_item \Drupal\Core\Field\FieldItemInterface */ diff --git a/core/modules/hal/tests/src/Functional/EntityResource/Term/TermHalJsonAnonTest.php b/core/modules/hal/tests/src/Functional/EntityResource/Term/TermHalJsonAnonTest.php index 95514b7fb1..ba08f2402d 100644 --- a/core/modules/hal/tests/src/Functional/EntityResource/Term/TermHalJsonAnonTest.php +++ b/core/modules/hal/tests/src/Functional/EntityResource/Term/TermHalJsonAnonTest.php @@ -53,7 +53,7 @@ protected function getExpectedNormalizedEntity() { $expected_parent_normalization_links = FALSE; $expected_parent_normalization_embedded = FALSE; switch($parent_term_ids) { - case [TermInterface::ROOT_ID]: + case [0]: $expected_parent_normalization_links = [ NULL, ]; @@ -83,7 +83,7 @@ protected function getExpectedNormalizedEntity() { ], ]; break; - case [TermInterface::ROOT_ID, 2]: + case [0, 2]: $expected_parent_normalization_links = [ NULL, [ diff --git a/core/modules/rest/tests/src/Functional/EntityResource/Term/TermResourceTestBase.php b/core/modules/rest/tests/src/Functional/EntityResource/Term/TermResourceTestBase.php index d31155bfa7..02e9a3ba4d 100644 --- a/core/modules/rest/tests/src/Functional/EntityResource/Term/TermResourceTestBase.php +++ b/core/modules/rest/tests/src/Functional/EntityResource/Term/TermResourceTestBase.php @@ -105,10 +105,10 @@ protected function getExpectedNormalizedEntity() { $expected_parent_normalization = FALSE; switch($parent_term_ids) { - case [TermInterface::ROOT_ID]: + case [0]: $expected_parent_normalization = [ [ - 'target_id' => TermInterface::ROOT_ID, + 'target_id' => NULL, ], ]; break; @@ -122,10 +122,10 @@ protected function getExpectedNormalizedEntity() { ], ]; break; - case [TermInterface::ROOT_ID, 2]: + case [0, 2]: $expected_parent_normalization = [ [ - 'target_id' => TermInterface::ROOT_ID, + 'target_id' => NULL, ], [ 'target_id' => 2, @@ -335,13 +335,13 @@ public function testGetTermWithParent(array $parent_term_ids) { public function providerTestGetTermWithParent() { return [ 'root parent: [0] (= no parent)' => [ - [TermInterface::ROOT_ID] + [0] ], 'non-root parent: [2]' => [ [2] ], 'multiple parents: [0,2] (root + non-root parent)' => [ - [TermInterface::ROOT_ID, 2] + [0, 2] ], 'multiple parents: [3,2] (both non-root parents)' => [ [3, 2] diff --git a/core/modules/serialization/src/Normalizer/EntityReferenceFieldItemNormalizer.php b/core/modules/serialization/src/Normalizer/EntityReferenceFieldItemNormalizer.php index ea2e020bf0..2d3c6f012b 100644 --- a/core/modules/serialization/src/Normalizer/EntityReferenceFieldItemNormalizer.php +++ b/core/modules/serialization/src/Normalizer/EntityReferenceFieldItemNormalizer.php @@ -12,6 +12,8 @@ */ class EntityReferenceFieldItemNormalizer extends FieldItemNormalizer { + use EntityReferenceFieldItemNormalizerTrait; + /** * The interface or class that this Normalizer supports. * @@ -42,6 +44,8 @@ public function __construct(EntityRepositoryInterface $entity_repository) { public function normalize($field_item, $format = NULL, array $context = []) { $values = parent::normalize($field_item, $format, $context); + $this->normalizeRootReferenceValue($values, $field_item); + /** @var \Drupal\Core\Entity\EntityInterface $entity */ if ($entity = $field_item->get('entity')->getValue()) { $values['target_type'] = $entity->getEntityTypeId(); @@ -55,6 +59,7 @@ public function normalize($field_item, $format = NULL, array $context = []) { $values['url'] = $url; } } + return $values; } diff --git a/core/modules/serialization/src/Normalizer/EntityReferenceFieldItemNormalizerTrait.php b/core/modules/serialization/src/Normalizer/EntityReferenceFieldItemNormalizerTrait.php index f9b368684b..1acfd70a7a 100644 --- a/core/modules/serialization/src/Normalizer/EntityReferenceFieldItemNormalizerTrait.php +++ b/core/modules/serialization/src/Normalizer/EntityReferenceFieldItemNormalizerTrait.php @@ -2,6 +2,29 @@ namespace Drupal\serialization\Normalizer; -class EntityReferenceFieldItemNormalizerTrait { +use Drupal\Core\Field\Plugin\Field\FieldType\EntityReferenceItem; + +/** + * Converts empty reference values for entity reference items. + */ +trait EntityReferenceFieldItemNormalizerTrait { + + protected function normalizeRootReferenceValue(&$values, EntityReferenceItem $field_item) { + // @todo Generalize for all tree-structured entity types. + if ($this->fieldItemReferencesTaxonomyTerm($field_item) && empty($values['target_id'])) { + $values['target_id'] = NULL; + } + } + + /** + * Determines if a field item references a taxonomy term. + * + * @param \Drupal\Core\Field\Plugin\Field\FieldType\EntityReferenceItem $field_item + * + * @return bool + */ + protected function fieldItemReferencesTaxonomyTerm(EntityReferenceItem $field_item) { + return $field_item->getFieldDefinition()->getSetting('target_type') === 'taxonomy_term'; + } } diff --git a/core/modules/serialization/tests/src/Unit/Normalizer/EntityReferenceFieldItemNormalizerTest.php b/core/modules/serialization/tests/src/Unit/Normalizer/EntityReferenceFieldItemNormalizerTest.php index 5cc6467e8b..c6806f8c31 100644 --- a/core/modules/serialization/tests/src/Unit/Normalizer/EntityReferenceFieldItemNormalizerTest.php +++ b/core/modules/serialization/tests/src/Unit/Normalizer/EntityReferenceFieldItemNormalizerTest.php @@ -139,6 +139,46 @@ public function testNormalize() { $this->assertSame($expected, $normalized); } + /** + * @covers ::normalize + */ + public function testNormalizeWithEmptyTaxonomyTermReference() { + // Override the serializer prophecy from setUp() to return a zero value. + $this->serializer = $this->prophesize(Serializer::class); + // Set up the serializer to return an entity property. + $this->serializer->normalize(Argument::cetera()) + ->willReturn(0); + + $this->normalizer->setSerializer($this->serializer->reveal()); + + $entity_reference = $this->prophesize(TypedDataInterface::class); + $entity_reference->getValue() + ->willReturn(NULL) + ->shouldBeCalled(); + + $field_definition = $this->prophesize(FieldDefinitionInterface::class); + $field_definition->getSetting('target_type') + ->willReturn('taxonomy_term'); + + $this->fieldItem->getFieldDefinition() + ->willReturn($field_definition->reveal()); + + $this->fieldItem->get('entity') + ->willReturn($entity_reference) + ->shouldBeCalled(); + + $this->fieldItem->getProperties(TRUE) + ->willReturn(['target_id' => $this->getTypedDataProperty(FALSE)]) + ->shouldBeCalled(); + + $normalized = $this->normalizer->normalize($this->fieldItem->reveal()); + + $expected = [ + 'target_id' => NULL, + ]; + $this->assertSame($expected, $normalized); + } + /** * @covers ::normalize */ diff --git a/core/modules/taxonomy/src/Entity/Term.php b/core/modules/taxonomy/src/Entity/Term.php index 26fb973482..3a140731c7 100644 --- a/core/modules/taxonomy/src/Entity/Term.php +++ b/core/modules/taxonomy/src/Entity/Term.php @@ -98,7 +98,7 @@ public function preSave(EntityStorageInterface $storage) { parent::preSave($storage); // Terms with no parents are mandatory children of . if (!$this->get('parent')->count()) { - $this->parent->target_id = static::ROOT_ID; + $this->parent->target_id = 0; } } diff --git a/core/modules/taxonomy/src/TermInterface.php b/core/modules/taxonomy/src/TermInterface.php index cfba19d5a5..3087a56ecb 100644 --- a/core/modules/taxonomy/src/TermInterface.php +++ b/core/modules/taxonomy/src/TermInterface.php @@ -10,11 +10,6 @@ */ interface TermInterface extends ContentEntityInterface, EntityChangedInterface { - /** - * Root ID for terms with no parent. - */ - const ROOT_ID = 0; - /** * Gets the term's description. * diff --git a/core/modules/taxonomy/src/TermStorage.php b/core/modules/taxonomy/src/TermStorage.php index 2db0392aea..f0ab848b7e 100644 --- a/core/modules/taxonomy/src/TermStorage.php +++ b/core/modules/taxonomy/src/TermStorage.php @@ -108,8 +108,7 @@ public function loadParents($tid) { * * @return \Drupal\taxonomy\TermInterface[] * The parent taxonomy term entities keyed by term ID. If this term has a - * parent, that item is keyed with ROOT_ID and will have NULL as - * value. + * parent, that item is keyed with 0 and will have NULL as value. * * @internal * @todo Refactor away when TreeInterface is introduced. @@ -119,7 +118,7 @@ protected function getParents(TermInterface $term) { // Cannot use $this->get('parent')->referencedEntities() here because that // strips out the '0' reference. foreach ($term->get('parent') as $item) { - if ($item->target_id == $term::ROOT_ID) { + if ($item->target_id == 0) { // The parent. $parents[0] = NULL; continue; diff --git a/core/modules/taxonomy/src/VocabularyStorage.php b/core/modules/taxonomy/src/VocabularyStorage.php index 0538e71b49..57ed1fca17 100644 --- a/core/modules/taxonomy/src/VocabularyStorage.php +++ b/core/modules/taxonomy/src/VocabularyStorage.php @@ -24,7 +24,7 @@ public function resetCache(array $ids = NULL) { public function getToplevelTids($vids) { $tids = \Drupal::entityQuery('taxonomy_term') ->condition('vid', $vids, 'IN') - ->condition('parent.target_id', TermInterface::ROOT_ID) + ->condition('parent.target_id', 0) ->execute(); return array_values($tids); diff --git a/core/modules/taxonomy/tests/src/Kernel/Migrate/d6/MigrateTaxonomyTermTest.php b/core/modules/taxonomy/tests/src/Kernel/Migrate/d6/MigrateTaxonomyTermTest.php index b6cf73f65c..8e24294754 100644 --- a/core/modules/taxonomy/tests/src/Kernel/Migrate/d6/MigrateTaxonomyTermTest.php +++ b/core/modules/taxonomy/tests/src/Kernel/Migrate/d6/MigrateTaxonomyTermTest.php @@ -93,7 +93,7 @@ public function testTaxonomyTerms() { $this->assertSame($values['vid'], $term->vid->target_id); $this->assertSame((string) $values['weight'], $term->weight->value); if ($values['parent'] === [0]) { - $this->assertSame((int) $term->parent->target_id, TermInterface::ROOT_ID); + $this->assertSame(0, (int) $term->parent->target_id); } else { $parents = [];