View source
<?php
namespace Drupal\jsonapi\Normalizer\JsonapiHypermediaImpostor;
use Drupal\Component\Serialization\Json;
use Drupal\Component\Utility\Crypt;
use Drupal\Core\Render\RenderContext;
use Drupal\Core\Render\RendererInterface;
use Drupal\jsonapi\JsonApiResource\Link;
use Drupal\jsonapi\JsonApiResource\LinkCollection;
use Drupal\jsonapi\Normalizer\LinkCollectionNormalizer;
use Drupal\jsonapi\Normalizer\Value\CacheableNormalization;
use Drupal\jsonapi_hypermedia\Plugin\LinkProviderManagerInterface;
final class JsonapiHypermediaLinkCollectionNormalizer extends LinkCollectionNormalizer {
protected $linkProviderManager;
protected $renderer;
public function setLinkProviderManager(LinkProviderManagerInterface $link_provider_manager) {
$this->linkProviderManager = $link_provider_manager;
}
public function setRenderer(RendererInterface $renderer) {
$this->renderer = $renderer;
}
public function normalize($link_collection, $format = NULL, array $context = []) {
assert($link_collection instanceof LinkCollection);
$render_context = new RenderContext();
$custom_links = $this->renderer
->executeInRenderContext($render_context, function () use ($link_collection) {
return $this->linkProviderManager
->getLinkCollection($link_collection
->getContext());
});
$normalized = [];
foreach (LinkCollection::merge($link_collection, $custom_links) as $key => $links) {
$normalizations = [];
foreach ($links as $link) {
assert($link instanceof Link);
$normalization = [];
$rel = (array) $link
->getLinkRelationType();
if (empty($rel)) {
$rel = [
$key,
];
}
$attributes = $link
->getTargetAttributes();
$normalization['href'] = $link
->getHref();
if (!empty($attributes)) {
$normalization['meta']['linkParams'] = $this->serializer
->normalize($attributes, $format, $context);
}
if (!empty($rel)) {
$rel = array_values($rel);
if (count($rel) > 1 || reset($rel) !== $key) {
$normalization['meta']['linkParams']['rel'] = $rel;
}
$normalizations[] = $normalization;
}
}
$compacted = array_reduce($normalizations, function ($compacted, $normalization) use ($key) {
foreach ($compacted as &$previous) {
$same_href = $previous['href'] === $normalization['href'];
$previous_attributes = array_diff_key($previous['meta']['linkParams'] ?? [], array_flip([
'rel',
]));
$normalization_attributes = array_diff_key($normalization['meta']['linkParams'] ?? [], array_flip([
'rel',
]));
$same_attributes = Json::encode($previous_attributes) === Json::encode($normalization_attributes);
if ($same_href && $same_attributes) {
$combined_link_relation_types = array_unique(array_merge($previous['meta']['linkParams']['rel'] ?? [
$key,
], $normalization['meta']['linkParams']['rel'] ?? []));
if ($combined_link_relation_types > 1 || reset($combined_link_relation_types) !== $key) {
$previous['meta']['linkParams']['rel'] = $combined_link_relation_types;
}
return $compacted;
}
}
$compacted[] = $normalization;
return $compacted;
}, []);
$is_multiple = count($compacted) > 1;
$normalized = array_reduce($compacted, function ($normalized, $normalization) use ($link, $key, $is_multiple) {
$link_key = $is_multiple ? sprintf('%s--%s', $key, $this
->hashLinkNormalization($normalization)) : $key;
$normalized[$link_key] = new CacheableNormalization($link, $normalization);
return $normalized;
}, $normalized);
}
$normalized_links = CacheableNormalization::aggregate($normalized);
return !$render_context
->isEmpty() ? $normalized_links
->withCacheableDependency($render_context
->pop()) : $normalized_links;
}
protected function hashLinkNormalization(array $link) {
if (!$this->hashSalt) {
$this->hashSalt = Crypt::randomBytesBase64();
}
return substr(str_replace([
'-',
'_',
], '', Crypt::hashBase64($this->hashSalt . Json::encode($link))), 0, 7);
}
}