[PP-1] Normalizers must set $entity->_restSubmittedFields for entity POSTing/PATCHing to work

Created on 20 March 2015, over 10 years ago
Updated 10 October 2025, 10 days ago

Problem/Motivation

In #2451397: Entity denormalization fails to retrieve bundle Drupal\serialization\Normalizer\EntityNormalizer::denormalize() had to add _restSubmittedFields to the entity object so that Drupal\rest\Plugin\rest\resource\EntityResource::post() and ::patch() could work at all.

That work-around sets a new, undefined, public-but-for-internal-use-only property on Entity objects:

$entity->_restSubmittedFields

The reason that work-around was introduced: field-level access checking must occur, but only for those fields that are being edited (POSTed or PATCHed, respectively). Doing field access checking on all fields regardless of the field data that is actually being provided in the request body would result in incorrect REST API behavior. (For example: an optional field that is not being created or updated would still be validated, and might result in a validation error. We ran into this exact problem at #2405091: Cannot create user entities - {"error":"Access denied on creating field pass"} , which had a work-around added for it.)

To know which fields to check access for, i.e. the fields that were actually sent (in the request body), we need to pass information from an earlier point in the call stack to a later point in the call stack. When the entity is being denormalized (i.e. from array-of-sent-data to Entity PHP object), we know which fields are present (in that array), and we need to pass that to the REST resource plugin.
In pseudocode:

\Drupal\rest\RequestHandler::handle($request) {
  $method = $request->getMethod();

  $entity_data = $serializer->decode($request->getBody())
  // In here we must set $entity->_restSubmittedFields…
  $entity = $serializer->denormalize($entity_data);

  // So that the method on the plugin that we call here knows which fields were actually sent.
  return $entity_rest_resource_plugin->$method($entity);
}

Consequences

Unfortunately, this has some negative consequences:

  1. Code smell: Key Drupal 8 functionality (CRUD on entities via the REST API) depends on a hack that is introducing global state.
  2. Contributed project soft blocker: each new serialization format's denormalizer must duplicate this hack, or otherwise that format won't work with the REST module. This required work-around means denormalizers MUST be aware of the REST module. (You can see this in core in the HAL module.)

Root cause

The root cause actually does not lie in the Serialization or REST modules. It lies in the Entity/Field/Typed Data API. Of course, the Entity/Field/Typed Data API was originally not designed with a REST API in mind. So the problem is that WSCCI did not pay the full integration cost. IOW: this is WSCCI Initiative technical debt from 2015 that we never paid off. It was rushed in because apparently in 2015, it was still impossible to POST or PATCH entities.

The capability that is missing in the Entity/Field/Typed Data API which REST functionality needs is tracking of changed aka "dirty" fields:

  • For POST requests, we need to know the changes compared to the initial/default field values
  • For PATCH requests, we need to know the changes compared to the stored field values.

Proposed resolution

Either:

Remaining tasks

User interface changes

None.

API changes

None.

Data model changes

None.

Summary of the discussion

A summary is helpful because this issue is quite confusing:

📌 Task
Status

Postponed

Version

11.0 🔥

Component

rest.module

Created by

🇬🇧United Kingdom alexpott 🇪🇺🌍

Live updates comments and jobs are added and updated live.
Sign in to follow issues

Comments & Activities

Not all content is available!

It's likely this issue predates Contrib.social: some issue and comment data are missing.

No activities found.

Production build 0.71.5 2024