You are here

class Relationship in Drupal 9

Same name and namespace in other branches
  1. 8 core/modules/jsonapi/src/JsonApiResource/Relationship.php \Drupal\jsonapi\JsonApiResource\Relationship

Represents references from one resource object to other resource object(s).

@internal JSON:API maintains no PHP API since its API is the HTTP API. This class may change at any time and this will break any dependencies on it.

Hierarchy

Expanded class hierarchy of Relationship

See also

https://www.drupal.org/project/drupal/issues/3032787

jsonapi.api.php

4 files declare their use of Relationship
EntityResource.php in core/modules/jsonapi/src/Controller/EntityResource.php
RelationshipNormalizer.php in core/modules/jsonapi/src/Normalizer/RelationshipNormalizer.php
RelationshipNormalizerTest.php in core/modules/jsonapi/tests/src/Kernel/Normalizer/RelationshipNormalizerTest.php
ResourceObjectNormalizer.php in core/modules/jsonapi/src/Normalizer/ResourceObjectNormalizer.php
5 string references to 'Relationship'
ConfigHandler::buildForm in core/modules/views_ui/src/Form/Ajax/ConfigHandler.php
Form constructor.
EntityResourceTest::setUp in core/modules/jsonapi/tests/src/Kernel/Controller/EntityResourceTest.php
RowPluginBase::buildOptionsForm in core/modules/views/src/Plugin/views/row/RowPluginBase.php
Provide a form for setting options.
views.data_types.schema.yml in core/modules/views/config/schema/views.data_types.schema.yml
core/modules/views/config/schema/views.data_types.schema.yml
Views::getHandlerTypes in core/modules/views/src/Views.php
Provide a list of views handler types used in a view, with some information about them.

File

core/modules/jsonapi/src/JsonApiResource/Relationship.php, line 22

Namespace

Drupal\jsonapi\JsonApiResource
View source
class Relationship implements TopLevelDataInterface {

  /**
   * The context resource object of the relationship.
   *
   * A relationship object represents references from a resource object in
   * which it’s defined to other resource objects. Respectively, the "context"
   * of the relationship and the "target(s)" of the relationship.
   *
   * A relationship object's context either comes from the resource object that
   * contains it or, in the case that the relationship object is accessed
   * directly via a relationship URL, from its `self` URL, which should identify
   * the resource to which it belongs.
   *
   * @var \Drupal\jsonapi\JsonApiResource\ResourceObject
   *
   * @see https://jsonapi.org/format/#document-resource-object-relationships
   * @see https://jsonapi.org/recommendations/#urls-relationships
   */
  protected $context;

  /**
   * The data of the relationship object.
   *
   * @var \Drupal\jsonapi\JsonApiResource\RelationshipData
   */
  protected $data;

  /**
   * The relationship's public field name.
   *
   * @var string
   */
  protected $fieldName;

  /**
   * The relationship object's links.
   *
   * @var \Drupal\jsonapi\JsonApiResource\LinkCollection
   */
  protected $links;

  /**
   * The relationship object's meta member.
   *
   * @var array
   */
  protected $meta;

  /**
   * Relationship constructor.
   *
   * This constructor is protected by design. To create a new relationship, use
   * static::createFromEntityReferenceField().
   *
   * @param string $public_field_name
   *   The public field name of the relationship field.
   * @param \Drupal\jsonapi\JsonApiResource\RelationshipData $data
   *   The relationship data.
   * @param \Drupal\jsonapi\JsonApiResource\LinkCollection $links
   *   Any links for the resource object, if a `self` link is not
   *   provided, one will be automatically added if the resource is locatable
   *   and is not internal.
   * @param array $meta
   *   Any relationship metadata.
   * @param \Drupal\jsonapi\JsonApiResource\ResourceObject $context
   *   The relationship's context resource object. Use the
   *   self::withContext() method to establish a context.
   *
   * @see \Drupal\jsonapi\JsonApiResource\Relationship::createFromEntityReferenceField()
   */
  protected function __construct($public_field_name, RelationshipData $data, LinkCollection $links, array $meta, ResourceObject $context) {
    $this->fieldName = $public_field_name;
    $this->data = $data;
    $this->links = $links
      ->withContext($this);
    $this->meta = $meta;
    $this->context = $context;
  }

