graphql_compose-1.0.0-beta20/modules/graphql_compose_views/src/Plugin/GraphQLCompose/SchemaType/View.php

modules/graphql_compose_views/src/Plugin/GraphQLCompose/SchemaType/View.php
<?php

declare(strict_types=1);

namespace Drupal\graphql_compose_views\Plugin\GraphQLCompose\SchemaType;

use Drupal\graphql_compose\Attribute\SchemaType;
use Drupal\graphql_compose\Plugin\GraphQLCompose\GraphQLComposeSchemaTypeBase;
use Drupal\graphql_compose_views\Plugin\views\display\GraphQL;
use Drupal\graphql_compose_views\Plugin\views\row\GraphQLFieldRow;
use Drupal\views\Plugin\views\filter\FilterPluginBase;
use Drupal\views\Views;
use GraphQL\Type\Definition\CustomScalarType;
use GraphQL\Type\Definition\EnumType;
use GraphQL\Type\Definition\InputObjectType;
use GraphQL\Type\Definition\InterfaceType;
use GraphQL\Type\Definition\ObjectType;
use GraphQL\Type\Definition\Type;
use GraphQL\Type\Definition\UnionType;

/**
 * {@inheritdoc}
 */
#[SchemaType(
  id: "View",
)]
class View extends GraphQLComposeSchemaTypeBase {

  /**
   * {@inheritdoc}
   *
   * Add dynamic view types that use View interface.
   */
  public function getTypes(): array {
    $types = [];

    $types[] = new InterfaceType([
      'name' => $this->getPluginId(),
      'description' => (string) $this->t('Views represent collections of curated data from the CMS.'),
      'fields' => fn() => [
        'id' => [
          'type' => Type::nonNull(Type::id()),
          'description' => (string) $this->t('The ID of the view.'),
        ],
        'view' => [
          'type' => Type::nonNull(Type::string()),
          'description' => (string) $this->t('The machine name of the view.'),
        ],
        'display' => [
          'type' => Type::nonNull(Type::string()),
          'description' => (string) $this->t('The machine name of the display.'),
        ],
        'langcode' => [
          'type' => Type::string(),
          'description' => (string) $this->t('The language code of the view.'),
        ],
        'label' => [
          'type' => Type::string(),
          'description' => (string) $this->t('The human friendly label of the view.'),
        ],
        'description' => [
          'type' => Type::string(),
          'description' => (string) $this->t('The description of the view.'),
        ],
        'pageInfo' => [
          'type' => Type::nonNull(static::type('ViewPageInfo')),
          'description' => (string) $this->t('Information about the page in the view.'),
        ],
      ],
    ]);

    $viewStorage = $this->entityTypeManager->getStorage('view');

    $union_types = [];

    foreach (Views::getApplicableViews('graphql_display') as $applicable_view) {
      // Destructure view and display ids.
      [$view_id, $display_id] = $applicable_view;

      if (!$view_entity = $viewStorage->load($view_id)) {
        continue;
      }

      /** @var \Drupal\views\ViewEntityInterface|null $view_entity */
      $view = $view_entity->getExecutable();
      $view->setDisplay($display_id);
      $view->initHandlers();

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

      $exposed_filters = $display->usesExposed();
      $result_type = $display->getGraphQlResultType();

      // Get the description for the view.
      $view_description = $view->storage->get('description') ?: $this->t('Result for view @view display @display.', [
        '@view' => $view_id,
        '@display' => $display_id,
      ]);

      // Create type for view base on View Interface.
      $types[] = new ObjectType([
        'name' => $display->getGraphQlResultName(),
        'description' => (string) $view_description,
        'interfaces' => fn () => [static::type('View')],
        'fields' => fn() => array_filter([
          'id' => [
            'type' => Type::nonNull(Type::id()),
            'description' => (string) $this->t('The ID of the view.'),
          ],
          'view' => [
            'type' => Type::nonNull(Type::string()),
            'description' => (string) $this->t('The machine name of the view.'),
          ],
          'display' => [
            'type' => Type::nonNull(Type::string()),
            'description' => (string) $this->t('The machine name of the display.'),
          ],
          'langcode' => [
            'type' => Type::string(),
            'description' => (string) $this->t('The language code of the view.'),
          ],
          'label' => [
            'type' => Type::string(),
            'description' => (string) $this->t('The human friendly label of the view.'),
          ],
          'description' => [
            'type' => Type::string(),
            'description' => (string) $this->t('The description of the view.'),
          ],
          'pageInfo' => [
            'type' => Type::nonNull(static::type('ViewPageInfo')),
            'description' => (string) $this->t('Information about the page in the view.'),
          ],
          'filters' => $exposed_filters ? [
            'type' => Type::nonNull(Type::listOf(static::type('ViewFilter'))),
            'description' => (string) $this->t('Exposed filters for the view.'),
          ] : [],
          'results' => [
            'type' => Type::nonNull(Type::listOf(Type::nonNull(static::type($result_type)))),
            'description' => (string) $this->t('The results of the view.'),
          ],
        ]),
      ]);

      // Keep a union of all the view types.
      $union_types[] = $display->getGraphQlResultName();

      $types = [
        ...$types,
        ...$this->getSortTypes($display),
        ...$this->getFieldTypes($display),
        ...$this->getUnionTypes($display),
        ...$this->getFilterTypes($display),
        ...$this->getContextualFilterTypes($display),
      ];

      $view->destroy();
    }

    // Create type for view base on View Interface.
    $types[] = new UnionType([
      'name' => 'ViewResultUnion',
      'description' => (string) $this->t('All available view result types.'),
      'types' => fn() => array_map(
        static::type(...),
        $union_types ?: ['UnsupportedType']
      ),
    ]);

    return $types;
  }

