graphql_core_schema-1.0.x-dev/src/Plugin/GraphQL/SchemaExtension/ViewsExtension.php
src/Plugin/GraphQL/SchemaExtension/ViewsExtension.php
<?php
namespace Drupal\graphql_core_schema\Plugin\GraphQL\SchemaExtension;
use Drupal\Component\Plugin\ConfigurableInterface;
use Drupal\Component\Utility\NestedArray;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\Core\Plugin\PluginFormInterface;
use Drupal\Core\Render\Element\Checkboxes;
use Drupal\graphql\GraphQL\ResolverBuilder;
use Drupal\graphql\GraphQL\ResolverRegistryInterface;
use Drupal\graphql\Plugin\GraphQL\SchemaExtension\SdlSchemaExtensionPluginBase;
use Drupal\graphql_core_schema\CoreSchemaExtensionInterface;
use Drupal\graphql_core_schema\ViewsSchemaBuilder;
use Drupal\views\Plugin\views\filter\FilterPluginBase;
use Drupal\views\Plugin\views\pager\PagerPluginBase;
use Drupal\views\Plugin\views\sort\SortPluginBase;
use Drupal\views\ViewEntityInterface;
use Drupal\views\ViewExecutable;
use Drupal\views\Views;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* The views schema extension.
*
* @SchemaExtension(
* id = "views",
* name = "Views",
* description = "An extension that provides integration with views.",
* schema = "core_composable"
* )
*/
class ViewsExtension extends SdlSchemaExtensionPluginBase implements ContainerFactoryPluginInterface, ConfigurableInterface, PluginFormInterface, CoreSchemaExtensionInterface {
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
return new static(
$configuration,
$plugin_id,
$plugin_definition,
$container->get('module_handler'),
$container->get('entity_type.manager'),
);
}
public function __construct(
array $configuration,
$pluginId,
array $pluginDefinition,
ModuleHandlerInterface $moduleHandler,
protected EntityTypeManagerInterface $entityTypeManager,
) {
parent::__construct($configuration, $pluginId, $pluginDefinition, $moduleHandler);
$this->entityTypeManager = $entityTypeManager;
}
/**
* {@inheritdoc}
*/
public function getEntityTypeDependencies() {
return ['view'];
}
/**
* {@inheritdoc}
*/
public function getExtensionDependencies() {
return [];
}
/**
* {@inheritdoc}
*/
public function getConfiguration() {
return $this->configuration;
}
/**
* {@inheritdoc}
*/
public function setConfiguration(array $configuration): void {
$this->configuration = NestedArray::mergeDeep($this->defaultConfiguration(), $configuration);
}
/**
* {@inheritdoc}
*/
public function defaultConfiguration() {
return [];
}
/**
* {@inheritdoc}
*/
public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
$form['enabled_views'] = [
'#type' => 'checkboxes',
'#required' => FALSE,
'#title' => $this->t('Enabled views'),
'#options' => Views::getViewsAsOptions(),
'#default_value' => $this->configuration['enabled_views'] ?? [],
];
return $form;
}
/**
* {@inheritdoc}
*/
public function validateConfigurationForm(array &$form, FormStateInterface $formState): void {
// @todo Validate dependencies between extensions.
}
/**
* {@inheritdoc}
*/
public function submitConfigurationForm(array &$form, FormStateInterface $formState): void {
$value = $formState->getValue('enabled_views');
$checked = Checkboxes::getCheckedCheckboxes($value);
$formState->setValue('enabled_views', $checked);
}
/**
* Get the enabled views.
*
* @return string[]
* Array of enabled views and displays.
*/
public function getEnabledViewDisplays() {
$configuration = $this->getConfiguration();
return array_values(array_filter($configuration['enabled_views'] ?? []));
}
/**
* {@inheritdoc}
*/
public function getBaseDefinition() {
$schemaBuilder = new ViewsSchemaBuilder($this->getEnabledViewDisplays());
$viewStorage = $this->entityTypeManager->getStorage('view');
$views = $viewStorage->loadMultiple();
/** @var \Drupal\views\ViewEntityInterface $view */
foreach ($views as $view) {
$schemaBuilder->generateViewType($view);
}
return $schemaBuilder->getGeneratedSchema();
}
/**
* {@inheritdoc}
*/
public function registerResolvers(ResolverRegistryInterface $registry): void {
$builder = new ResolverBuilder();
$this->registerPagerResolvers($registry, $builder);
$this->registerFilterResolvers($registry, $builder);
$this->registerSortResolvers($registry, $builder);
$registry->addTypeResolver('ViewExecutable', function ($executable) {
if ($executable instanceof ViewExecutable) {
if ($executable->executed) {
return 'ViewExecutableResult';
}
return ViewsSchemaBuilder::getGraphqlTypeName($executable);
}
});
$registry->addFieldResolver('View', 'executable',
$builder->compose(
// The check for enabled views.
// That way implementors can still expose a view executable themselves
// using the view_executable data producer.
$builder->callback(function (ViewEntityInterface $view, $args) {
$displayId = $args['displayId'] ?? 'default';
$key = $view->id() . ':' . $displayId;
// Only expose enabled view displays.
if (!in_array($key, $this->getEnabledViewDisplays())) {
return NULL;
}
return $view;
}),
$builder->produce('view_executable')
->map('view', $builder->fromParent())
->map('displayId', $builder->fromArgument('displayId'))
),
);
$registry->addFieldResolver('Query', 'getView', $builder->compose(
$builder->produce('entity_load')
->map('type', $builder->fromValue('view'))
->map('id', $builder->callback(function ($parent, $args) {
return strtolower($args['id']);
}))
));
$registry->addFieldResolver('ViewExecutable', 'execute', $builder->compose(
$builder->produce('view_executor')
->map('viewExecutable', $builder->fromParent())
->map('page', $builder->fromArgument('page'))
->map('limit', $builder->fromArgument('limit'))
->map('sortBy', $builder->fromArgument('sortBy'))
->map('sortOrder', $builder->fromArgument('sortOrder'))
->map('filters', $builder->fromArgument('filters'))
->map('contextualFilters', $builder->fromArgument('contextualFilters'))
->map('queryParams', $builder->fromArgument('queryParams'))
)
);
$registry->addFieldResolver('ViewExecutableResult', 'rows',
$builder->callback(function ($result) {
return $result['rows'];
}
));
$registry->addFieldResolver('ViewExecutableResult', 'total_rows',
$builder->callback(function ($result) {
return $result['total_rows'];
}
));
}
/**
* Add resolvers for the ViewPager type.
*
* @param \Drupal\graphql\GraphQL\ResolverRegistryInterface $registry
* The resolver registry.
* @param \Drupal\graphql\GraphQL\ResolverBuilder $builder
* The resolver builder.
*/
private function registerPagerResolvers(ResolverRegistryInterface $registry, ResolverBuilder $builder): void {
$registry->addFieldResolver('ViewExecutable', 'pager',
$builder->callback(function (ViewExecutable $executable) {
return $executable->getPager();
}
));
$registry->addFieldResolver('ViewPager', 'perPage',
$builder->callback(function (PagerPluginBase $pager) {
return $pager->getItemsPerPage();
}
));
$registry->addFieldResolver('ViewPager', 'totalItems',
$builder->callback(function (PagerPluginBase $pager) {
return $pager->getTotalItems();
}
));
}
/**
* Add resolvers for the ViewSort type.
*
* @param \Drupal\graphql\GraphQL\ResolverRegistryInterface $registry
* The resolver registry.
* @param \Drupal\graphql\GraphQL\ResolverBuilder $builder
* The resolver builder.
*/
private function registerSortResolvers(ResolverRegistryInterface $registry, ResolverBuilder $builder): void {
$registry->addFieldResolver('ViewExecutable', 'sorts',
$builder->callback(function (ViewExecutable $executable) {
return $executable->sort;
}
));
$registry->addFieldResolver('ViewSort', 'pluginId',
$builder->callback(function (SortPluginBase $sort) {
return $sort->getPluginId();
}
));
$registry->addFieldResolver('ViewSort', 'baseId',
$builder->callback(function (SortPluginBase $sort) {
return $sort->getBaseId();
}
));
$registry->addFieldResolver('ViewSort', 'field',
$builder->callback(function (SortPluginBase $sort) {
return $sort->field;
}
));
$registry->addFieldResolver('ViewSort', 'realField',
$builder->callback(function (SortPluginBase $sort) {
return $sort->realField;
}
));
}
/**
* Add resolvers for the ViewFilters type.
*
* @param \Drupal\graphql\GraphQL\ResolverRegistryInterface $registry
* The resolver registry.
* @param \Drupal\graphql\GraphQL\ResolverBuilder $builder
* The resolver builder.
*/
private function registerFilterResolvers(ResolverRegistryInterface $registry, ResolverBuilder $builder): void {
$registry->addFieldResolver('ViewExecutable', 'filters',
$builder->callback(function (ViewExecutable $executable) {
return $executable->filter;
}
));
$registry->addFieldResolver('ViewFilter', 'pluginId',
$builder->callback(function (FilterPluginBase $filter) {
return $filter->getPluginId();
}
));
$registry->addFieldResolver('ViewFilter', 'baseId',
$builder->callback(function (FilterPluginBase $filter) {
return $filter->getBaseId();
}
));
$registry->addFieldResolver('ViewFilter', 'field',
$builder->callback(function (FilterPluginBase $filter) {
return $filter->field;
}
));
$registry->addFieldResolver('ViewFilter', 'adminLabel',
$builder->callback(function (FilterPluginBase $filter) {
return $filter->adminLabel();
}
));
$registry->addFieldResolver('ViewFilter', 'adminLabelShort',
$builder->callback(function (FilterPluginBase $filter) {
return $filter->adminLabel(TRUE);
}
));
$registry->addFieldResolver('ViewFilter', 'adminSummary',
$builder->callback(function (FilterPluginBase $filter) {
return $filter->adminSummary();
}
));
$registry->addFieldResolver('ViewFilter', 'options',
$builder->callback(function (FilterPluginBase $filter) {
return $filter->options;
}
));
$registry->addFieldResolver('ViewFilter', 'groupInfo',
$builder->callback(function (FilterPluginBase $filter) {
return $filter->options['group_info'] ?? NULL;
}
));
$registry->addFieldResolver('ViewFilterGroupInfo', 'groupItems',
$builder->callback(function ($value) {
if (is_array($value)) {
$values = array_values($value['group_items'] ?? []);
return $values;
}
return [];
}
));
$registry->addFieldResolver('ViewFilter', 'realField',
$builder->callback(function (FilterPluginBase $filter) {
return $filter->realField;
}
));
$registry->addFieldResolver('ViewFilter', 'table',
$builder->callback(function (FilterPluginBase $filter) {
return $filter->table;
}
));
$registry->addFieldResolver('ViewFilter', 'value',
$builder->callback(function (FilterPluginBase $filter) {
return $filter->value;
}
));
$registry->addFieldResolver('ViewFilter', 'operator',
$builder->callback(function (FilterPluginBase $filter) {
return $filter->operator;
}
));
$registry->addFieldResolver('ViewFilter', 'noOperator',
$builder->callback(function (FilterPluginBase $filter) {
return $filter->no_operator;
}
));
$registry->addFieldResolver('ViewFilter', 'alwaysRequired',
$builder->callback(function (FilterPluginBase $filter) {
return $filter->always_required;
}
));
$registry->addFieldResolver('ViewFilter', 'isExposed',
$builder->callback(function (FilterPluginBase $filter) {
return $filter->isExposed();
}
));
$registry->addFieldResolver('ViewFilter', 'isAGroup',
$builder->callback(function (FilterPluginBase $filter) {
return $filter->isAGroup();
}
));
}
}
