graphql_core_schema-1.0.x-dev/src/ViewsSchemaBuilder.php

src/ViewsSchemaBuilder.php
<?php

namespace Drupal\graphql_core_schema;

use Drupal\views\ViewEntityInterface;
use Drupal\views\ViewExecutable;
use GraphQL\Type\Definition\CustomScalarType;
use GraphQL\Type\Definition\InputObjectType;
use GraphQL\Type\Definition\InterfaceType;
use GraphQL\Type\Definition\ObjectType;
use GraphQL\Type\Definition\Type;
use GraphQL\Utils\SchemaPrinter;

/**
 * The ViewsSchemaBuilder class.
 */
class ViewsSchemaBuilder {

  /**
   * The generated types.
   *
   * @var \GraphQL\Type\Definition\TypeWithFields[]
   */
  protected $types;

  /**
   * The ViewExecutable base fields.
   *
   * @var array
   */
  protected $baseFields;

  /**
   * The Entity scalar.
   *
   * Only used as a placeholder, will not be part of the generated schema.
   *
   * @var \GraphQL\Type\Definition\CustomScalarType
   */
  protected $entityScalar;

  /**
   * The enabled views and displays.
   *
   * @var string[]
   */
  protected $enabledViewDisplays;

  /**
   * Constructs a new ViewsSchemaBuilder.
   *
   * @param array $enabled
   *   List of enabled views.
   */
  public function __construct(array $enabled) {
    $this->enabledViewDisplays = $enabled;
    $this->entityScalar = new CustomScalarType(['name' => 'Entity']);
    $this->types = [];
    $this->types['ViewContextualFilter'] = new InputObjectType([
      'name' => 'ViewContextualFilter',
      'fields' => [
        'key' => Type::nonNull(Type::string()),
        'value' => Type::string(),
      ],
    ]);

    $this->types['ViewQueryParam'] = new InputObjectType([
      'name' => 'ViewQueryParam',
      'fields' => [
        'key' => Type::nonNull(Type::string()),
        'value' => Type::string(),
      ],
    ]);
    $this->types['ViewExecutableResult'] = new ObjectType([
      'name' => 'ViewExecutableResult',
      'fields' => [
        'rows' => fn () => Type::listOf($this->entityScalar),
        'total_rows' => Type::nonNull(Type::int()),
      ],
    ]);
    $this->types['ViewValue'] = new CustomScalarType([
      'name' => 'ViewValue',
    ]);
    $this->types['ViewFilterGroupItem'] = new ObjectType([
      'name' => 'ViewFilterGroupItem',
      'fields' => [
        'title' => Type::string(),
        'operator' => Type::string(),
        'value' => Type::string(),
      ],
    ]);
    $this->types['ViewFilterGroupInfo'] = new ObjectType([
      'name' => 'ViewFilterGroupInfo',
      'fields' => [
        'label' => Type::string(),
        'description' => Type::string(),
        'identifier' => Type::string(),
        'optional' => Type::boolean(),
        'widget' => Type::string(),
        'multiple' => Type::boolean(),
        'remember' => Type::boolean(),
        'defaultGroup' => Type::string(),
        'groupItems' => fn () => Type::listOf($this->types['ViewFilterGroupItem']),
      ],
    ]);
    $this->types['ViewFilter'] = new ObjectType([
      'name' => 'ViewFilter',
      'fields' => [
        'pluginId' => Type::string(),
        'baseId' => Type::string(),
        'adminLabel' => Type::string(),
        'adminLabelShort' => Type::string(),
        'adminSummary' => Type::string(),
        'field' => Type::string(),
        'realField' => Type::string(),
        'table' => Type::string(),
        'value' => fn () => $this->types['ViewValue'],
        'options' => fn () => $this->types['ViewValue'],
        'groupInfo' => fn () => $this->types['ViewFilterGroupInfo'],
        'operator' => Type::string(),
        'noOperator' => Type::boolean(),
        'alwaysRequired' => Type::boolean(),
        'isExposed' => Type::boolean(),
        'isAGroup' => Type::boolean(),
      ],
    ]);

    $this->types['ViewSort'] = new ObjectType([
      'name' => 'ViewSort',
      'fields' => [
        'pluginId' => Type::string(),
        'baseId' => Type::string(),
        'field' => Type::string(),
        'realField' => Type::string(),
      ],
    ]);
    $this->types['ViewPager'] = new ObjectType([
      'name' => 'ViewPager',
      'fields' => [
        'perPage' => Type::int(),
        'totalItems' => Type::int(),
      ],
    ]);
    $this->baseFields = [
      'filters' => fn () => Type::listOf($this->types['ViewFilter']),
      'sorts' => fn () => Type::listOf($this->types['ViewSort']),
      'pager' => fn () => $this->types['ViewPager'],
      'execute' => [
        'type' => fn () => $this->types['ViewExecutableResult'],
        'args' => [
          'page' => Type::int(),
          'limit' => Type::int(),
          'sortBy' => Type::string(),
          'sortOrder' => Type::string(),
          'contextualFilters' => fn () => Type::listOf($this->types['ViewContextualFilter']),
          'queryParams' => fn() => Type::listOf($this->types['ViewQueryParam']),
        ],
      ],
    ];
    $this->types['ViewExecutable'] = new InterfaceType([
      'name' => 'ViewExecutable',
      'fields' => $this->baseFields,
    ]);
  }

