Skip to content

Instantly share code, notes, and snippets.

@jasonbahl
Last active May 25, 2022 21:01
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jasonbahl/f6cc5d3003a56edfb1362eba8857ba12 to your computer and use it in GitHub Desktop.
Save jasonbahl/f6cc5d3003a56edfb1362eba8857ba12 to your computer and use it in GitHub Desktop.
Parses a GraphQL Query and returns an array of Types that were requested
<?php
/**
* Given the Schema and a query string, return a list of GraphQL Types that are being asked for
* by the query.
*
* @param Schema $schema The WPGraphQL Schema
* @param string $query The query string
*
* @return array
* @throws SyntaxError|Exception
*/
public function get_query_types( $schema, $query ) {
if ( empty( $query ) || $schema === null ) {
return [];
}
try {
$ast = Parser::parse( $query );
} catch ( SyntaxError $error ) {
return [];
}
$type_map = [];
$type_info = new TypeInfo( $schema );
$visitor = [
'enter' => function ( $node, $key, $parent, $path, $ancestors ) use ( $type_info, &$type_map, $schema ) {
$type_info->enter( $node );
$type = $type_info->getType();
if ( ! $type ) {
return;
}
$named_type = Type::getNamedType( $type );
// determine if the field is returning a list of types
// or singular types
// @todo: this might still be too fragile. We might need to adjust for cases where we can have list_of( nonNull( type ) ), etc
$prefix = $named_type && ( $type->name === Type::listOf( $named_type )->name ) ? 'list:' : null;
if ( $named_type instanceof InterfaceType ) {
$possible_types = $schema->getPossibleTypes( $named_type );
foreach ( $possible_types as $possible_type ) {
$type_map[] = $prefix . strtolower( $possible_type );
}
} else if ( $named_type instanceof ObjectType ) {
$type_map[] = $prefix . strtolower( $named_type );
}
},
'leave' => function ( $node, $key, $parent, $path, $ancestors ) use ( $type_info ) {
$type_info->leave( $node );
},
];
Visitor::visit( $ast, Visitor::visitWithTypeInfo( $type_info, $visitor ) );
$map = array_values( array_unique( array_filter( $type_map ) ) );
return apply_filters( 'graphql_cache_collection_get_query_types', $map, $schema, $query, $type_info );
}
@jasonbahl
Copy link
Author

jasonbahl commented May 20, 2022

Given a graphql query, this returns an array of the Types being requested.

For example:

{
  posts {
    nodes {
      id
      title
    }
  }
}

returns:

[
  "rootquery",
  "rootquerytopostconnection",
  "list:post"
]

And:

{
  myCustomType {
    testUnion {
      nodes {
        __typename
        ... on Post {
          id
        }
      }
    }
  }
  posts {
    nodes {
      id
    }
  }
  contentNodes {
    nodes {
      date
      ... on Post {
        categories {
          nodes {
            id
          }
        }
        tags {
          nodes {
            id
            name
          }
        }
      }
    }
  }
}

returns:

[
  "rootquery",
  "mycustomtype",
  "mycustomtypetotestunionconnection",
  "post",
  "rootquerytopostconnection",
  "list:post",
  "rootquerytocontentnodeconnection",
  "list:cage",
  "list:mediaitem",
  "list:page",
  "list:rabbit",
  "list:testmodel",
  "list:graphqldocument",
  "posttocategoryconnection",
  "list:category",
  "posttotagconnection",
  "list:tag"
]

NOTE:
For fields that return an interface, this gets all possible types and adds them to the list. So the contentNodes query, which returns a connection to the ContentNode Interface adds all the possible types to this list:

CleanShot 2022-05-20 at 10 12 36

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment