You are here

final class LinkCollection in Drupal 10

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

Contains a set of JSON:API Link objects.

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

Hierarchy

  • class \Drupal\jsonapi\JsonApiResource\LinkCollection implements \Drupal\jsonapi\JsonApiResource\IteratorAggregate

Expanded class hierarchy of LinkCollection

See also

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

jsonapi.api.php

7 files declare their use of LinkCollection
DefaultExceptionSubscriber.php in core/modules/jsonapi/src/EventSubscriber/DefaultExceptionSubscriber.php
EntityResource.php in core/modules/jsonapi/src/Controller/EntityResource.php
EntryPoint.php in core/modules/jsonapi/src/Controller/EntryPoint.php
FileUpload.php in core/modules/jsonapi/src/Controller/FileUpload.php
JsonapiMaintenanceModeSubscriber.php in core/modules/jsonapi/src/EventSubscriber/JsonapiMaintenanceModeSubscriber.php

... See full list

File

core/modules/jsonapi/src/JsonApiResource/LinkCollection.php, line 16

Namespace

Drupal\jsonapi\JsonApiResource
View source
final class LinkCollection implements \IteratorAggregate {

  /**
   * The links in the collection, keyed by unique strings.
   *
   * @var \Drupal\jsonapi\JsonApiResource\Link[]
   */
  protected $links;

  /**
   * The link context.
   *
   * All links objects exist within a context object. Links form a relationship
   * between a source IRI and target IRI. A context is the link's source.
   *
   * @var \Drupal\jsonapi\JsonApiResource\JsonApiDocumentTopLevel|\Drupal\jsonapi\JsonApiResource\ResourceObject|\Drupal\jsonapi\JsonApiResource\Relationship
   *
   * @see https://tools.ietf.org/html/rfc8288#section-3.2
   */
  protected $context;

  /**
   * LinkCollection constructor.
   *
   * @param \Drupal\jsonapi\JsonApiResource\Link[] $links
   *   An associated array of key names and JSON:API Link objects.
   * @param \Drupal\jsonapi\JsonApiResource\JsonApiDocumentTopLevel|\Drupal\jsonapi\JsonApiResource\ResourceObject|\Drupal\jsonapi\JsonApiResource\Relationship $context
   *   (internal use only) The context object. Use the self::withContext()
   *   method to establish a context. This should be done automatically when
   *   a LinkCollection is passed into a context object.
   */
  public function __construct(array $links, $context = NULL) {
    assert(Inspector::assertAll(function ($key) {
      return static::validKey($key);
    }, array_keys($links)));
    assert(Inspector::assertAll(function ($link) {
      return $link instanceof Link || is_array($link) && Inspector::assertAllObjects($link, Link::class);
    }, $links));
    assert(is_null($context) || Inspector::assertAllObjects([
      $context,
    ], JsonApiDocumentTopLevel::class, ResourceObject::class, Relationship::class));
    ksort($links);
    $this->links = array_map(function ($link) {
      return is_array($link) ? $link : [
        $link,
      ];
    }, $links);
    $this->context = $context;
  }

  /**
   * {@inheritdoc}
   */

  #[\ReturnTypeWillChange]
  public function getIterator() {
    assert(!is_null($this->context), 'A LinkCollection is invalid unless a context has been established.');
    return new \ArrayIterator($this->links);
  }

  /**
   * Gets a new LinkCollection with the given link inserted.
   *
   * @param string $key
   *   A key for the link. If the key already exists and the link shares an
   *   href, link relation type and attributes with an existing link with that
   *   key, those links will be merged together.
   * @param \Drupal\jsonapi\JsonApiResource\Link $new_link
   *   The link to insert.
   *
   * @return static
   *   A new LinkCollection with the given link inserted or merged with the
   *   current set of links.
   */
  public function withLink($key, Link $new_link) {
    assert(static::validKey($key));
    $merged = $this->links;
    if (isset($merged[$key])) {
      foreach ($merged[$key] as $index => $existing_link) {
        if (Link::compare($existing_link, $new_link) === 0) {
          $merged[$key][$index] = Link::merge($existing_link, $new_link);
          return new static($merged, $this->context);
        }
      }
    }
    $merged[$key][] = $new_link;
    return new static($merged, $this->context);
  }

  /**
   * Whether a link with the given key exists.
   *
   * @param string $key
   *   The key.
   *
   * @return bool
   *   TRUE if a link with the given key exist, FALSE otherwise.
   */
  public function hasLinkWithKey($key) {
    return array_key_exists($key, $this->links);
  }

  /**
   * Establishes a new context for a LinkCollection.
   *
   * @param \Drupal\jsonapi\JsonApiResource\JsonApiDocumentTopLevel|\Drupal\jsonapi\JsonApiResource\ResourceObject|\Drupal\jsonapi\JsonApiResource\Relationship $context
   *   The new context object.
   *
   * @return static
   *   A new LinkCollection with the given context.
   */
  public function withContext($context) {
    return new static($this->links, $context);
  }

  /**
   * Gets the LinkCollection's context object.
   *
   * @return \Drupal\jsonapi\JsonApiResource\JsonApiDocumentTopLevel|\Drupal\jsonapi\JsonApiResource\ResourceObject|\Drupal\jsonapi\JsonApiResource\Relationship
   *   The LinkCollection's context.
   */
  public function getContext() {
    assert(!is_null($this->context), 'A LinkCollection is invalid unless a context has been established.');
    return $this->context;
  }

  /**
   * Filters a LinkCollection using the provided callback.
   *
   * @param callable $f
   *   The filter callback. The callback has the signature below.
   *
   * @code
   *   boolean callback(string $key, \Drupal\jsonapi\JsonApiResource\Link $link, mixed $context))
   * @endcode
   *
   * @return \Drupal\jsonapi\JsonApiResource\LinkCollection
   *   A new, filtered LinkCollection.
   */
  public function filter(callable $f) {
    $links = iterator_to_array($this);
    $filtered = array_reduce(array_keys($links), function ($filtered, $key) use ($links, $f) {
      if ($f($key, $links[$key], $this->context)) {
        $filtered[$key] = $links[$key];
      }
      return $filtered;
    }, []);
    return new LinkCollection($filtered, $this->context);
  }

  /**
   * Merges two LinkCollections.
   *
   * @param \Drupal\jsonapi\JsonApiResource\LinkCollection $a
   *   The first link collection.
   * @param \Drupal\jsonapi\JsonApiResource\LinkCollection $b
   *   The second link collection.
   *
   * @return \Drupal\jsonapi\JsonApiResource\LinkCollection
   *   A new LinkCollection with the links of both inputs.
   */
  public static function merge(LinkCollection $a, LinkCollection $b) {
    assert($a
      ->getContext() === $b
      ->getContext());
    $merged = new LinkCollection([], $a
      ->getContext());
    foreach ($a as $key => $links) {
      $merged = array_reduce($links, function (self $merged, Link $link) use ($key) {
        return $merged
          ->withLink($key, $link);
      }, $merged);
    }
    foreach ($b as $key => $links) {
      $merged = array_reduce($links, function (self $merged, Link $link) use ($key) {
        return $merged
          ->withLink($key, $link);
      }, $merged);
    }
    return $merged;
  }

  /**
   * Ensures that a link key is valid.
   *
   * @param string $key
   *   A key name.
   *
   * @return bool
   *   TRUE if the key is valid, FALSE otherwise.
   */
  protected static function validKey($key) {
    return is_string($key) && !is_numeric($key) && strpos($key, ':') === FALSE;
  }

}

Members