Support for Drupal 7 is ending on 5 January 2025—it’s time to migrate to Drupal 10! Learn about the many benefits of Drupal 10 and find migration tools in our resource center.
Problem/Motivation
If you have an entity (a), that references another entity (b) and (b) is a content entity that does not have a uuid
field, then you will get this error when you request entity (a) in the hal_json
format:
InvalidArgumentException: Field uuid is unknown. in Drupal\Core\Entity\ContentEntityBase->getTranslatedField() (line 471 of core/lib/Drupal/Core/Entity/ContentEntityBase.php).
Drupal\Core\Entity\ContentEntityBase->get('uuid') (Line: 84)
Drupal\hal\Normalizer\ContentEntityNormalizer->normalize(Object, 'hal_json', Array) (Line: 128)
Symfony\Component\Serializer\Serializer->normalize(Object, 'hal_json', Array) (Line: 70)
Drupal\hal\Normalizer\EntityReferenceItemNormalizer->normalize(Object, 'hal_json', Array) (Line: 128)
Symfony\Component\Serializer\Serializer->normalize(Object, 'hal_json', Array) (Line: 98)
Drupal\hal\Normalizer\FieldNormalizer->normalizeFieldItems(Object, 'hal_json', Array) (Line: 34)
Drupal\hal\Normalizer\FieldNormalizer->normalize(Object, 'hal_json', Array) (Line: 128)
Symfony\Component\Serializer\Serializer->normalize(Object, 'hal_json', Array) (Line: 96)
Drupal\hal\Normalizer\ContentEntityNormalizer->normalize(Object, 'hal_json', Array) (Line: 128)
Symfony\Component\Serializer\Serializer->normalize(Object, 'hal_json', Array) (Line: 101)
Symfony\Component\Serializer\Serializer->serialize(Object, 'hal_json') (Line: 119)
Drupal\rest\RequestHandler->Drupal\rest\{closure}() (Line: 574)
Drupal\Core\Render\Renderer->executeInRenderContext(Object, Object) (Line: 120)
Drupal\rest\RequestHandler->handle(Object, Object)
call_user_func_array(Array, Array) (Line: 123)
Drupal\Core\EventSubscriber\EarlyRenderingControllerWrapperSubscriber->Drupal\Core\EventSubscriber\{closure}() (Line: 574)
Drupal\Core\Render\Renderer->executeInRenderContext(Object, Object) (Line: 124)
Drupal\Core\EventSubscriber\EarlyRenderingControllerWrapperSubscriber->wrapControllerExecutionInRenderContext(Array, Array) (Line: 97)
Drupal\Core\EventSubscriber\EarlyRenderingControllerWrapperSubscriber->Drupal\Core\EventSubscriber\{closure}()
call_user_func_array(Object, Array) (Line: 144)
Symfony\Component\HttpKernel\HttpKernel->handleRaw(Object, 1) (Line: 64)
Symfony\Component\HttpKernel\HttpKernel->handle(Object, 1, 1) (Line: 57)
Drupal\Core\StackMiddleware\Session->handle(Object, 1, 1) (Line: 47)
Drupal\Core\StackMiddleware\KernelPreHandle->handle(Object, 1, 1) (Line: 47)
Drupal\Core\StackMiddleware\ReverseProxyMiddleware->handle(Object, 1, 1) (Line: 50)
Drupal\Core\StackMiddleware\NegotiationMiddleware->handle(Object, 1, 1) (Line: 31)
Drupal\gc_api\StackMiddleware\NegotiationMiddleware->handle(Object, 1, 1) (Line: 23)
Stack\StackedHttpKernel->handle(Object, 1, 1) (Line: 628)
Drupal\Core\DrupalKernel->handle(Object) (Line: 19)
This is happening because in EntityReferenceItemNormalizer
we have this:
$context['included_fields'] = array('uuid');
// Normalize the target entity.
$embedded = $this->serializer->normalize($target_entity, $format, $context);
and then in ContentEntityNormalizer
we have this:
// If the fields to use were specified, only output those field values.
if (isset($context['included_fields'])) {
$fields = array();
foreach ($context['included_fields'] as $field_name) {
$fields[] = $entity->get($field_name);
}
}
else {
$fields = $entity->getFields();
}
Since entity (b) does not have uuid
then $entity->get('uuid')
throws a fatal error.
Proposed resolution
Check to see if the referenced entity has a uuid field before attempting to access it.
Remaining tasks
Write TestsWrite Patch
User interface changes
None.
API changes
Fixes a major bug in the hal module.
Data model changes
None.
Comment | File | Size | Author |
---|---|---|---|
#31 | 2835576-32-fail.patch | 2.78 KB | larowlan |
#31 | interdiff-83a78b.txt | 1.3 KB | larowlan |
#30 | 2835576-30-fail.patch | 2.69 KB | larowlan |
#30 | interdiff-df2726.txt | 1.27 KB | larowlan |
#28 | 2835576-28-fail.patch | 2.76 KB | larowlan |
Comments
Comment #2
davidwbarratt CreditAttribution: davidwbarratt at Golf Channel commentedA failing test...
Comment #4
davidwbarratt CreditAttribution: davidwbarratt at Golf Channel commentedComment #5
davidwbarratt CreditAttribution: davidwbarratt at Golf Channel commentedComment #6
davidwbarratt CreditAttribution: davidwbarratt at Golf Channel commentedComment #7
davidwbarratt CreditAttribution: davidwbarratt at Golf Channel commentedComment #8
davidwbarratt CreditAttribution: davidwbarratt at Golf Channel commentedComment #9
davidwbarratt CreditAttribution: davidwbarratt at Golf Channel commentedComment #11
Wim LeersAt minimum, this needs tests.
I know that UUIDs are not strictly required, but that was mostly intended for config entities AFAIK… because if content entities don't have UUIDs, they cannot be synced, so they cannot take advantage of automatic syncing and the Workflow initiative for example.
Demoting to
, precisely because it's extremely rare to have content entities that don't have UUIDs.Comment #12
Wim LeersComment #13
shadcn CreditAttribution: shadcn at Chapter Three commented@Wim Leers I'll write the tests.
Comment #14
Wim Leers@arshadcn++
Comment #15
shadcn CreditAttribution: shadcn at Chapter Three commentedSo looking at this again. I think the tests in #2 and #4 combined with the tests we wrote in #2843752: EntityResource: Provide comprehensive test coverage for Item entity should cover this (I saw the same issue when writing the tests for Item entity).
What do you think @Wim Leers?
Comment #16
Wim LeersSorry, lost track of this issue! So much activity in the REST issue queue that this already was on the second issue page for all API-first core modules :)
I think you're right! #2843752: EntityResource: Provide comprehensive test coverage for Item entity already updated
EntityResourceTestBase
to support entity types without UUIDs. So we already have test coverage that proves REST works for entity types without UUIDs. This issue then should be concerned only with was originally reported: the HAL normalization of a content entity that references another content entity and that referenced content entity does not have a UUID.But let's confirm this by uploading the patch in #4 without the fix: that should fail.
In the mean time, here's a review already:
Let's move all of this to
::createEntity()
, that will be much clearer.s/also//
Then the "Set a default value on the field." stuff can be done in a far less confusing way.
And then this can become required instead of optional.
What's key here is that
uuid
is missing. That's what this issue is changing. But… devil's advocate… then how are you supposed to fetch this referenced entity? The whole point is that referenced entities are "embedded" automatically, but the embedding is limited to just the UUID field. But if there's no UUID field… then how are you supposed to find the referenced entity?Should this then fall back to embedding the
ID
field?This is why it's so hugely problematic to have the HAL normalization require UUIDs, and then some entity types not having UUIDs at all!
Comment #17
Wim LeersDone.
(Had to rebase, but git did that all automatically for me :))
Comment #18
Wim LeersNow back to NW for the remarks in #16.
Comment #28
larowlanThis came up as a daily triage target
Here's a re-roll of the test only-patch.
The actual patch doesn't apply anymore because the code there is different, and I suspect the current code-path doesn't contain this bug.
If this passes, we can close as works as designed.
Comment #29
larowlan#2871591: Allow ComplexData in TypedData to specify computed properties that should be exposed in normalization and other contexts was where this was changed
Comment #30
larowlanPHPCS
Comment #31
larowlanMore linting
Comment #33
larowlanOk, so this fails, but differently
The old fail was
Field uuid is unknown
The new fail is just that the expected value differs.
I still think this now works as designed by way of the array_intersect_key added in #2871591: Allow ComplexData in TypedData to specify computed properties that should be exposed in normalization and other contexts
@Wim Leers or @shadcn could you confirm?
Comment #34
SpokjeThe
hal
module has moved out of Drupal Core and into a Contrib Module.Moving this issue to the Contrib Module queue.
Comment #35
SpokjeLet's try that again...
Comment #36
larowlanClosing as a duplicate of #2871591: Allow ComplexData in TypedData to specify computed properties that should be exposed in normalization and other contexts