  /**
   * {@inheritdoc}
   */
  public function getExtensions(): array {
    $extensions = parent::getExtensions();

    $viewStorage = $this->entityTypeManager->getStorage('view');

    foreach (Views::getApplicableViews('graphql_display') as $applicable_view) {
      // Destructure view and display ids.
      [$view_id, $display_id] = $applicable_view;

      if (!$view_entity = $viewStorage->load($view_id)) {
        continue;
      }

      /** @var \Drupal\views\ViewEntityInterface|null $view_entity */
      $view = $view_entity->getExecutable();
      $view->setDisplay($display_id);

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

      if (!$display->getOption('graphql_query_exposed')) {
        continue;
      }

      // Get the description for the view.
      $view_description = $view->storage->get('description') ?: $this->t('Query for view @view display @display.', [
        '@view' => $view_id,
        '@display' => $display_id,
      ]);

      $query = $display->getGraphQlQueryName();
      $type = $display->getGraphQlResultName();

      $args = [
        ...$this->getSortArgs($display),
        ...$this->getPagerArgs($display),
        ...$this->getFilterArgs($display),
        ...$this->getContextualFilterArgs($display),
      ];

      $extensions[] = new ObjectType([
        'name' => 'Query',
        'fields' => fn() => [
          $query => [
            'type' => static::type($type),
            'description' => (string) $view_description,
            'args' => $args,
          ],
        ],
      ]);

      $view->destroy();
    }

    return $extensions;
  }

  /**
   * Get union types for entity row display.
   *
   * @param \Drupal\graphql_compose_views\Plugin\views\display\GraphQL $display
   *   The view display.
   *
   * @return \GraphQL\Type\Definition\UnionType[]
   *   Union types.
   */
  public function getUnionTypes(GraphQL $display) {
    $types = [];

    if ($display->hasGraphQlUnionTypes()) {
      $union_types = $display->getGraphQlUnionTypes();

      $types[] = new UnionType([
        'name' => $display->getGraphQlRowName(),
        'description' => (string) $this->t('All available types for view result row.'),
        'types' => fn() => array_map(
          static::type(...),
          $union_types,
        ),
      ]);
    }

    return $types;
  }

  /**
   * Get sort types for display.
   *
   * @param \Drupal\graphql_compose_views\Plugin\views\display\GraphQL $display
   *   The view display.
   *
   * @return \GraphQL\Type\Definition\EnumType[]
   *   Sort types.
   */
  private function getSortTypes(GraphQL $display): array {
    $types = [];

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

    if (!empty($exposed_sorts)) {
      $types[] = new EnumType([
        'name' => $display->getGraphQlSortInputName(),
        'values' => $display->getGraphQlSortEnums(),
      ]);
    }

    return $types;
  }

