View source
<?php
namespace Drupal\graphql\Plugin\Deriver;
use Drupal\Component\Plugin\Derivative\DeriverBase;
use Drupal\Component\Plugin\PluginManagerInterface;
use Drupal\Component\Utility\SortArray;
use Drupal\Core\Cache\Cache;
use Drupal\Core\Cache\CacheableDependencyInterface;
use Drupal\Core\Plugin\Discovery\ContainerDeriverInterface;
use Drupal\graphql\Plugin\FieldPluginManager;
use Drupal\graphql\Plugin\MutationPluginManager;
use Drupal\graphql\Plugin\SubscriptionPluginManager;
use Drupal\graphql\Plugin\TypePluginManagerAggregator;
use Symfony\Component\DependencyInjection\ContainerInterface;
class PluggableSchemaDeriver extends DeriverBase implements ContainerDeriverInterface {
protected $basePluginId;
protected $fieldManager;
protected $mutationManager;
protected $subscriptionManager;
protected $typeManagers;
public static function create(ContainerInterface $container, $basePluginId) {
return new static($basePluginId, $container
->get('plugin.manager.graphql.field'), $container
->get('plugin.manager.graphql.mutation'), $container
->get('plugin.manager.graphql.subscription'), $container
->get('graphql.type_manager_aggregator'));
}
public function __construct($basePluginId, FieldPluginManager $fieldManager, MutationPluginManager $mutationManager, SubscriptionPluginManager $subscriptionManager, TypePluginManagerAggregator $typeManagers) {
$this->basePluginId = $basePluginId;
$this->fieldManager = $fieldManager;
$this->mutationManager = $mutationManager;
$this->subscriptionManager = $subscriptionManager;
$this->typeManagers = $typeManagers;
}
public function getDerivativeDefinitions($basePluginDefinition) {
$typeMap = $this
->buildTypeMap(iterator_to_array($this->typeManagers));
$typeReferenceMap = $this
->buildTypeReferenceMap($typeMap);
$typeAssocationMap = $this
->buildTypeAssociationMap($typeMap);
$fieldAssocationMap = $this
->buildFieldAssociationMap($this->fieldManager, $typeMap);
$fieldMap = $this
->buildFieldMap($this->fieldManager, $fieldAssocationMap);
$mutationMap = $this
->buildMutationMap($this->mutationManager);
$subscriptionMap = $this
->buildSubscriptionMap($this->subscriptionManager);
$managers = array_merge([
$this->fieldManager,
$this->mutationManager,
$this->subscriptionManager,
], iterator_to_array($this->typeManagers));
$cacheTags = array_reduce($managers, function ($carry, CacheableDependencyInterface $current) {
return Cache::mergeTags($carry, $current
->getCacheTags());
}, []);
$cacheContexts = array_reduce($managers, function ($carry, CacheableDependencyInterface $current) {
return Cache::mergeContexts($carry, $current
->getCacheContexts());
}, []);
$cacheMaxAge = array_reduce($managers, function ($carry, CacheableDependencyInterface $current) {
return Cache::mergeMaxAges($carry, $current
->getCacheMaxAge());
}, Cache::PERMANENT);
$this->derivatives[$this->basePluginId] = [
'type_map' => $typeMap,
'type_reference_map' => $typeReferenceMap,
'type_association_map' => $typeAssocationMap,
'field_association_map' => $fieldAssocationMap,
'field_map' => $fieldMap,
'mutation_map' => $mutationMap,
'subscription_map' => $subscriptionMap,
'schema_cache_tags' => $cacheTags,
'schema_cache_contexts' => $cacheContexts,
'schema_cache_max_age' => $cacheMaxAge,
] + $basePluginDefinition;
return $this->derivatives;
}
protected function buildTypeMap(array $managers) {
$types = array_reduce(array_keys($managers), function ($carry, $type) use ($managers) {
$manager = $managers[$type];
$definitions = $manager
->getDefinitions();
return array_reduce(array_keys($definitions), function ($carry, $id) use ($type, $definitions) {
$current = $definitions[$id];
$name = $current['name'];
if (empty($carry[$name]) || $carry[$name]['weight'] < $current['weight']) {
$carry[$name] = [
'type' => $type,
'id' => $id,
'class' => $current['class'],
'weight' => !empty($current['weight']) ? $current['weight'] : 0,
'reference' => !empty($current['type']) ? $current['type'] : NULL,
];
}
return $carry;
}, $carry);
}, []);
return array_map(function ($type) use ($managers) {
$manager = $managers[$type['type']];
$instance = $manager
->getInstance([
'id' => $type['id'],
]);
return $type + [
'definition' => $instance
->getDefinition(),
] + $type;
}, $types);
}
protected function buildTypeReferenceMap(array $types) {
$references = array_reduce(array_keys($types), function ($references, $name) use ($types) {
$current = $types[$name];
$reference = $current['reference'];
if (!empty($reference) && (empty($references[$reference]) || $references[$reference]['weight'] < $current['weight'])) {
$references[$reference] = [
'name' => $name,
'weight' => !empty($current['weight']) ? $current['weight'] : 0,
];
}
return $references;
}, []);
return array_map(function ($reference) {
return $reference['name'];
}, $references);
}
protected function buildFieldAssociationMap(FieldPluginManager $manager, $types) {
$definitions = $manager
->getDefinitions();
$fields = array_reduce(array_keys($definitions), function ($carry, $id) use ($definitions, $types) {
$current = $definitions[$id];
$parents = $current['parents'] ?: [
'Root',
];
return array_reduce($parents, function ($carry, $parent) use ($current, $id, $types) {
if (strpos($parent, ':') !== FALSE) {
list($parent, $name) = explode(':', $parent);
}
$name = isset($name) ? $name : $current['name'];
if (empty($carry[$parent][$name]) || $carry[$parent][$name]['weight'] < $current['weight']) {
$carry[$parent][$name] = [
'id' => $id,
'weight' => !empty($current['weight']) ? $current['weight'] : 0,
];
}
return $carry;
}, $carry);
}, []);
$rename = [];
foreach ($fields as $parent => $fieldList) {
foreach ($fieldList as $field => $info) {
if (!array_key_exists($parent, $types)) {
continue;
}
foreach ($types[$parent]['definition']['interfaces'] as $interface) {
if (isset($fields[$interface][$field]) && $definitions[$fields[$interface][$field]['id']]['type'] != $definitions[$info['id']]['type']) {
$rename[$parent][$field] = TRUE;
}
}
}
}
foreach ($rename as $parent => $names) {
foreach (array_keys($names) as $name) {
$fields[$parent][$name . 'Of' . $parent] = $fields[$parent][$name];
unset($fields[$parent][$name]);
}
}
$fieldable = [
GRAPHQL_TYPE_PLUGIN,
GRAPHQL_INTERFACE_PLUGIN,
];
$fields = array_intersect_key($fields, array_filter($types, function ($type) use ($fieldable) {
return in_array($type['type'], $fieldable);
}) + [
'Root' => NULL,
]);
return array_map(function ($fields) {
return array_map(function ($field) {
return $field['id'];
}, $fields);
}, $fields);
}
protected function buildTypeAssociationMap(array $types) {
$assocations = array_filter(array_map(function ($type) use ($types) {
if ($type['type'] === 'type') {
return array_map(function () use ($type) {
return [
$type['definition']['name'],
];
}, array_flip($type['definition']['interfaces']));
}
if ($type['type'] === 'interface') {
return [
$type['definition']['name'] => array_values(array_map(function ($type) {
return $type['definition']['name'];
}, array_filter($types, function ($subType) use ($type) {
return $subType['type'] === 'type' && in_array($type['definition']['name'], $subType['definition']['interfaces']);
}))),
];
}
if ($type['type'] === 'union') {
$explicit = $type['definition']['types'];
$implicit = array_values(array_map(function ($type) {
return $type['definition']['name'];
}, array_filter($types, function ($subType) use ($type) {
return $subType['type'] === 'type' && in_array($type['definition']['name'], $subType['definition']['unions']);
})));
return [
$type['definition']['name'] => array_merge($explicit, $implicit),
];
}
return [];
}, $types));
$assocations = array_map('array_unique', array_reduce($assocations, 'array_merge_recursive', []));
$assocations = array_map(function ($parent) use ($types) {
$children = array_map(function ($child) use ($types) {
return $types[$child] + [
'name' => $child,
];
}, $parent);
uasort($children, [
SortArray::class,
'sortByWeightElement',
]);
$children = array_reverse($children);
return array_map(function ($child) {
return $child['name'];
}, $children);
}, $assocations);
return $assocations;
}
protected function buildFieldMap(FieldPluginManager $manager, $association) {
return array_reduce($association, function ($carry, $fields) use ($manager) {
return array_reduce($fields, function ($carry, $id) use ($manager) {
if (!isset($carry[$id])) {
$instance = $manager
->getInstance([
'id' => $id,
]);
$definition = $manager
->getDefinition($id);
$carry[$id] = [
'id' => $id,
'class' => $definition['class'],
'definition' => $instance
->getDefinition(),
];
}
return $carry;
}, $carry);
}, []);
}
protected function buildMutationMap(MutationPluginManager $manager) {
$definitions = $manager
->getDefinitions();
$mutations = array_reduce(array_keys($definitions), function ($carry, $id) use ($definitions) {
$current = $definitions[$id];
$name = $current['name'];
if (empty($carry[$name]) || $carry[$name]['weight'] < $current['weight']) {
$carry[$name] = [
'id' => $id,
'class' => $current['class'],
'weight' => !empty($current['weight']) ? $current['weight'] : 0,
];
}
return $carry;
}, []);
return array_map(function ($definition) use ($manager) {
$id = $definition['id'];
$instance = $manager
->getInstance([
'id' => $id,
]);
return [
'definition' => $instance
->getDefinition(),
] + $definition;
}, $mutations);
}
protected function buildSubscriptionMap(SubscriptionPluginManager $manager) {
$definitions = $manager
->getDefinitions();
$subscriptions = array_reduce(array_keys($definitions), function ($carry, $id) use ($definitions) {
$current = $definitions[$id];
$name = $current['name'];
if (empty($carry[$name]) || $carry[$name]['weight'] < $current['weight']) {
$carry[$name] = [
'id' => $id,
'class' => $current['class'],
'weight' => !empty($current['weight']) ? $current['weight'] : 0,
];
}
return $carry;
}, []);
return array_map(function ($definition) use ($manager) {
$id = $definition['id'];
$instance = $manager
->getInstance([
'id' => $id,
]);
return [
'definition' => $instance
->getDefinition(),
] + $definition;
}, $subscriptions);
}
}