Empty args array is passed into GraphQLType fields() function, although it's correct in query resolve().
The cause is line 99 in Rebing\GraphQL\Support\SelectFields.php where $requestedFields['args'] is already empty.
Previous graphql version used self::$args instead, that contains the correct data.
Execute a query with some arguments and try to access these from GraphQLType fields().
Can you describe a bit more your use case why you would need (the now non-existent) self::$args (basically the "global" one from the resolver) deep down in a relation query?
Thanks
Here's an example
class TestType extends GraphQLType
{
public function fields(): array
{
return [
'test' => [
'type' => Type::listOf(GraphQL::type('test')),
'query' => function (array $args, $query) {
// I need to use values from $args here
return $query;
},
],
];
}
}
$requestedFields['args'] is correct at the start of getSelectableFieldsAndRelations function, self::handleFields(...) line strips it out.
Can you give an example of a GraphQL query (with args) where the type using the 'query' is used in?
A simplified example follows.
GraphQL query
{
getDocuments(company_id: 77) {
id
comment
text
}
}
PaginationQuery class
class DocumentsQuery extends PaginationQuery
{
public function type(): Type
{
return GraphQL::paginate('document');
}
public function args(): array
{
return array_merge(parent::args(), [
'company_id' => [
'type' => Type::nonNull(Type::int()),
'rules' => ['required'],
],
]);
}
public function resolve($root, $args, $context, ResolveInfo $info, Closure $getSelectFields)
{
$fields = $getSelectFields();
$with = $fields->getRelations();
$select = $fields->getSelect();
return Repo::document()->companyDocs($args['company_id'], $search, $with, $select);
}
}
Type
class DocumentType extends GraphQLType
{
protected $attributes = [
'name' => 'Document',
'model' => Document::class,
];
public function fields(): array
{
return [
'id' => [
'type' => Type::nonNull(Type::int()),
],
'comments' => [
'type' => Type::listOf(GraphQL::type('comment')),
'query' => function (array $args, $query) {
if (array_has($args, 'company_id')) {
return $query->where('company_id', '=', $args['company_id']);
}
return $query;
},
],
];
}
}
I think the correct solution is to add the 'args' to the 'comments' relation:
'comments' => [
'type' => Type::listOf(GraphQL::type('comment')),
'args' => [
'company_id' => [
'type' => Type::nonNull(Type::int()),
'rules' => ['required'],
],
],
'query' => function (array $args, $query) {
if (array_has($args, 'company_id')) {
return $query->where('company_id', '=', $args['company_id']);
}
return $query;
},
],
Then the query would become like this:
{
getDocuments(company_id: 77) {
id
comment(company_id: 77) {
text
}
}
Usually the repetitiveness is handled by using variables within the query:
query getDocuments($company_id: int!){
getDocuments(company_id: $company_id) {
id
comment(company_id: $company_id) {
text
}
}
Note: I think in this example, in practice, you would not have
int!for the'args'on'commens', but ratherint; I think in this case it also means that the query needs to define two variables because the types need to match (intvs.int!); YMMV
Here is the initial bug report for it: https://github.com/rebing/graphql-laravel/issues/314
Although I created the report, I did not come up with the problem, rather it was discovered as part of https://github.com/rebing/graphql-laravel/issues/310 when I investigating another issue reported.
Here is the PR which changed / fixed this: https://github.com/rebing/graphql-laravel/pull/327
It's quite some ground to cover so I try to summarize it briefly:
'args' from the "root object" === the GraphQL query object were passed on to custom 'query'IMO this change was correct to make: passing arbitrary top level data to a deeply nested query does not make sense and totally bypassing GraphQL type expectations; that's why those args have to be locally defined on the relation.
It seems it's a breaking change from 1.x to 2.0 which we missed to properly document in the upgrade guide :/
Please let me know what you think about this, thank you.
Thank you for the explanation, it makes sense.
I've currently fixed my use case and also made the tests pass by merging the query and field level arguments. Field arguments should overwrite the matching global ones, so this fix shouldn't break anything.
I have to evaluate the query refactoring needed to use the field level arguments.
@mfn You are right that the correct solution would be to modify the query to use sub-arguments. However, I think we should merge this PR for now, as it would solve the current problem and it should not break anything else. It's also like this in the documentation: https://github.com/rebing/graphql-laravel#type-relationship-query
The problem with the merge is that the 'query' does not know where it came from:
'args' named 'flag' of type bool'query'As long you pass the 'flag' to the GraphQL query, your relation 'query' also receives it and it can't decide whether it was meant to use it or not.
I was about to write a test for the PR when I realized this. This can lead to big confusions. We need another approach here.
Everything in GraphQL relies on parent/child relationships. You can't skip a level and you can only access your direct parents information.
Obviously via ResolveInfo you can provide some hacks but only "top down"; even as a child you can't access "higher up" nodes in the hierarchy.
However, the GraphQL spec created one thing which is shared between all resolvers within a query/mutation: the "context".
Therefore I believe a better solution would be to:
'query'Context class by overriding \Rebing\GraphQL\GraphQLController::queryContext'query' and it can decide whether to mutate the $query based on data from the context or the local 'args'The signature would be:
'query' => function (array $args, $query, $ctx) {
…
},
And $ctx is whatever queryContext returned.
I think I understand what drives the desire for the fix: this is one of the breaking changes which affects consumers of GraphQL and are much harder to contain.
However I stand by my opinion that the internal behaviour of the passed 'args' itself should not be changed (un-intuitive, hard to debug problems) and I think my proposal allows to fix this so consumers are not affected.
I'm willing to take care of the implementation/test and docs if we consent on this.
@mfn I agree with your considerations.
We can forward arbitrary data via context to the query. It's also trivial to refactor our existing code to use this feature as opposed to changing the client queries.
Roger, will move forward here ASAP
@rebing @nurges Please review https://github.com/rebing/graphql-laravel/pull/464
Seems ok to me