graphql_core_schema-1.0.x-dev/src/Plugin/GraphQL/DataProducer/ViewExecutor.php
src/Plugin/GraphQL/DataProducer/ViewExecutor.php
<?php
namespace Drupal\graphql_core_schema\Plugin\GraphQL\DataProducer;
use Drupal\Core\Entity\EntityRepositoryInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Language\LanguageManagerInterface;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\Core\Url;
use Drupal\graphql\GraphQL\Execution\FieldContext;
use Drupal\graphql\Plugin\GraphQL\DataProducer\DataProducerPluginBase;
use Drupal\graphql_core_schema\EntitySchemaHelper;
use Drupal\graphql_core_schema\GraphQL\Buffers\SubRequestBuffer;
use Drupal\search_api\Plugin\views\query\SearchApiQuery;
use Drupal\views\Entity\Render\EntityTranslationRenderTrait;
use Drupal\views\ViewExecutable;
use GraphQL\Deferred;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* The data producer to execute views.
*
* @DataProducer(
* id = "view_executor",
* name = @Translation("View Executor"),
* description = @Translation("Execute the view and return the results."),
* produces = @ContextDefinition("any",
* label = @Translation("Executable")
* ),
* consumes = {
* "viewExecutable" = @ContextDefinition("any",
* label = @Translation("View Executable"),
* ),
* "page" = @ContextDefinition("any",
* label = @Translation("Page"),
* required = FALSE
* ),
* "limit" = @ContextDefinition("any",
* label = @Translation("Limit"),
* required = FALSE
* ),
* "sortBy" = @ContextDefinition("string",
* label = @Translation("Sort by"),
* required = FALSE
* ),
* "sortOrder" = @ContextDefinition("string",
* label = @Translation("Sort order"),
* required = FALSE
* ),
* "filters" = @ContextDefinition("any",
* label = @Translation("Filters"),
* required = FALSE
* ),
* "contextualFilters" = @ContextDefinition("any",
* label = @Translation("Contextual filters"),
* required = FALSE
* ),
* "queryParams" = @ContextDefinition("any",
* label = @Translation("Query Parameters"),
* required = FALSE
* ),
* }
* )
*/
class ViewExecutor extends DataProducerPluginBase implements ContainerFactoryPluginInterface {
use EntityTranslationRenderTrait {
getEntityTranslationRenderer as parentGetEntityTranslationRenderer;
}
/**
* View.
*
* @var \Drupal\views\ViewExecutable
*/
protected ViewExecutable $view;
/**
* Entity type ID.
*
* @var string
*/
protected string $entityTypeId;
/**
* {@inheritdoc}
*/
public static function create(
ContainerInterface $container,
array $configuration,
$pluginId,
$pluginDefinition,
) {
return new static(
$configuration,
$pluginId,
$pluginDefinition,
$container->get('graphql_core_schema.buffer.subrequest'),
$container->get('language_manager'),
$container->get('entity_type.manager'),
$container->get('entity.repository')
);
}
public function __construct(
array $configuration,
$pluginId,
$pluginDefinition,
protected SubRequestBuffer $subRequestBuffer,
protected LanguageManagerInterface $languageManager,
protected EntityTypeManagerInterface $entityTypeManager,
protected EntityRepositoryInterface $entityRepository,
) {
parent::__construct($configuration, $pluginId, $pluginDefinition);
$this->subRequestBuffer = $subRequestBuffer;
$this->languageManager = $languageManager;
$this->entityTypeManager = $entityTypeManager;
$this->entityRepository = $entityRepository;
}
/**
* The resolver.
*
* @param \Drupal\views\ViewExecutable $executable
* The view.
* @param int $page
* The page.
* @param int $limit
* The limit.
* @param string $sortBy
* The sort field.
* @param string $sortOrder
* The sort order.
* @param array|null $filters
* The exposed filters.
* @param array|null $contextualFilters
* The contextual filters.
* @param array|null $queryParams
* The query params.
* @param \Drupal\graphql\GraphQL\Execution\FieldContext $fieldContext
* The field context.
*
* @return \GraphQL\Deferred
* The result.
*/
public function resolve(ViewExecutable $executable, ?int $page = NULL, ?int $limit = NULL, ?string $sortBy = NULL, ?string $sortOrder = NULL, ?array $filters = NULL, ?array $contextualFilters = NULL, ?array $queryParams = NULL, ?FieldContext $fieldContext = NULL) {
$page = $page ?? 0;
$url = $executable->hasUrl() ? $executable->getUrl() : Url::fromRoute('<front>');
$exposedInput = [];
$queryArguments = [];
if ($queryParams) {
foreach ($queryParams as $param) {
if (!isset($param['key'])) {
continue;
}
$value = $param['value'] ?? NULL;
if ($value === NULL) {
continue;
}
$queryArguments[$param['key']] = $value;
$exposedInput[$param['key']] = $value;
}
}
if ($filters) {
$displayOptions = $executable->getDisplay()->getOption('filters') ?: [];
$exposedFilters = array_reduce(array_filter($displayOptions, function ($definition) {
return !empty($definition['exposed']) && !empty($definition['expose']['identifier']);
}), function ($carry, $definition) {
$identifier = $definition['expose']['identifier'];
$carry[EntitySchemaHelper::toCamelCase($identifier)] = $identifier;
return $carry;
}, []);
foreach ($filters as $graphqlIdentifier => $value) {
if (!array_key_exists($graphqlIdentifier, $exposedFilters)) {
continue;
}
if ($value === NULL) {
continue;
}
$identifier = $exposedFilters[$graphqlIdentifier];
$queryArguments[$identifier] = $value;
$exposedInput[$identifier] = $value;
}
}
if (isset($queryArguments['page'])) {
$limit = $queryArguments['items_per_page'] ?? $limit;
}
if (!empty($queryArguments)) {
$url->setOption('query', $queryArguments);
}
if ($sortBy) {
$exposedInput['sort_by'] = $sortBy;
}
if ($sortOrder) {
$exposedInput['sort_order'] = $sortOrder;
}
if ($contextualFilters) {
$args = [];
foreach ($contextualFilters as $contextualFilter) {
$args[$contextualFilter['key']] = $contextualFilter['value'];
}
$executable->setArguments($args);
}
// Needed by the EntityTranslationRenderTrait.
$this->view = $executable;
$baseEntityType = $executable->getBaseEntityType();
if (!empty($baseEntityType)) {
$this->entityTypeId = $baseEntityType->id();
}
$self = $this;
$resolve = $this->subRequestBuffer->add($url, function () use ($executable, $page, $limit, $exposedInput, $self, $fieldContext) {
if ($page) {
$executable->setCurrentPage($page);
}
if ($limit) {
$executable->setItemsPerPage($limit);
}
if (!empty($exposedInput)) {
$executable->setExposedInput($exposedInput);
}
$executable->isGraphQLQuery = TRUE;
$executable->execute();
$executable->render();
$rows = [];
foreach ($executable->result as $row) {
// Some views, especially those with search api backend do not have a
// base entity type on the view. To avoid fatal error, we provide the
// entity type per rows because it can be different in the same view.
if (empty($this->entityTypeId)) {
$this->entityTypeId = $row->_entity->getEntityTypeId();
}
// The search api backend uses its own language management. So only run
// translation handling if this isn't a search api backend view.
$row_entity = $row->_entity;
if (!($this->view->getQuery() instanceof SearchApiQuery)) {
$row_entity = $self->getEntityTranslationByRelationship($row->_entity, $row);
}
$rows[] = $row_entity;
}
$fieldContext->addCacheContexts(['url.query_args']);
$fieldContext->addCacheTags($executable->getCacheTags());
return [
'rows' => $rows,
'total_rows' => $executable->getPager()->getTotalItems(),
'executable' => $executable,
];
});
return new Deferred(function () use ($resolve) {
return $resolve();
});
}
/**
* {@inheritdoc}
*/
protected function getEntityTranslationRenderer() {
// We need to call the query method on the renderer so that the language
// alias is set or getLanguage will only return the default language.
$renderer = $this->parentGetEntityTranslationRenderer();
$renderer->query($this->view->getQuery());
return $renderer;
}
/**
* Get the entity type manager.
*
* @todo open issue to have this added as an abstract method on the trait.
*
* @return \Drupal\Core\Entity\EntityTypeManagerInterface
* The entity type manager.
*/
public function getEntityTypeManager(): EntityTypeManagerInterface {
return $this->entityTypeManager;
}
/**
* Get the entity type repository.
*
* @todo open issue to have this added as an abstract method on the trait.
*
* @return \Drupal\Core\Entity\EntityRepositoryInterface
* The entity type repository.
*/
public function getEntityRepository(): EntityRepositoryInterface {
return $this->entityRepository;
}
/**
* {@inheritdoc}
*/
public function getEntityTypeId(): string {
return $this->entityTypeId ?? '';
}
/**
* {@inheritdoc}
*/
protected function getLanguageManager(): LanguageManagerInterface {
return $this->languageManager;
}
/**
* {@inheritdoc}
*/
protected function getView(): ViewExecutable {
return $this->view;
}
}