  /**
   * Creates a new Relationship from an entity reference field.
   *
   * @param \Drupal\jsonapi\JsonApiResource\ResourceObject $context
   *   The context resource object of the relationship to be created.
   * @param \Drupal\Core\Field\EntityReferenceFieldItemListInterface $field
   *   The entity reference field from which to create the relationship.
   * @param \Drupal\jsonapi\JsonApiResource\LinkCollection $links
   *   (optional) Any extra links for the Relationship, if a `self` link is not
   *   provided, one will be automatically added if the context resource is
   *   locatable and is not internal.
   * @param array $meta
   *   (optional) Any relationship metadata.
   *
   * @return static
   *   An instantiated relationship object.
   */
  public static function createFromEntityReferenceField(ResourceObject $context, EntityReferenceFieldItemListInterface $field, LinkCollection $links = NULL, array $meta = []) {
    $context_resource_type = $context
      ->getResourceType();
    $resource_field = $context_resource_type
      ->getFieldByInternalName($field
      ->getName());
    return new static($resource_field
      ->getPublicName(), new RelationshipData(ResourceIdentifier::toResourceIdentifiers($field), $resource_field
      ->hasOne() ? 1 : -1), static::buildLinkCollectionFromEntityReferenceField($context, $field, $links ?: new LinkCollection([])), $meta, $context);
  }

  /**
   * Gets context resource object of the relationship.
   *
   * @return \Drupal\jsonapi\JsonApiResource\ResourceObject
   *   The context ResourceObject.
   *
   * @see \Drupal\jsonapi\JsonApiResource\Relationship::$context
   */
  public function getContext() {
    return $this->context;
  }

  /**
   * Gets the relationship object's public field name.
   *
   * @return string
   *   The relationship's field name.
   */
  public function getFieldName() {
    return $this->fieldName;
  }

  /**
   * Gets the relationship object's data.
   *
   * @return \Drupal\jsonapi\JsonApiResource\RelationshipData
   *   The relationship's data.
   */
  public function getData() {
    return $this->data;
  }

  /**
   * Gets the relationship object's links.
   *
   * @return \Drupal\jsonapi\JsonApiResource\LinkCollection
   *   The relationship object's links.
   */
  public function getLinks() {
    return $this->links;
  }

  /**
   * Gets the relationship object's metadata.
   *
   * @return array
   *   The relationship object's metadata.
   */
  public function getMeta() {
    return $this->meta;
  }

  /**
   * {@inheritdoc}
   */
  public function getOmissions() {
    return new OmittedData([]);
  }

  /**
   * {@inheritdoc}
   */
  public function getMergedLinks(LinkCollection $top_level_links) {

    // When directly fetching a relationship object, the relationship object's
    // links become the top-level object's links unless they've been
    // overridden. Overrides are especially important for the `self` link, which
    // must match the link that generated the response. For example, the
    // top-level `self` link might have an `include` query parameter that would
    // be lost otherwise.
    // See https://jsonapi.org/format/#fetching-relationships-responses-200 and
    // https://jsonapi.org/format/#document-top-level.
    return LinkCollection::merge($top_level_links, $this
      ->getLinks()
      ->filter(function ($key) use ($top_level_links) {
      return !$top_level_links
        ->hasLinkWithKey($key);
    })
      ->withContext($top_level_links
      ->getContext()));
  }

  /**
   * {@inheritdoc}
   */
  public function getMergedMeta(array $top_level_meta) {
    return NestedArray::mergeDeep($top_level_meta, $this
      ->getMeta());
  }