  /**
   * Get sort args for display.
   *
   * @param \Drupal\graphql_compose_views\Plugin\views\display\GraphQL $display
   *   The view display.
   *
   * @return array
   *   Sort args for GraphQL.
   */
  private function getSortArgs(GraphQL $display): array {
    $args = [];

    // Pagination enabled at a set limit.
    $exposed_sorts = array_filter(
      $display->getOption('sorts') ?: [],
      fn ($filter) => !empty($filter['exposed'])
    );

    if ($exposed_sorts) {
      $args['sortKey'] = [
        'type' => static::type($display->getGraphQlSortInputName()),
        'description' => (string) $this->t('Sort the view by this key.'),
      ];
    }

    if ($display->getOption('exposed_form')['options']['expose_sort_order'] ?? FALSE) {
      $args['sortDir'] = [
        'type' => static::type('SortDirection'),
        'description' => (string) $this->t('Sort the view direction.'),
      ];
    }

    return $args;
  }

  /**
   * Get pager types for display.
   *
   * @param \Drupal\graphql_compose_views\Plugin\views\display\GraphQL $display
   *   The view display.
   *
   * @return array
   *   Pager types.
   */
  private function getPagerArgs(GraphQL $display): array {

    if (!in_array($display->getOption('pager')['type'] ?? '', ['full', 'mini'])) {
      return [];
    }

    $args = [];

    $args['page'] = [
      'type' => Type::int(),
      'description' => (string) $this->t('The page number to display.'),
      'defaultValue' => 0,
    ];

    $pager_options = $display->getOption('pager')['options'] ?? [];

    // Allow setting items per page.
    if ($pager_options['expose']['items_per_page'] ?? FALSE) {
      $args['pageSize'] = [
        'type' => Type::int(),
        'description' => (string) $this->t('@label. Allowed values are: @input.', [
          '@label' => $pager_options['expose']['items_per_page_label'],
          '@input' => $pager_options['expose']['items_per_page_options'],
        ]),
        'defaultValue' => $pager_options['items_per_page'] ?? 10,
      ];
    }

    if ($pager_options['expose']['offset'] ?? FALSE) {
      $args['offset'] = [
        'type' => Type::int(),
        'description' => (string) $this->t('@label. The number of items skipped from beginning of this view.', [
          '@label' => $pager_options['expose']['offset_label'],
        ]),
        'defaultValue' => $pager_options['offset'] ?? 0,
      ];
    }

    return $args;
  }

  /**
   * Get field types for display.
   *
   * @param \Drupal\graphql_compose_views\Plugin\views\display\GraphQL $display
   *   The view display.
   *
   * @return \GraphQL\Type\Definition\Type[]
   *   Field types.
   */
  private function getFieldTypes(GraphQL $display): array {
    $types = [];
    $type_fields = [];

    $row_plugin = $display->getPlugin('row');
    if (!$row_plugin instanceof GraphQLFieldRow) {
      return [];
    }

    $fields = $display->view->display_handler->getOption('fields') ?: [];

    // Filter out the excluded fields.
    $fields = array_filter(
      $fields,
      fn(array $field) => empty($field['exclude'])
    );

    foreach ($fields as $id => $field) {
      // Alias and type set by user.
      $field_alias = $row_plugin->getAlias($id);
      $field_type = $row_plugin->getType($id);

      // Raw output could be anything.
      // We're going to need a custom scalar and dump junk into it.
      if ($field_type === 'Scalar') {
        $types[] = $custom_scalar = new CustomScalarType([
          'name' => $display->getGraphQlName($field_alias . 'Field'),
          'description' => (string) $this->t('Output of @field. Contents unknown.', [
            '@field' => $field_alias,
          ]),
        ]);

        $type_fields[$field_alias] = $custom_scalar;
      }
      else {
        // Map the type to a new GraphQL type.
        $type_fields[$field_alias] = call_user_func([Type::class, $field_type]);
      }
    }

    $types[] = new ObjectType([
      'name' => $display->getGraphQlRowName(),
      'description' => (string) $this->t('Result for view @view display @display.', [
        '@view' => $display->view->id(),
        '@display' => $display->display['id'],
      ]),
      'fields' => fn() => $type_fields,
    ]);

    return $types;
  }

