View source  
  <?php
namespace Drupal\graphql\SchemaProvider;
use Drupal\Core\Entity\ContentEntityInterface;
use Drupal\Core\Entity\EntityManagerInterface;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Entity\TypedData\EntityDataDefinitionInterface;
use Drupal\Core\Field\FieldDefinitionInterface;
use Drupal\Core\TypedData\TypedDataManager;
use Drupal\graphql\TypeResolverInterface;
use Drupal\graphql\Utility\StringHelper;
use Fubhy\GraphQL\Language\Node;
use Fubhy\GraphQL\Type\Definition\Types\EnumType;
use Fubhy\GraphQL\Type\Definition\Types\ListModifier;
use Fubhy\GraphQL\Type\Definition\Types\ModifierInterface;
use Fubhy\GraphQL\Type\Definition\Types\NonNullModifier;
use Fubhy\GraphQL\Type\Definition\Types\ObjectType;
use Fubhy\GraphQL\Type\Definition\Types\ScalarType;
use Fubhy\GraphQL\Type\Definition\Types\Type;
class EntitySchemaProvider extends SchemaProviderBase {
  
  protected $entityManager;
  
  protected $typeResolver;
  
  protected $typedDataManager;
  
  public function __construct(EntityManagerInterface $entityManager, TypedDataManager $typedDataManager, TypeResolverInterface $typeResolver) {
    $this->entityManager = $entityManager;
    $this->typeResolver = $typeResolver;
    $this->typedDataManager = $typedDataManager;
  }
  
  public function getQuerySchema() {
    $fields = [];
    
    $types = array_filter($this->entityManager
      ->getDefinitions(), function (EntityTypeInterface $entityType) {
      return $entityType
        ->isSubclassOf('\\Drupal\\Core\\Entity\\ContentEntityInterface');
    });
    
    $names = StringHelper::formatPropertyNameList(array_keys($types));
    foreach ($types as $key => $type) {
      
      $definition = $this->typedDataManager
        ->createDataDefinition("entity:{$key}");
      if (!($resolved = $this->typeResolver
        ->resolveRecursive($definition))) {
        continue;
      }
      $fields[$names[$key]] = [
        'type' => $resolved,
        'args' => [
          'id' => [
            'type' => new NonNullModifier(Type::intType()),
          ],
        ],
        'resolve' => [
          __CLASS__,
          'getEntitySingle',
        ],
        'resolveData' => [
          'type' => $key,
        ],
      ];
      $arguments = $this
        ->getQueryArguments($definition);
      $argumentNames = StringHelper::formatPropertyNameList(array_keys($arguments));
      $fields["{$names[$key]}Query"] = [
        'type' => new ListModifier($resolved),
        'args' => [
          'offset' => [
            'type' => Type::intType(),
          ],
          'limit' => [
            'type' => Type::intType(),
          ],
        ] + array_combine($argumentNames, $arguments),
        'resolve' => [
          __CLASS__,
          'getEntityList',
        ],
        'resolveData' => [
          'type' => $key,
          'args' => array_flip($argumentNames),
        ],
      ];
    }
    return $fields;
  }
  
  protected function getQueryArguments(EntityDataDefinitionInterface $definition) {
    $args = [];
    foreach ($definition
      ->getPropertyDefinitions() as $fieldName => $fieldDefinition) {
      if (!$fieldDefinition instanceof FieldDefinitionInterface) {
        continue;
      }
      $storage = $fieldDefinition
        ->getFieldStorageDefinition();
      if (!$storage
        ->isQueryable()) {
        continue;
      }
      
      $mainPropertyName = $storage
        ->getMainPropertyName();
      $mainProperty = $storage
        ->getPropertyDefinition($mainPropertyName);
      if (!($propertyType = $this->typeResolver
        ->resolveRecursive($mainProperty))) {
        continue;
      }
      $wrappedType = $propertyType;
      
      while ($wrappedType instanceof ModifierInterface) {
        $wrappedType = $wrappedType
          ->getWrappedType();
      }
      
      if (!($wrappedType instanceof ScalarType || $wrappedType instanceof EnumType)) {
        continue;
      }
      $args[$fieldName] = [
        'type' => new ListModifier($wrappedType),
        'description' => $fieldDefinition
          ->getDescription(),
      ];
    }
    return $args;
  }
  
  public static function getEntitySingle($source, array $args = NULL, $root, Node $field, $a, $b, $c, $data) {
    
    $storage = \Drupal::entityManager()
      ->getStorage($data['type']);
    if ($entity = $storage
      ->load($args['id'])) {
      return $entity
        ->getTypedData();
    }
    return NULL;
  }
  
  public static function getEntityList($source, array $args = NULL, $root, Node $field, $a, $b, $c, $data) {
    $storage = \Drupal::entityManager()
      ->getStorage($data['type']);
    $query = $storage
      ->getQuery()
      ->accessCheck(TRUE);
    $rangeArgs = array(
      'offset',
      'limit',
    );
    $filterArgs = array_diff_key($args, array_flip($rangeArgs));
    foreach ($filterArgs as $key => $arg) {
      if (isset($arg) && isset($data['args'][$key])) {
        $arg = is_array($arg) && sizeof($arg) === 1 ? reset($arg) : $arg;
        $operator = is_array($arg) ? 'IN' : '=';
        $query
          ->condition($data['args'][$key], $arg, $operator);
      }
    }
    if (!empty($args['offset']) || !empty($args['limit'])) {
      $query
        ->range($args['offset'] ?: NULL, $args['limit'] ?: NULL);
    }
    $result = $query
      ->execute();
    if (!empty($result)) {
      $entities = $storage
        ->loadMultiple($result);
      
      return array_map(function (ContentEntityInterface $entity) {
        return $entity
          ->getTypedData();
      }, array_filter($entities, function (ContentEntityInterface $entity) {
        return $entity
          ->access('view');
      }));
    }
    return [];
  }
}