  /**
   * Get the GraphQL type for a view.
   *
   * @param \Drupal\views\ViewExecutable $executable
   *   The view executable.
   *
   * @return string
   *   The GraphQL type name for the view.
   */
  public static function getGraphqlTypeName(ViewExecutable $executable) {
    return EntitySchemaHelper::toPascalCase([
      'view',
      $executable->storage->id(),
      $executable->current_display,
    ]);
  }

  /**
   * Generate the type for the view.
   *
   * @param \Drupal\views\ViewEntityInterface $view
   *   The view entity.
   */
  public function generateViewType(ViewEntityInterface $view) {
    $displays = $view->get('display');

    foreach ($displays as $displayId => $options) {
      $key = $view->id() . ':' . $displayId;
      $executable = $view->getExecutable();
      $executable->setDisplay($displayId);

      // Only expose enabled view displays.
      if (!in_array($key, $this->enabledViewDisplays)) {
        continue;
      }

      $graphqlTypeName = self::getGraphqlTypeName($executable);
      $display = $executable->getDisplay();

      $filters = array_reduce(array_filter($display->getOption('filters') ?: [], function ($filter) {
        return array_key_exists('exposed', $filter) && $filter['exposed'];
      }), function ($carry, $current) {
        return $carry + [
          $current['expose']['identifier'] => $current,
        ];
      }, []);

      $filterFields = [];
      foreach ($filters as $filterName => $filter) {
        if ($this->isGenericInputFilter($filter)) {
          continue;
        }
        $fieldName = EntitySchemaHelper::toCamelCase($filterName);
        $filterFields[$fieldName] = $filter['expose']['multiple']
          ? Type::listOf(Type::string())
          : Type::string();
      }

      // Build field arguments.
      $args = [
        'page' => Type::int(),
        'limit' => Type::int(),
      ];

      if (!empty($filterFields)) {
        $id = implode('_', [
          $view->id(),
          $displayId,
          'view',
          'filters',
          'input',
        ]);
        $argTypeName = EntitySchemaHelper::toPascalCase($id);
        if (!isset($this->types[$argTypeName])) {
          $this->types[$argTypeName] = new InputObjectType([
            'name' => $argTypeName,
            'fields' => $filterFields,
          ]);
        }
        $args['filters'] = fn() => $this->types[$argTypeName];
      }

      // Check if sorting is enabled.
      if (!empty($display->getOption('sorts'))) {
        $args['sortBy'] = Type::string();
        $args['sortOrder'] = Type::string();
      }

      $fields = $this->baseFields;
      $fields['execute']['args'] = array_merge($fields['execute']['args'], $args);
      $this->types[$graphqlTypeName] = new ObjectType([
        'name' => $graphqlTypeName,
        'interfaces' => [fn () => $this->types['ViewExecutable']],
        'fields' => $fields,
      ]);
    }
  }

  /**
   * Checks if a filter definition is a generic input filter.
   *
   * @param mixed $filter
   *   The filter.
   *   $filter['value'] = [];
   *   $filter['value'] = [
   *     "text",
   *     "test"
   *   ];
   *   $filter['value'] = [
   *     'distance' => 10,
   *     'distance2' => 30,
   *     ...
   *   ];.
   *
   * @return bool
   *   Returns true if it is a generic filter.
   */
  public function isGenericInputFilter($filter) {
    if (!is_array($filter['value']) || count($filter['value']) == 0) {
      return FALSE;
    }

    $firstKey = array_keys($filter['value'])[0];
    return is_string($firstKey);
  }

  /**
   * Creates a definition for a generic input filter.
   *
   * @param mixed $filter
   *   The filter.
   *   $filter['value'] = [];
   *   $filter['value'] = [
   *     "text",
   *     "test"
   *   ];
   *   $filter['value'] = [
   *     'distance' => 10,
   *     'distance2' => 30,
   *     ...
   *   ];.
   *
   * @return array
   *   The filter definition.
   */
  public function createGenericInputFilterDefinition($filter) {
    $filterId = $filter['expose']['identifier'];

    $id = implode('_', [
      $filter['expose']['multiple'] ? $filterId : $filterId . '_multi',
      'view',
      'filter',
      'input',
    ]);

    $fields = [];
    foreach ($filter['value'] as $fieldKey => $fieldDefaultValue) {
      $fields[$fieldKey] = [
        'type' => Type::string(),
      ];
    }

    $genericInputFilter = [
      'id' => $id,
      'name' => EntitySchemaHelper::toPascalCase([$id]),
      'fields' => $fields,
    ];

    $this->derivatives[$id] = $genericInputFilter;

    return [
      'type' => $filter['expose']['multiple'] ? $genericInputFilter['name'] : $genericInputFilter['name'],
    ];
  }

  /**
   * Get the generated schema.
   *
   * @return string
   *   The generated schema.
   */
  public function getGeneratedSchema() {
    $result = '';
    foreach ($this->types as $type) {
      $result .= SchemaPrinter::printType($type) . "\n";
    }

    return $result;
  }

  /**
   * Returns information about view arguments (contextual filters).
   *
   * @param array $viewArguments
   *   The "arguments" option of a view display.
   *
   * @return array
   *   The argument list.
   */
  protected function getArgumentsFields(array $viewArguments) {
    $fields = [];
    foreach ($viewArguments as $argumentId => $argument) {
      $fields[$argumentId] = fn() => Type::string();
    }
    return $fields;
  }

}

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

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