graphql_compose-1.0.0-beta20/modules/graphql_compose_routes/src/Plugin/GraphQL/DataProducer/RouteEntityExtra.php
modules/graphql_compose_routes/src/Plugin/GraphQL/DataProducer/RouteEntityExtra.php
<?php
declare(strict_types=1);
namespace Drupal\graphql_compose_routes\Plugin\GraphQL\DataProducer;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\Core\Plugin\Context\ContextDefinition;
use Drupal\Core\StringTranslation\TranslatableMarkup;
use Drupal\Core\Url;
use Drupal\graphql\Attribute\DataProducer;
use Drupal\graphql\GraphQL\Buffers\EntityBuffer;
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 Drupal\graphql_compose_routes\GraphQL\Buffers\EntityPreviewBuffer;
use GraphQL\Deferred;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Loads the entity associated with the current URL.
*/
#[DataProducer(
id: "route_entity_extra",
name: new TranslatableMarkup("Load entity, preview or revision by url"),
description: new TranslatableMarkup("The entity belonging to the current url."),
produces: new ContextDefinition(
data_type: "entity",
label: new TranslatableMarkup("Entity"),
),
consumes: [
"url" => new ContextDefinition(
data_type: "any",
label: new TranslatableMarkup("The URL"),
),
"language" => new ContextDefinition(
data_type: "string",
label: new TranslatableMarkup("Language"),
required: FALSE,
),
],
)]
class RouteEntityExtra extends DataProducerPluginBase implements ContainerFactoryPluginInterface {
use EntityTranslateTrait;
/**
* 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\EntityBuffer $entityBuffer
* The entity buffer service.
* @param \Drupal\graphql\GraphQL\Buffers\EntityRevisionBuffer $entityRevisionBuffer
* The entity revision buffer service.
* @param \Drupal\graphql_compose_routes\GraphQL\Buffers\EntityPreviewBuffer $entityPreviewBuffer
* The entity preview buffer service.
*/
public function __construct(
array $configuration,
$pluginId,
$pluginDefinition,
protected EntityTypeManagerInterface $entityTypeManager,
protected EntityBuffer $entityBuffer,
protected EntityRevisionBuffer $entityRevisionBuffer,
protected EntityPreviewBuffer $entityPreviewBuffer,
) {
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'),
$container->get('graphql.buffer.entity_revision'),
$container->get('graphql_compose_routes.buffer.entity_preview'),
);
}
/**
* Convert a URL to an entity via buffer.
*
* @param \Drupal\Core\Url|null $url
* The URL to resolve.
* @param string|null $langcode
* The language code to use.
* @param \Drupal\graphql\GraphQL\Execution\FieldContext $context
* Cache context.
*/
public function resolve(?Url $url, ?string $langcode, FieldContext $context): ?Deferred {
if (!$url instanceof Url) {
return NULL;
}
[, $type] = explode('.', $url->getRouteName());
$parameters = $url->getRouteParameters();
// Get the current or preferred language.
$langcode = $this->getCurrentLanguage($context, $langcode);
// Previews.
if (array_key_exists($type . '_preview', $parameters)) {
return $this->resolvePreview($type, $parameters, $langcode, $context);
}
// Revisions.
if (array_key_exists($type . '_revision', $parameters)) {
return $this->resolveRevision($type, $parameters, $langcode, $context);
}
// Eg /user - What is that to the Schema? Theres no data.
// It's a route internal, but not an entity.
if (empty($parameters[$type])) {
return NULL;
}
// Entities.
return $this->resolveEntity($type, $parameters, $langcode, $context);
}
/**
* Resolve an entity.
*
* @param string $type
* The entity type.
* @param array $parameters
* The URL parameters.
* @param string|null $langcode
* The language code to use.
* @param \Drupal\graphql\GraphQL\Execution\FieldContext $context
* Cache context.
*
* @return \GraphQL\Deferred
* The deferred entity.
*/
protected function resolveEntity(string $type, array $parameters, ?string $langcode, FieldContext $context): Deferred {
$entity_id = (int) $parameters[$type];
$resolver = $this->entityBuffer->add($type, $entity_id);
return new Deferred(function () use ($type, $resolver, $langcode, $context) {
if (!$entity = $resolver()) {
return $this->resolveNotFound($type, $context);
}
$entity = $this->getTranslation($entity, $langcode);
if (!$entity instanceof EntityInterface) {
return $this->resolveNotFound($type, $context);
}
$access = $entity->access('view', NULL, TRUE);
$context->addCacheableDependency($access);
return $access->isAllowed() ? $entity : NULL;
});
}
/**
* Resolve a preview entity.
*
* @param string $type
* The entity type.
* @param array $parameters
* The URL parameters.
* @param string|null $langcode
* The language code to use.
* @param \Drupal\graphql\GraphQL\Execution\FieldContext $context
* Cache context.
*
* @return \GraphQL\Deferred
* The deferred entity.
*/
protected function resolvePreview(string $type, array $parameters, ?string $langcode, FieldContext $context): Deferred {
$preview_id = (int) $parameters[$type . '_preview'];
$resolver = $this->entityPreviewBuffer->add($type, $preview_id);
return new Deferred(function () use ($type, $resolver, $langcode, $context) {
if (!$entity = $resolver()) {
return $this->resolveNotFound($type, $context);
}
$entity = $this->getTranslation($entity, $langcode);
if (!$entity instanceof EntityInterface) {
return $this->resolveNotFound($type, $context);
}
$access = $entity->access('view', NULL, TRUE);
$context->addCacheableDependency($access);
// Disable caching for accessible preview entities.
if ($access->isAllowed()) {
$context->setContextValue('preview', TRUE);
$context->mergeCacheMaxAge(0);
return $entity;
}
return NULL;
});
}
/**
* Resolve a preview revision.
*
* @param string $type
* The entity type.
* @param array $parameters
* The URL parameters.
* @param string|null $langcode
* The language code to use.
* @param \Drupal\graphql\GraphQL\Execution\FieldContext $context
* Cache context.
*
* @return \GraphQL\Deferred
* The deferred entity.
*/
protected function resolveRevision(string $type, array $parameters, ?string $langcode, FieldContext $context): Deferred {
$revision_id = (int) $parameters[$type . '_revision'];
$resolver = $this->entityRevisionBuffer->add($type, $revision_id);
return new Deferred(function () use ($type, $resolver, $langcode, $context) {
if (!$entity = $resolver()) {
return $this->resolveNotFound($type, $context);
}
$entity = $this->getTranslation($entity, $langcode);
if (!$entity instanceof EntityInterface) {
return $this->resolveNotFound($type, $context);
}
$access = $entity->access('view', NULL, TRUE);
$context->addCacheableDependency($access);
return $access->isAllowed() ? $entity : NULL;
});
}
/**
* Resolve a not found entity.
*
* If there is no entity with this id, add the list cache tags so that
* the cache entry is purged whenever a new entity of this type is
* saved.
*
* @param string $type
* The entity type.
* @param \Drupal\graphql\GraphQL\Execution\FieldContext $context
* Cache context.
*
* @return null
* Always null.
*/
private function resolveNotFound($type, FieldContext $context) {
$type = $this->entityTypeManager->getDefinition($type, FALSE);
if ($type) {
$context->addCacheTags($type->getListCacheTags());
}
$context->addCacheTags(['4xx-response']);
return NULL;
}
}
