graphql_compose-1.0.0-beta20/modules/graphql_compose_views/src/Plugin/GraphQL/DataProducer/ViewsExecutable.php

modules/graphql_compose_views/src/Plugin/GraphQL/DataProducer/ViewsExecutable.php
<?php

declare(strict_types=1);

namespace Drupal\graphql_compose_views\Plugin\GraphQL\DataProducer;

use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\Core\Plugin\Context\ContextDefinition;
use Drupal\Core\Render\RenderContext;
use Drupal\Core\Render\RendererInterface;
use Drupal\Core\StringTranslation\TranslatableMarkup;
use Drupal\graphql\Attribute\DataProducer;
use Drupal\graphql\GraphQL\Execution\FieldContext;
use Drupal\graphql\Plugin\GraphQL\DataProducer\DataProducerPluginBase;
use Drupal\views\Plugin\views\filter\FilterPluginBase;
use Drupal\views\ViewExecutable as ViewsViewExecutable;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\RequestStack;

/**
 * Load a view executable.
 */
#[DataProducer(
  id: "views_executable",
  name: new TranslatableMarkup("Views Executable"),
  description: new TranslatableMarkup("A views executable."),
  produces: new ContextDefinition(
    data_type: "any",
    label: new TranslatableMarkup("Views executable"),
  ),
  consumes: [
    "view_id" => new ContextDefinition(
      data_type: "string",
      label: new TranslatableMarkup("View ID"),
    ),
    "display_id" => new ContextDefinition(
      data_type: "string",
      label: new TranslatableMarkup("View Display ID"),
    ),
    "page" => new ContextDefinition(
      data_type: "integer",
      label: new TranslatableMarkup("Page"),
      default_value: 0,
      required: FALSE,
    ),
    "page_size" => new ContextDefinition(
      data_type: "integer",
      label: new TranslatableMarkup("Items per page"),
      required: FALSE,
    ),
    "offset" => new ContextDefinition(
      data_type: "integer",
      label: new TranslatableMarkup("Result offset"),
      default_value: 0,
      required: FALSE,
    ),
    "filter" => new ContextDefinition(
      data_type: "any",
      label: new TranslatableMarkup("View filters"),
      required: FALSE,
    ),
    "contextual_filter" => new ContextDefinition(
      data_type: "any",
      label: new TranslatableMarkup("View contextual filters"),
      required: FALSE,
    ),
    "sort_key" => new ContextDefinition(
      data_type: "string",
      label: new TranslatableMarkup("Sort by key"),
      required: FALSE,
    ),
    "sort_dir" => new ContextDefinition(
      data_type: "string",
      label: new TranslatableMarkup("Sort direction"),
      required: FALSE,
    ),
  ],
)]
class ViewsExecutable extends DataProducerPluginBase implements ContainerFactoryPluginInterface {

  /**
   * Drupal renderer service.
   *
   * @var \Drupal\Core\Render\RendererInterface
   */
  protected RendererInterface $renderer;

  /**
   * Drupal entity type manager.
   *
   * @var \Drupal\Core\Entity\EntityTypeManagerInterface
   */
  protected EntityTypeManagerInterface $entityTypeManager;

  /**
   * The request stack.
   *
   * @var \Symfony\Component\HttpFoundation\RequestStack
   */
  protected RequestStack $requestStack;

  /**
   * {@inheritdoc}
   */
  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
    $instance = new static(
      $configuration,
      $plugin_id,
      $plugin_definition,
    );

    $instance->renderer = $container->get('renderer');
    $instance->entityTypeManager = $container->get('entity_type.manager');
    $instance->requestStack = $container->get('request_stack');