  /**
   * Get filter types for display.
   *
   * @param \Drupal\graphql_compose_views\Plugin\views\display\GraphQL $display
   *   The view display.
   *
   * @return \GraphQL\Type\Definition\InputObjectType[]
   *   Filter types.
   */
  private function getFilterTypes(GraphQL $display): array {

    $types = [];
    $filter_fields = [];

    /** @var \Drupal\views\Plugin\views\filter\FilterPluginBase[] $exposed_filters */
    $exposed_filters = array_filter(
      $display->getHandlers('filter'),
      fn (FilterPluginBase $filter) => $filter->isExposed()
    );

    foreach ($exposed_filters as $filter) {
      $info = $filter->exposedInfo();

      $required = $filter->isAGroup()
        ? !$filter->options['group_info']['optional']
        : $filter->options['expose']['required'];

      $multiple = $filter->isAGroup()
        ? $filter->options['group_info']['multiple']
        : $filter->options['expose']['multiple'];

      $between = in_array($filter->operator, ['between', 'not between']);

      switch ($filter->getPluginId()) {
        case 'boolean':
        case 'search_api_boolean':
          $type = Type::boolean();
          break;

        case 'numeric':
        case 'search_api_numeric':
          $type = $between
            ? static::type('BetweenFloatInput')
            : Type::float();
          break;

        default:
          $type = $between
            ? static::type('BetweenStringInput')
            : Type::string();
          break;
      }

      if ($multiple) {
        $type = Type::listOf($type);
      }

      if ($required) {
        $type = Type::nonNull($type);
      }

      $filter_fields[$info['value']] = [
        'type' => $type,
        'description' => (string) $this->t('@label @description', [
          '@label' => $info['label'] ?? '',
          '@description' => $info['description'] ?? '',
        ]),
      ];
    }

    if (!empty($filter_fields)) {
      $types[] = new InputObjectType([
        'name' => $display->getGraphQlFilterInputName(),
        'fields' => fn() => $filter_fields,
      ]);
    }

    return $types;
  }

  /**
   * Get filter arguments for display.
   *
   * @param \Drupal\graphql_compose_views\Plugin\views\display\GraphQL $display
   *   The view display.
   *
   * @return array
   *   Filter arguments.
   */
  private function getFilterArgs(GraphQL $display): array {

    $args = [];

    /** @var \Drupal\views\Plugin\views\filter\FilterPluginBase[] $exposed_filters */
    $exposed_filters = array_filter(
      $display->getHandlers('filter'),
      fn (FilterPluginBase $filter) => $filter->isExposed()
    );

    $required_filters = array_filter(
      $exposed_filters,
      fn (FilterPluginBase $filter) => $filter->isAGroup()
        ? !$filter->options['group_info']['optional']
        : $filter->options['expose']['required']
    );

    if ($exposed_filters) {
      $type = static::type($display->getGraphQlFilterInputName());

      if ($required_filters) {
        $type = Type::nonNull($type);
      }

      $args['filter'] = [
        'type' => $type,
        'description' => (string) $this->t('Filter the view.'),
      ];
    }

    return $args;
  }

  /**
   * Get contextual filter types for display.
   *
   * @param \Drupal\graphql_compose_views\Plugin\views\display\GraphQL $display
   *   The view display.
   *
   * @return \GraphQL\Type\Definition\InputObjectType[]
   *   Contextual filter types.
   */
  private function getContextualFilterTypes(GraphQL $display): array {

    $types = [];

    $contextual_filters = $display->getOption('arguments') ?: [];

    if ($contextual_filters) {
      $types[] = new InputObjectType([
        'name' => $display->getGraphQlContextualFilterInputName(),
        'fields' => fn() => array_map(
          fn () => Type::string(),
          $contextual_filters,
        ),
      ]);
    }

    return $types;
  }

  /**
   * Get contextual filter arguments for display.
   *
   * @param \Drupal\graphql_compose_views\Plugin\views\display\GraphQL $display
   *   The view display.
   *
   * @return array
   *   Contextual filter arguments.
   */
  private function getContextualFilterArgs(GraphQL $display): array {
    $args = [];

    $contextual_filters = $display->getOption('arguments') ?: [];

    if ($contextual_filters) {
      $args['contextualFilter'] = [
        'type' => static::type($display->getGraphQlContextualFilterInputName()),
        'description' => (string) $this->t('Contextual filters for the view.'),
      ];
    }

    return $args;
  }

}

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

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