headless_cms-1.0.3/modules/headless_cms_preview/src/JsonApi/PreviewIncludeResolver.php
modules/headless_cms_preview/src/JsonApi/PreviewIncludeResolver.php
<?php
declare(strict_types=1);
namespace Drupal\headless_cms_preview\JsonApi;
use Drupal\Core\Access\AccessResult;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Field\FieldItemListInterface;
use Drupal\Core\Field\Plugin\Field\FieldType\EntityReferenceItemInterface;
use Drupal\Core\TypedData\DataReferenceDefinitionInterface;
use Drupal\jsonapi\Exception\EntityAccessDeniedHttpException;
use Drupal\jsonapi\IncludeResolver as BaseIncludeResolver;
use Drupal\jsonapi\JsonApiResource\Data;
use Drupal\jsonapi\JsonApiResource\IncludedData;
use Drupal\jsonapi\JsonApiResource\LabelOnlyResourceObject;
use Drupal\jsonapi\JsonApiResource\ResourceIdentifierInterface;
use Drupal\jsonapi\JsonApiResource\ResourceObject;
/**
* Custom JSON:API include resolver for preview data.
*/
class PreviewIncludeResolver extends BaseIncludeResolver {
/**
* {@inheritdoc}
*
* Alters the core `resolveIncludeTree` method to only load entities from
* the preview data directly stored on the field_items.
*
* Major changes are marked with `CUSTOMIZATION START` and `CUSTOMIZATION
* END`.
*/
protected function resolveIncludeTree(array $include_tree, Data $data, ?Data $includes = NULL) {
$includes = is_null($includes) ? new IncludedData([]) : $includes;
foreach ($include_tree as $field_name => $children) {
$references = [];
foreach ($data as $resource_object) {
// Some objects in the collection may be LabelOnlyResourceObjects or
// EntityAccessDeniedHttpException objects.
assert($resource_object instanceof ResourceIdentifierInterface);
$public_field_name = $resource_object->getResourceType()->getPublicName($field_name);
if ($resource_object instanceof LabelOnlyResourceObject) {
$message = "The current user is not allowed to view this relationship.";
$exception = new EntityAccessDeniedHttpException($resource_object->getEntity(), AccessResult::forbidden("The user only has authorization for the 'view label' operation."), '', $message, $public_field_name);
$includes = IncludedData::merge($includes, new IncludedData([$exception]));
continue;
}
elseif (!$resource_object instanceof ResourceObject) {
continue;
}
// Not all entities in $entity_collection will be of the same bundle and
// may not have all of the same fields. Therefore, calling
// $resource_object->get($a_missing_field_name) will result in an
// exception.
if (!$resource_object->hasField($public_field_name)) {
continue;
}
$field_list = $resource_object->getField($public_field_name);
// Config entities don't have real fields and can't have relationships.
if (!$field_list instanceof FieldItemListInterface) {
continue;
}
$field_access = $field_list->access('view', NULL, TRUE);
if (!$field_access->isAllowed()) {
$message = 'The current user is not allowed to view this relationship.';
$exception = new EntityAccessDeniedHttpException($field_list->getEntity(), $field_access, '', $message, $public_field_name);
$includes = IncludedData::merge($includes, new IncludedData([$exception]));
continue;
}
if (is_subclass_of($field_list->getItemDefinition()->getClass(), EntityReferenceItemInterface::class)) {
foreach ($field_list as $field_item) {
if (!($field_item->getDataDefinition()->getPropertyDefinition('entity') instanceof DataReferenceDefinitionInterface)) {
continue;
}
if (!($field_item->entity instanceof EntityInterface)) {
continue;
}
$target_type = $field_item->entity->getEntityTypeId();
// CUSTOMIZATION START - Support preview.
$entity = $field_item->entity;
$entity->mergeCacheMaxAge(0);
$references[$target_type . ':preview'][] = $entity;
// CUSTOMIZATION END.
}
}
}
foreach ($references as $target_type_and_rev => $ids) {
// Revision type might be null, so we suppress the warning.
@[$target_type] = array_pad(explode(':', $target_type_and_rev), 2, NULL);
// CUSTOMIZATION START - Support preview.
$ids = array_filter($ids, fn($i) => !is_null($i));
$targeted_entities = array_reduce($ids, function ($carry, $entity) {
/** @var \Drupal\Core\Entity\EntityInterface $entity */
$carry[$entity->id()] = $entity;
return $carry;
}, []);
// CUSTOMIZATION END.
$access_checked_entities = array_map(function (EntityInterface $entity) {
return $this->entityAccessChecker->getAccessCheckedResourceObject($entity);
}, $targeted_entities);
$targeted_collection = new IncludedData(array_filter($access_checked_entities, function (ResourceIdentifierInterface $resource_object) {
return !$resource_object->getResourceType()->isInternal();
}));
$includes = static::resolveIncludeTree($children, $targeted_collection, IncludedData::merge($includes, $targeted_collection));
}
}
return $includes;
}
}
