You are here

protected function DocumentExtractor::handleRelationships in JSON:API Resources 8

Builds the relationships for the resource object.

Parameters

array $data: Multi-dimensional array containing the decoded data.

Return value

array The relationships array.

1 call to DocumentExtractor::handleRelationships()
DocumentExtractor::extractResourceObjectFromRequest in src/Unstable/DocumentExtractor.php
Decodes and builds a resource object from a request body.

File

src/Unstable/DocumentExtractor.php, line 216

Class

DocumentExtractor
Document extractor for requests.

Namespace

Drupal\jsonapi_resources\Unstable

Code

protected function handleRelationships(array $data) : array {

  // Turn all single object relationship data fields into an array of objects.
  $relationships = array_map(function ($relationship) {
    if (isset($relationship['data']['type']) && isset($relationship['data']['id'])) {
      return [
        'data' => [
          $relationship['data'],
        ],
      ];
    }
    else {
      return $relationship;
    }
  }, $data['relationships']);
  $result = [];

  // Get an array of ids for every relationship.
  foreach ($relationships as $relationship_field => $relationship) {
    if (empty($relationship['data'])) {
      return [];
    }
    if (empty($relationship['data'][0]['id'])) {
      throw new BadRequestHttpException('No ID specified for related resource');
    }
    $id_list = array_column($relationship['data'], 'id');
    if (empty($relationship['data'][0]['type'])) {
      throw new BadRequestHttpException('No type specified for related resource');
    }
    if (!($resource_type = $this->resourceTypeRepository
      ->getByTypeName($relationship['data'][0]['type']))) {
      throw new BadRequestHttpException(sprintf('Invalid type specified for related resource: "%s"', $relationship['data'][0]['type']));
    }
    $entity_type_id = $resource_type
      ->getEntityTypeId();
    try {
      $entity_storage = $this->entityTypeManager
        ->getStorage($entity_type_id);
    } catch (PluginNotFoundException $e) {
      throw new BadRequestHttpException(sprintf('Invalid type specified for related resource: "%s"', $relationship['data'][0]['type']));
    }

    // In order to maintain the order ($delta) of the relationships, we need
    // to load the entities and create a mapping between id and uuid.
    $uuid_key = $this->entityTypeManager
      ->getDefinition($entity_type_id)
      ->getKey('uuid');
    $related_entities = array_values($entity_storage
      ->loadByProperties([
      $uuid_key => $id_list,
    ]));
    $map = [];
    foreach ($related_entities as $related_entity) {
      $map[$related_entity
        ->uuid()] = $related_entity
        ->id();
    }

    // $id_list has the correct order of uuids. We stitch this together with
    // $map which contains loaded entities, and then bring in the correct
    // meta values from the relationship, whose deltas match with $id_list.
    $canonical_ids = [];
    foreach ($id_list as $delta => $uuid) {
      if (!isset($map[$uuid])) {

        // @see \Drupal\jsonapi\Normalizer\EntityReferenceFieldNormalizer::normalize()
        if ($uuid === 'virtual') {
          continue;
        }
        throw new NotFoundHttpException(sprintf('The resource identified by `%s:%s` (given as a relationship item) could not be found.', $relationship['data'][$delta]['type'], $uuid));
      }
      $reference_item = [
        'target_id' => $map[$uuid],
      ];
      if (isset($relationship['data'][$delta]['meta'])) {
        $reference_item += $relationship['data'][$delta]['meta'];
      }
      $canonical_ids[] = $reference_item;
    }
    $result[$relationship_field] = $canonical_ids;
  }
  return $result;
}