function FieldResolver::resolveInternalIncludePath

Same name in other branches
  1. 9 core/modules/jsonapi/src/Context/FieldResolver.php \Drupal\jsonapi\Context\FieldResolver::resolveInternalIncludePath()
  2. 10 core/modules/jsonapi/src/Context/FieldResolver.php \Drupal\jsonapi\Context\FieldResolver::resolveInternalIncludePath()
  3. 11.x core/modules/jsonapi/src/Context/FieldResolver.php \Drupal\jsonapi\Context\FieldResolver::resolveInternalIncludePath()

Validates and resolves an include path into its internal possibilities.

Each resource type may define its own external names for its internal field names. As a result, a single external include path may target multiple internal paths.

This can happen when an entity reference field has different allowed entity types *per bundle* (as is possible with comment entities) or when different resource types share an external field name but resolve to different internal fields names.

Example 1: An installation may have three comment types for three different entity types, two of which have a file field and one of which does not. In that case, a path like

field_comments . entity_id . media;

might be resolved to both

field_comments . entity_id . field_audio;

and

field_comments . entity_id . field_image;

.

Example 2: A path of

field_author_profile . account;

might resolve to

field_author_profile . uid;

and

field_author_profile . field_user;

if

field_author_profile;

can relate to two different JSON:API resource types (like `node--profile` and `node--migrated_profile`) which have the external field name

account;

aliased to different internal field names.

Parameters

\Drupal\jsonapi\ResourceType\ResourceType $resource_type: The resource type for which the path should be validated.

string[] $path_parts: The include path as an array of strings. For example, the include query parameter string of

field_tags . uid;

should be given as

[
    'field_tags',
    'uid',
];

.

int $depth: (internal) Used to track recursion depth in order to generate better exception messages.

Return value

string[] The resolved internal include paths.

Throws

\Symfony\Component\HttpKernel\Exception\BadRequestHttpException Thrown if the path contains invalid specifiers.

1 call to FieldResolver::resolveInternalIncludePath()
IncludeResolver::resolveInternalIncludePaths in core/modules/jsonapi/src/IncludeResolver.php
Resolves an array of public field paths.

File

core/modules/jsonapi/src/Context/FieldResolver.php, line 176

Class

FieldResolver
A service that evaluates external path expressions against Drupal fields.

Namespace

Drupal\jsonapi\Context

Code

public static function resolveInternalIncludePath(ResourceType $resource_type, array $path_parts, $depth = 0) {
    $cacheability = (new CacheableMetadata())->addCacheContexts([
        'url.query_args:include',
    ]);
    if (empty($path_parts[0])) {
        throw new CacheableBadRequestHttpException($cacheability, 'Empty include path.');
    }
    $public_field_name = $path_parts[0];
    $internal_field_name = $resource_type->getInternalName($public_field_name);
    $relatable_resource_types = $resource_type->getRelatableResourceTypesByField($public_field_name);
    if (empty($relatable_resource_types)) {
        $message = "`{$public_field_name}` is not a valid relationship field name.";
        if (!empty($possible = implode(', ', array_keys($resource_type->getRelatableResourceTypes())))) {
            $message .= " Possible values: {$possible}.";
        }
        throw new CacheableBadRequestHttpException($cacheability, $message);
    }
    $remaining_parts = array_slice($path_parts, 1);
    if (empty($remaining_parts)) {
        return [
            [
                $internal_field_name,
            ],
        ];
    }
    $exceptions = [];
    $resolved = [];
    foreach ($relatable_resource_types as $relatable_resource_type) {
        try {
            // Each resource type may resolve the path differently and may return
            // multiple possible resolutions.
            $resolved = array_merge($resolved, static::resolveInternalIncludePath($relatable_resource_type, $remaining_parts, $depth + 1));
        } catch (CacheableBadRequestHttpException $e) {
            $exceptions[] = $e;
        }
    }
    if (!empty($exceptions) && count($exceptions) === count($relatable_resource_types)) {
        $previous_messages = implode(' ', array_unique(array_map(function (CacheableBadRequestHttpException $e) {
            return $e->getMessage();
        }, $exceptions)));
        // Only add the full include path on the first level of recursion so that
        // the invalid path phrase isn't repeated at every level.
        throw new CacheableBadRequestHttpException($cacheability, $depth === 0 ? sprintf("`%s` is not a valid include path. {$previous_messages}", implode('.', $path_parts)) : $previous_messages);
    }
    // Remove duplicates by converting to strings and then using array_unique().
    $resolved_as_strings = array_map(function ($possibility) {
        return implode('.', $possibility);
    }, $resolved);
    $resolved_as_strings = array_unique($resolved_as_strings);
    // The resolved internal paths do not include the current field name because
    // resolution happens in a recursive process. Convert back from strings.
    return array_map(function ($possibility) use ($internal_field_name) {
        return array_merge([
            $internal_field_name,
        ], explode('.', $possibility));
    }, $resolved_as_strings);
}

Buggy or inaccurate documentation? Please file an issue. Need support? Need help programming? Connect with the Drupal community.