    return $instance;
  }

  /**
   * Resolve a view executable.
   *
   * @param string $view_id
   *   The view ID.
   * @param string $display_id
   *   The view display ID.
   * @param int|null $page
   *   The page number.
   * @param int|null $page_size
   *   The page size.
   * @param int|null $offset
   *   The page offset.
   * @param array|null $filter
   *   The filters to apply.
   * @param array|null $contextual_filter
   *   The contextual filters to apply.
   * @param string|null $sort_key
   *   The sort key.
   * @param string|null $sort_dir
   *   The sort direction (ASC/DESC).
   * @param \Drupal\graphql\GraphQL\Execution\FieldContext $context
   *   The cache context.
   *
   * @return \Drupal\views\ViewExecutable|null
   *   The view executable.
   */
  public function resolve(string $view_id, string $display_id, ?int $page, ?int $page_size, ?int $offset, ?array $filter, ?array $contextual_filter, ?string $sort_key, ?string $sort_dir, FieldContext $context): ?ViewsViewExecutable {

    /** @var \Drupal\views\ViewEntityInterface|null $view_entity */
    $view_entity = $this->entityTypeManager->getStorage('view')->load($view_id);
    $context->addCacheableDependency($view_entity);

    $view = $view_entity->getExecutable();
    $view->setDisplay($display_id);

    /** @var \Drupal\graphql_compose_views\Plugin\views\display\GraphQL $display */
    $display = $view->getDisplay();

    // Add isRenderedInCurrentRequest support for Search API.
    // @see \Drupal\graphql_compose_views\Plugin\search_api\display\ViewsGraphQL
    $request = $this->requestStack->getCurrentRequest();
    $executed = (array) $request->attributes->get('_graphql_views', []);
    $executed[] = $view_id . ':' . $display_id;
    $request->attributes->set('_graphql_views', $executed);

    // Pagination enabled at a set limit.
    $pager = $display->getOption('pager');
    $is_paged = in_array($pager['type'] ?? '', [
      'full',
      'mini',
    ]);

    if ($is_paged) {
      $view->setCurrentPage($page ?? 0);

      if ($pager['options']['expose']['offset'] ?? FALSE) {
        $view->setOffset($offset ?? 0);
      }

      // Limit to enabled config.
      $items_per_page = $pager['options']['expose']['items_per_page'] ?? FALSE;
      $items_per_page_options = $pager['options']['expose']['items_per_page_options'] ?? FALSE;

      if ($page_size && $items_per_page && $items_per_page_options) {
        $allowed = array_map('trim', explode(',', $items_per_page_options));
        if (in_array($page_size, $allowed)) {
          $view->setItemsPerPage($page_size);
        }
      }
    }

    // Exposed input filters.
    $exposed_filters = array_filter(
      $display->getHandlers('filter'),
      fn (FilterPluginBase $filter) => $filter->isExposed()
    );

    $exposed_filters = array_map(
      fn (FilterPluginBase $filter) => $filter->exposedInfo()['value'],
      $exposed_filters
    );

    // Default exposed input.
    $exposed_input = $view->getExposedInput();
    $filter_input = [];

    foreach ($filter ?: [] as $key => $value) {
      $filter_input[$key] = $value;

      // Remap any incoming KeyValueInput pairs.
      if (is_array($value) && isset($value['key'], $value['value'])) {
        $filter_input[$value['key']] = $value['value'];
      }
    }

    // Only allow exposed filters in exposed_input.
    foreach ($filter_input ?: [] as $key => $value) {
      if (in_array($key, $exposed_filters)) {
        $exposed_input[$key] = is_bool($value) ? (string) intval($value) : $value;
      }
    }

    // Sorts.
    $exposed_sorts = array_filter(
      $display->getOption('sorts') ?: [],
      fn ($filter) => !empty($filter['exposed'])
    );

    if ($exposed_sorts && $sort_key) {
      $sort_key = strtolower($sort_key);
      $sort_enums = array_change_key_case($display->getGraphQlSortEnums());
      if (array_key_exists($sort_key, $sort_enums)) {
        $exposed_input['sort_by'] = $sort_enums[$sort_key]['value'];
      }
    }

    // Set sort order.
    $exposed_sort_dir = $display->getOption('exposed_form')['options']['expose_sort_order'] ?? TRUE;
    if ($exposed_sort_dir && $sort_dir) {
      $exposed_input['sort_order'] = $sort_dir === 'ASC' ? 'ASC' : 'DESC';
    }

    // Construct contextual filters.
    // Contextual args are a bit yolo.
    $context_args = [];
    foreach ($contextual_filter ?: [] as $value) {
      $context_args[] = is_bool($value) ? (string) intval($value) : $value;
    }

    // Execute the view.
    $render_context = new RenderContext();

    $executed_view = $this->renderer->executeInRenderContext(
      $render_context,
      function () use ($view, $context_args, $exposed_input) {
        $view->setExposedInput($exposed_input);
        $view->preExecute($context_args);
        $view->execute();
        return $view;
      }
    );

    if (!$render_context->isEmpty()) {
      $context->addCacheableDependency($render_context->pop());
    }

    return $executed_view;
  }

}

Главная | Обратная связь

drupal hosting | друпал хостинг | it patrol .inc