View source
<?php
namespace Drupal\graphql\Plugin\GraphQL\Fields;
use Drupal\Component\Plugin\PluginBase;
use Drupal\Component\Render\MarkupInterface;
use Drupal\Core\Cache\CacheableDependencyInterface;
use Drupal\Core\Cache\CacheableMetadata;
use Drupal\Core\Render\RenderContext;
use Drupal\graphql\GraphQL\Execution\ResolveContext;
use Drupal\graphql\GraphQL\ValueWrapperInterface;
use Drupal\graphql\Plugin\FieldPluginInterface;
use Drupal\graphql\Plugin\FieldPluginManager;
use Drupal\graphql\Plugin\GraphQL\Traits\ArgumentAwarePluginTrait;
use Drupal\graphql\Plugin\GraphQL\Traits\CacheablePluginTrait;
use Drupal\graphql\Plugin\GraphQL\Traits\DeprecatablePluginTrait;
use Drupal\graphql\Plugin\GraphQL\Traits\DescribablePluginTrait;
use Drupal\graphql\Plugin\GraphQL\Traits\TypedPluginTrait;
use Drupal\graphql\Plugin\SchemaBuilderInterface;
use GraphQL\Deferred;
use GraphQL\Type\Definition\ListOfType;
use GraphQL\Type\Definition\NonNull;
use GraphQL\Type\Definition\ResolveInfo;
abstract class FieldPluginBase extends PluginBase implements FieldPluginInterface {
use CacheablePluginTrait;
use DescribablePluginTrait;
use TypedPluginTrait;
use ArgumentAwarePluginTrait;
use DeprecatablePluginTrait;
protected $languageContext;
protected $renderer;
protected $isLanguageAware = NULL;
public static function createInstance(SchemaBuilderInterface $builder, FieldPluginManager $manager, $definition, $id) {
return [
'description' => $definition['description'],
'contexts' => $definition['contexts'],
'deprecationReason' => $definition['deprecationReason'],
'type' => $builder
->processType($definition['type']),
'args' => $builder
->processArguments($definition['args']),
'resolve' => function ($value, array $args, ResolveContext $context, ResolveInfo $info) use ($manager, $id) {
$instance = $manager
->getInstance([
'id' => $id,
]);
return $instance
->resolve($value, $args, $context, $info);
},
];
}
protected function getLanguageContext() {
if (!isset($this->languageContext)) {
$this->languageContext = \Drupal::service('graphql.language_context');
}
return $this->languageContext;
}
protected function getRenderer() {
if (!isset($this->renderer)) {
$this->renderer = \Drupal::service('renderer');
}
return $this->renderer;
}
public function getDefinition() {
$definition = $this
->getPluginDefinition();
return [
'type' => $this
->buildType($definition),
'description' => $this
->buildDescription($definition),
'args' => $this
->buildArguments($definition),
'deprecationReason' => $this
->buildDeprecationReason($definition),
'contexts' => $this
->buildCacheContexts($definition),
];
}
public function resolve($value, array $args, ResolveContext $context, ResolveInfo $info) {
$definition = $this
->getPluginDefinition();
if (!$context
->getGlobal('development', FALSE) && !$context
->getGlobal('bypass field security', FALSE)) {
if (empty($definition['secure'])) {
throw new \Exception(sprintf("Unable to resolve insecure field '%s'.", $info->fieldName));
}
}
foreach ($definition['contextual_arguments'] as $argument) {
if (array_key_exists($argument, $args) && !is_null($args[$argument])) {
$context
->setContext($argument, $args[$argument], $info);
}
$args[$argument] = $context
->getContext($argument, $info);
}
return $this
->resolveDeferred([
$this,
'resolveValues',
], $value, $args, $context, $info);
}
protected function isLanguageAwareField() {
if (is_null($this->isLanguageAware)) {
$this->isLanguageAware = (bool) count(array_filter($this
->getPluginDefinition()['response_cache_contexts'], function ($context) {
return strpos($context, 'languages:') === 0;
}));
}
return $this->isLanguageAware;
}
protected function resolveDeferred(callable $callback, $value, array $args, ResolveContext $context, ResolveInfo $info) {
$isLanguageAware = $this
->isLanguageAwareField();
$languageContext = $this
->getLanguageContext();
$renderContext = new RenderContext();
$executor = function () use ($callback, $renderContext, $value, $args, $context, $info) {
return $this
->getRenderer()
->executeInRenderContext($renderContext, function () use ($callback, $value, $args, $context, $info) {
$result = $callback($value, $args, $context, $info);
if ($result instanceof \Generator) {
$result = iterator_to_array($result);
}
return $result;
});
};
$result = $isLanguageAware ? $languageContext
->executeInLanguageContext($executor, $context
->getContext('language', $info)) : $executor();
if (!$renderContext
->isEmpty() && $info->operation->operation === 'query') {
$context
->addCacheableDependency($renderContext
->pop());
}
if (is_callable($result)) {
return new Deferred(function () use ($result, $value, $args, $context, $info, $isLanguageAware, $languageContext) {
if ($isLanguageAware) {
return $languageContext
->executeInLanguageContext(function () use ($result, $value, $args, $context, $info) {
return $this
->resolveDeferred($result, $value, $args, $context, $info);
}, $context
->getContext('language', $info));
}
return $this
->resolveDeferred($result, $value, $args, $context, $info);
});
}
if ($info->operation->operation === 'query') {
$dependencies = $this
->getCacheDependencies($result, $value, $args, $context, $info);
foreach ($dependencies as $dependency) {
$context
->addCacheableDependency($dependency);
}
}
return $this
->unwrapResult($result, $info);
}
protected function unwrapResult($result, ResolveInfo $info) {
$result = array_map(function ($item) {
return $item instanceof ValueWrapperInterface ? $item
->getValue() : $item;
}, $result);
$result = array_map(function ($item) {
return $item instanceof MarkupInterface ? $item
->__toString() : $item;
}, $result);
$type = $info->returnType;
if ($type instanceof ListOfType || $type instanceof NonNull && $type
->getWrappedType() instanceof ListOfType) {
return $result;
}
return !empty($result) ? reset($result) : NULL;
}
protected function getCacheDependencies(array $result, $parent, array $args, ResolveContext $context, ResolveInfo $info) {
$self = new CacheableMetadata();
$definition = $this
->getPluginDefinition();
if (!empty($definition['response_cache_contexts'])) {
$self
->addCacheContexts($definition['response_cache_contexts']);
}
if (!empty($definition['response_cache_tags'])) {
$self
->addCacheTags($definition['response_cache_tags']);
}
if (isset($definition['response_cache_max_age'])) {
$self
->mergeCacheMaxAge($definition['response_cache_max_age']);
}
return array_merge([
$self,
], array_filter($result, function ($item) {
return $item instanceof CacheableDependencyInterface;
}));
}
protected function resolveValues($value, array $args, ResolveContext $context, ResolveInfo $info) {
(yield NULL);
}
}