graphql_compose-1.0.0-beta20/src/Plugin/GraphQL/DataProducer/EntityLoadRevision.php
src/Plugin/GraphQL/DataProducer/EntityLoadRevision.php
<?php
declare(strict_types=1);
namespace Drupal\graphql_compose\Plugin\GraphQL\DataProducer;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Entity\RevisionableInterface;
use Drupal\Core\Entity\TranslatableRevisionableStorageInterface;
use Drupal\Core\Language\LanguageInterface;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\Core\Plugin\Context\ContextDefinition;
use Drupal\Core\StringTranslation\TranslatableMarkup;
use Drupal\graphql\Attribute\DataProducer;
use Drupal\graphql\GraphQL\Buffers\EntityRevisionBuffer;
use Drupal\graphql\GraphQL\Execution\FieldContext;
use Drupal\graphql\Plugin\GraphQL\DataProducer\DataProducerPluginBase;
use Drupal\graphql_compose\EntityTranslateTrait;
use GraphQL\Deferred;
use GraphQL\Error\UserError;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Loads the entity by revision.
*/
#[DataProducer(
id: "entity_load_revision",
name: new TranslatableMarkup("Load entity revision"),
description: new TranslatableMarkup("The entity belonging to the current url."),
produces: new ContextDefinition(
data_type: "entity",
label: new TranslatableMarkup("Entity"),
),
consumes: [
"entity" => new ContextDefinition(
data_type: "entity",
label: new TranslatableMarkup("The entity to load revisions from"),
),
"identifier" => new ContextDefinition(
data_type: "any",
label: new TranslatableMarkup("Revision ID"),
required: FALSE,
),
"language" => new ContextDefinition(
data_type: "string",
label: new TranslatableMarkup("Language code"),
required: FALSE,
),
],
)]
class EntityLoadRevision extends DataProducerPluginBase implements ContainerFactoryPluginInterface {
use EntityTranslateTrait;
/**
* The latest revision identifiers.
*/
const REVISION_LATEST = [
'latest',
'newest',
'working',
'working-copy',
];
/**
* The current revision identifiers.
*/
const REVISION_CURRENT = [
'active',
'current',
'default',
];
/**
* Constructs a \Drupal\Component\Plugin\PluginBase object.
*
* @param array $configuration
* The plugin configuration array.
* @param string $pluginId
* The plugin id.
* @param mixed $pluginDefinition
* The plugin definition array.
* @param \Drupal\Core\Entity\EntityTypeManagerInterface $entityTypeManager
* The language manager service.
* @param \Drupal\graphql\GraphQL\Buffers\EntityRevisionBuffer $entityRevisionBuffer
* The entity revision buffer service.
*/
public function __construct(
array $configuration,
$pluginId,
$pluginDefinition,
protected EntityTypeManagerInterface $entityTypeManager,
protected EntityRevisionBuffer $entityRevisionBuffer,
) {
parent::__construct($configuration, $pluginId, $pluginDefinition);
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
return new static(
$configuration,
$plugin_id,
$plugin_definition,
$container->get('entity_type.manager'),
$container->get('graphql.buffer.entity_revision'),
);
}
/**
* Resolve the entity revision.
*
* @param \Drupal\Core\Entity\EntityInterface|null $entity
* The entity to load revisions from.
* @param int|string|null $identifier
* The revision ID to load.
* @param string|null $language
* The language code to use.
* @param \Drupal\graphql\GraphQL\Execution\FieldContext $context
* Cache context.
*/
public function resolve(?EntityInterface $entity, int|string|null $identifier, ?string $language, FieldContext $context): Deferred|EntityInterface|null {
if (is_string($identifier)) {
$identifier = strtolower($identifier);
}
if (!$identifier || in_array($identifier, self::REVISION_CURRENT)) {
return $entity;
}
if (!$entity instanceof RevisionableInterface) {
return $entity;
}
$entity_id = $entity->id();
$entity_type_id = $entity->getEntityTypeId();
// Get the current or preferred language.
$langcode = $this->getCurrentLanguage($context, $language, LanguageInterface::TYPE_INTERFACE);
// Quickly resolve the latest revision.
if (in_array($identifier, self::REVISION_LATEST)) {
/** @var \Drupal\Core\Entity\RevisionableStorageInterface $storage */
$storage = $this->entityTypeManager->getStorage($entity_type_id);
$identifier = ($storage instanceof TranslatableRevisionableStorageInterface)
? $storage->getLatestTranslationAffectedRevisionId($entity_id, $langcode)
: $storage->getLatestRevisionId($entity_id);
}
// Still did not get a valid revision identifier.
if (!$identifier || !is_numeric($identifier)) {
return NULL;
}
// Add the entity to the buffer.
$resolver = $this->entityRevisionBuffer->add($entity_type_id, (int) $identifier);
return new Deferred(function () use ($resolver, $langcode, $entity_id, $entity_type_id, $context) {
/** @var \Drupal\Core\Entity\RevisionableInterface|null $revision */
if (!$revision = $resolver()) {
// Add cache list tags to invalidate the cache.
$entity_type = $this->entityTypeManager->getDefinition($entity_type_id, FALSE);
if ($entity_type) {
$context->addCacheTags($entity_type->getListCacheTags());
}
$context->addCacheTags(['4xx-response']);
return NULL;
}
// Check the revision belongs to the entity.
if ($revision->id() !== $entity_id) {
throw new UserError('The requested revision does not belong to the requested entity.');
}
$context->setContextValue('revision', $revision->getRevisionId());
$revision = $this->getTranslation($revision, $langcode);
if (!$revision instanceof EntityInterface) {
return NULL;
}
// Check revision access.
$access = $revision->access('view', NULL, TRUE);
$context->addCacheableDependency($access);
return $access->isAllowed() ? $revision : NULL;
});
}
}
