You are here

public function EntityResource::addToRelationshipData in Drupal 9

Same name and namespace in other branches
  1. 8 core/modules/jsonapi/src/Controller/EntityResource.php \Drupal\jsonapi\Controller\EntityResource::addToRelationshipData()

Adds a relationship to a to-many relationship.

Parameters

\Drupal\jsonapi\ResourceType\ResourceType $resource_type: The base JSON:API resource type for the request to be served.

\Drupal\Core\Entity\FieldableEntityInterface $entity: The requested entity.

string $related: The related field name.

\Symfony\Component\HttpFoundation\Request $request: The request object.

Return value

\Drupal\jsonapi\ResourceResponse The response.

Throws

\Drupal\jsonapi\Exception\EntityAccessDeniedHttpException Thrown when the current user is not allowed to PATCH the selected field(s).

\Symfony\Component\HttpKernel\Exception\ConflictHttpException Thrown when POSTing to a "to-one" relationship.

\Drupal\Core\Entity\EntityStorageException Thrown when the underlying entity cannot be saved.

\Drupal\jsonapi\Exception\UnprocessableHttpEntityException Thrown when the updated entity does not pass validation.

File

core/modules/jsonapi/src/Controller/EntityResource.php, line 604

Class

EntityResource
Process all entity requests.

Namespace

Drupal\jsonapi\Controller

Code

public function addToRelationshipData(ResourceType $resource_type, FieldableEntityInterface $entity, $related, Request $request) {
  $resource_identifiers = $this
    ->deserialize($resource_type, $request, ResourceIdentifier::class, $related);
  $internal_relationship_field_name = $resource_type
    ->getInternalName($related);

  // According to the specification, you are only allowed to POST to a
  // relationship if it is a to-many relationship.

  /** @var \Drupal\Core\Field\EntityReferenceFieldItemListInterface $field_list */
  $field_list = $entity->{$internal_relationship_field_name};

  /** @var \Drupal\field\Entity\FieldConfig $field_definition */
  $field_definition = $field_list
    ->getFieldDefinition();
  $is_multiple = $field_definition
    ->getFieldStorageDefinition()
    ->isMultiple();
  if (!$is_multiple) {
    throw new ConflictHttpException(sprintf('You can only POST to to-many relationships. %s is a to-one relationship.', $related));
  }
  $original_resource_identifiers = ResourceIdentifier::toResourceIdentifiersWithArityRequired($field_list);
  $new_resource_identifiers = array_udiff(ResourceIdentifier::deduplicate(array_merge($original_resource_identifiers, $resource_identifiers)), $original_resource_identifiers, [
    ResourceIdentifier::class,
    'compare',
  ]);

  // There are no relationships that need to be added so we can exit early.
  if (empty($new_resource_identifiers)) {
    $status = static::relationshipResponseRequiresBody($resource_identifiers, $original_resource_identifiers) ? 200 : 204;
    return $this
      ->getRelationship($resource_type, $entity, $related, $request, $status);
  }
  $main_property_name = $field_definition
    ->getItemDefinition()
    ->getMainPropertyName();
  foreach ($new_resource_identifiers as $new_resource_identifier) {
    $new_field_value = [
      $main_property_name => $this
        ->getEntityFromResourceIdentifier($new_resource_identifier)
        ->id(),
    ];

    // Remove `arity` from the received extra properties, otherwise this
    // will fail field validation.
    $new_field_value += array_diff_key($new_resource_identifier
      ->getMeta(), array_flip([
      ResourceIdentifier::ARITY_KEY,
    ]));
    $field_list
      ->appendItem($new_field_value);
  }
  $this
    ->validate($entity);
  $entity
    ->save();
  $final_resource_identifiers = ResourceIdentifier::toResourceIdentifiersWithArityRequired($field_list);
  $status = static::relationshipResponseRequiresBody($resource_identifiers, $final_resource_identifiers) ? 200 : 204;
  return $this
    ->getRelationship($resource_type, $entity, $related, $request, $status);
}