  /**
   * Builds a LinkCollection for the given entity reference field.
   *
   * @param \Drupal\jsonapi\JsonApiResource\ResourceObject $context
   *   The context resource object of the relationship object.
   * @param \Drupal\Core\Field\EntityReferenceFieldItemListInterface $field
   *   The entity reference field from which to create the links.
   * @param \Drupal\jsonapi\JsonApiResource\LinkCollection $links
   *   Any extra links for the Relationship, if a `self` link is not provided,
   *   one will be automatically added if the context resource is locatable and
   *   is not internal.
   *
   * @return \Drupal\jsonapi\JsonApiResource\LinkCollection
   *   The built links.
   */
  protected static function buildLinkCollectionFromEntityReferenceField(ResourceObject $context, EntityReferenceFieldItemListInterface $field, LinkCollection $links) {
    $context_resource_type = $context
      ->getResourceType();
    $public_field_name = $context_resource_type
      ->getPublicName($field
      ->getName());
    if ($context_resource_type
      ->isLocatable() && !$context_resource_type
      ->isInternal()) {
      $context_is_versionable = $context_resource_type
        ->isVersionable();
      if (!$links
        ->hasLinkWithKey('self')) {
        $route_name = Routes::getRouteName($context_resource_type, "{$public_field_name}.relationship.get");
        $self_link = Url::fromRoute($route_name, [
          'entity' => $context
            ->getId(),
        ]);
        if ($context_is_versionable) {
          $self_link
            ->setOption('query', [
            JsonApiSpec::VERSION_QUERY_PARAMETER => $context
              ->getVersionIdentifier(),
          ]);
        }
        $links = $links
          ->withLink('self', new Link(new CacheableMetadata(), $self_link, 'self'));
      }
      $has_non_internal_resource_type = array_reduce($context_resource_type
        ->getRelatableResourceTypesByField($public_field_name), function ($carry, ResourceType $target) {
        return $carry ?: !$target
          ->isInternal();
      }, FALSE);

      // If a `related` link was not provided, automatically generate one from
      // the relationship object to the collection resource with all of the
      // resources targeted by this relationship. However, that link should
      // *not* be generated if all of the relatable resources are internal.
      // That's because, in that case, a route will not exist for it.
      if (!$links
        ->hasLinkWithKey('related') && $has_non_internal_resource_type) {
        $route_name = Routes::getRouteName($context_resource_type, "{$public_field_name}.related");
        $related_link = Url::fromRoute($route_name, [
          'entity' => $context
            ->getId(),
        ]);
        if ($context_is_versionable) {
          $related_link
            ->setOption('query', [
            JsonApiSpec::VERSION_QUERY_PARAMETER => $context
              ->getVersionIdentifier(),
          ]);
        }
        $links = $links
          ->withLink('related', new Link(new CacheableMetadata(), $related_link, 'related'));
      }
    }
    return $links;
  }

}

Members

Namesort descending Modifiers Type Description Overrides
Relationship::$context protected property The context resource object of the relationship.
Relationship::$data protected property The data of the relationship object.
Relationship::$fieldName protected property The relationship's public field name.
Relationship::$links protected property The relationship object's links.
Relationship::$meta protected property The relationship object's meta member.
Relationship::buildLinkCollectionFromEntityReferenceField protected static function Builds a LinkCollection for the given entity reference field.
Relationship::createFromEntityReferenceField public static function Creates a new Relationship from an entity reference field.
Relationship::getContext public function Gets context resource object of the relationship.
Relationship::getData public function Gets the relationship object's data. Overrides TopLevelDataInterface::getData
Relationship::getFieldName public function Gets the relationship object's public field name.
Relationship::getLinks public function Gets the relationship object's links.
Relationship::getMergedLinks public function Merges the object's links with the top-level links. Overrides TopLevelDataInterface::getMergedLinks
Relationship::getMergedMeta public function Merges the object's meta member with the top-level meta member. Overrides TopLevelDataInterface::getMergedMeta
Relationship::getMeta public function Gets the relationship object's metadata.
Relationship::getOmissions public function Returns the data that was omitted from the JSON:API document. Overrides TopLevelDataInterface::getOmissions
Relationship::__construct protected function Relationship constructor.