Skip to content

Commit 4a48bde

Browse files
committed
Merge branch 'feature/GQL-47' into develop
2 parents 822f339 + 1c37e3e commit 4a48bde

File tree

13 files changed

+581
-19
lines changed

13 files changed

+581
-19
lines changed

Changelog.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,11 @@
22

33
The change log describes what is "Added", "Removed", "Changed" or "Fixed" between each release.
44

5+
## In Progress
6+
7+
- Add support for variables attachment to query
8+
- Add support for variables passing query
9+
- Support for input objects variables
510

611
## 1.1: 2019-04-26
712

README.md

Lines changed: 53 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,34 @@ The RawObject class being constructed is used for injecting the string into the
111111
into the RawObject constructor will be put in the query as it is without any custom formatting normally done by the
112112
query class.
113113

114+
## Query With Variables
115+
```
116+
$gql = (new Query('companies'))
117+
->setVariables(
118+
[
119+
new Variable('name', 'String', true),
120+
new Variable('limit', 'Int', false, 5)
121+
]
122+
)
123+
->setArguments(['name' => '$name', 'first' => '$limit'])
124+
->setSelectionSet(
125+
[
126+
'name',
127+
'serialNumber'
128+
]
129+
);
130+
```
131+
This query shows how variables can be used in this package to allow for dynamic requests enabled by GraphQL standards.
132+
133+
### The Variable Class
134+
The Variable class is an immutable class that represents a variable in GraphQL standards. Its constructor receives 4
135+
arguments:
136+
- name: Represents the variable name
137+
- type: Represents the variable type according to the GraphQL server schema
138+
- isRequired (Optional): Represents if the variable is required or not, it's false by default
139+
- defaultValue (Optional): Represents the default value to be assigned to the variable. The default value will only be
140+
considered if the isRequired argument is set to false.
141+
114142
# The Query Builder
115143
The QueryBuilder class can be used to construct Query objects dynamically, which can be useful in some cases. It works
116144
very similarly to the Query class, but the Query building is divided into steps.
@@ -119,7 +147,8 @@ That's how the "Query With Input Object Argument" example can be created using t
119147
QueryBuilder:
120148
```
121149
$builder = (new QueryBuilder('companies'))
122-
->setArgument('filter', new RawObject('{name_starts_with: "Face"}'))
150+
->setVariable('namePrefix', 'String', true)
151+
->setArgument('filter', new RawObject('{name_starts_with: $namePrefix}'))
123152
->selectField('name')
124153
->selectField('serialNumber');
125154
$gql = $builder->getQuery();
@@ -139,7 +168,7 @@ $client = new Client(
139168
```
140169

141170
# Running Queries
142-
171+
## Result Formatting
143172
Running query with the GraphQL client and getting the results in object structure:
144173
```
145174
$results = $client->runQuery($gql);
@@ -151,6 +180,28 @@ $results = $client->runQuery($gql, true);
151180
$results->getData()['Company'][1]['branches']['address']
152181
```
153182

183+
## Passing Variables to Queries
184+
Running queries containing variables requires passing an associative array which maps variable names (keys) to variable
185+
values (values) to the `runQuery` method. Here's an example:
186+
```
187+
$gql = (new Query('companies'))
188+
->setVariables(
189+
[
190+
new Variable('name', 'String', true),
191+
new Variable('limit', 'Int', false, 5)
192+
]
193+
)
194+
->setArguments(['name' => '$name', 'first' => '$limit'])
195+
->setSelectionSet(
196+
[
197+
'name',
198+
'serialNumber'
199+
]
200+
);
201+
$variablesArray = ['name' => 'Tech Co.', 'first' => 5];
202+
$results = $client->runQuery($gql, true, $variablesArray);
203+
```
204+
154205
# Mutations
155206
Mutations follow the same rules of queries in GraphQL, they select fields on returned objects, receive arguments, and
156207
can have sub-fields.

src/Client.php

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -45,11 +45,12 @@ public function __construct(string $endpointUrl, array $authorizationHeaders = [
4545
/**
4646
* @param Query|QueryBuilderInterface $query
4747
* @param bool $resultsAsArray
48+
* @param array $variables
4849
*
4950
* @return Results|null
5051
* @throws QueryError
5152
*/
52-
public function runQuery($query, bool $resultsAsArray = false): ?Results
53+
public function runQuery($query, bool $resultsAsArray = false, array $variables = []): ?Results
5354
{
5455
if ($query instanceof QueryBuilderInterface) {
5556
$query = $query->getQuery();
@@ -59,17 +60,18 @@ public function runQuery($query, bool $resultsAsArray = false): ?Results
5960
throw new TypeError('Client::runQuery accepts the first argument of type Query or QueryBuilderInterface');
6061
}
6162

62-
return $this->runRawQuery((string) $query, $resultsAsArray);
63+
return $this->runRawQuery((string) $query, $resultsAsArray, $variables);
6364
}
6465

6566
/**
6667
* @param string $queryString
6768
* @param bool $resultsAsArray
69+
* @param array $variables
6870
*
6971
* @return Results|null
7072
* @throws QueryError
7173
*/
72-
public function runRawQuery(string $queryString, $resultsAsArray = false): ?Results
74+
public function runRawQuery(string $queryString, $resultsAsArray = false, array $variables = []): ?Results
7375
{
7476
// Set request headers for authorization and content type
7577
if (!empty($this->authorizationHeaders)) {
@@ -78,7 +80,8 @@ public function runRawQuery(string $queryString, $resultsAsArray = false): ?Resu
7880
$options['headers']['Content-Type'] = 'application/json';
7981

8082
// Set query in the request body
81-
$options['body'] = json_encode(['query' => (string) $queryString]);
83+
$bodyArray = ['query' => (string) $queryString, 'variables' => $variables];
84+
$options['body'] = json_encode($bodyArray);
8285

8386
// Send api request and get response
8487
try {
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<?php
2+
3+
namespace GraphQL\Exception;
4+
5+
use InvalidArgumentException;
6+
7+
/**
8+
* Class InvalidVariableException
9+
*
10+
* @package GraphQL\Exception
11+
*/
12+
class InvalidVariableException extends InvalidArgumentException
13+
{
14+
/**
15+
* InvalidVariableException constructor.
16+
*
17+
* @param string $message
18+
*/
19+
public function __construct($message = "")
20+
{
21+
parent::__construct($message);
22+
}
23+
}

src/Query.php

Lines changed: 99 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,10 @@
22

33
namespace GraphQL;
44

5+
use Exception;
56
use GraphQL\Exception\ArgumentException;
67
use GraphQL\Exception\InvalidSelectionException;
8+
use GraphQL\Exception\InvalidVariableException;
79
use GraphQL\Util\StringLiteralFormatter;
810

911
/**
@@ -27,12 +29,26 @@ class Query
2729
*/
2830
protected const OPERATION_TYPE = 'query';
2931

32+
/**
33+
* Stores the name of the operation to be run on the server
34+
*
35+
* @var string
36+
*/
37+
private $operationName;
38+
3039
/**
3140
* Stores the object being queried for
3241
*
3342
* @var string
3443
*/
35-
private $object;
44+
private $fieldName;
45+
46+
/**
47+
* Stores the list of variables to be used in the query
48+
*
49+
* @var array|Variable[]
50+
*/
51+
private $variables;
3652

3753
/**
3854
* Stores the list of arguments used when querying data
@@ -58,14 +74,49 @@ class Query
5874
/**
5975
* GQLQueryBuilder constructor.
6076
*
61-
* @param string $objectName
77+
* @param string $fieldName
78+
*/
79+
public function __construct(string $fieldName)
80+
{
81+
$this->fieldName = $fieldName;
82+
$this->operationName = '';
83+
$this->variables = [];
84+
$this->arguments = [];
85+
$this->selectionSet = [];
86+
$this->isNested = false;
87+
}
88+
89+
/**
90+
* @param string $operationName
91+
*
92+
* @return Query
93+
*/
94+
public function setOperationName(string $operationName)
95+
{
96+
if (!empty($operationName)) {
97+
$this->operationName = " $operationName";
98+
}
99+
100+
return $this;
101+
}
102+
103+
/**
104+
* @param array $variables
105+
*
106+
* @return Query
62107
*/
63-
public function __construct(string $objectName)
108+
public function setVariables(array $variables)
64109
{
65-
$this->object = $objectName;
66-
$this->arguments = [];
67-
$this->selectionSet = [];
68-
$this->isNested = false;
110+
$nonVarElements = array_filter($variables, function($e) {
111+
return !$e instanceof Variable;
112+
});
113+
if (count($nonVarElements) > 0) {
114+
throw new InvalidVariableException('At least one of the elements of the variables array provided is not an instance of GraphQL\\Variable');
115+
}
116+
117+
$this->variables = $variables;
118+
119+
return $this;
69120
}
70121

71122
/**
@@ -116,6 +167,34 @@ public function setSelectionSet(array $selectionSet): Query
116167
return $this;
117168
}
118169

170+
/**
171+
* @return string
172+
*/
173+
protected function constructVariables(): string
174+
{
175+
if (empty($this->variables)) {
176+
return '';
177+
}
178+
179+
$varsString = '(';
180+
$first = true;
181+
foreach ($this->variables as $variable) {
182+
183+
// Append space at the beginning if it's not the first item on the list
184+
if ($first) {
185+
$first = false;
186+
} else {
187+
$varsString .= ' ';
188+
}
189+
190+
// Append variable string value to the variables string
191+
$varsString .= (string) $variable;
192+
}
193+
$varsString .= ')';
194+
195+
return $varsString;
196+
}
197+
119198
/**
120199
* @return string
121200
*/
@@ -188,13 +267,23 @@ protected function constructSelectionSet(): string
188267
public function __toString()
189268
{
190269
$queryFormat = static::QUERY_FORMAT;
191-
if (!$this->isNested && $this->object !== static::OPERATION_TYPE) {
192-
$queryFormat = static::OPERATION_TYPE . " {\n" . static::QUERY_FORMAT . "\n}";
270+
if (!$this->isNested && $this->fieldName !== static::OPERATION_TYPE) {
271+
$queryFormat = $this->generateSignature() . " {\n" . static::QUERY_FORMAT . "\n}";
193272
}
194273
$argumentsString = $this->constructArguments();
195274
$selectionSetString = $this->constructSelectionSet();
196275

197-
return sprintf($queryFormat, $this->object, $argumentsString, $selectionSetString);
276+
return sprintf($queryFormat, $this->fieldName, $argumentsString, $selectionSetString);
277+
}
278+
279+
/**
280+
* @return string
281+
*/
282+
protected function generateSignature(): string
283+
{
284+
$signatureFormat = '%s%s%s';
285+
286+
return sprintf($signatureFormat, static::OPERATION_TYPE, $this->operationName, $this->constructVariables());
198287
}
199288

200289
/**

src/QueryBuilder/AbstractQueryBuilder.php

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
use GraphQL\Exception\EmptySelectionSetException;
66
use GraphQL\Query;
77
use GraphQL\RawObject;
8+
use GraphQL\Variable;
89

910
/**
1011
* Class AbstractQueryBuilder
@@ -18,6 +19,11 @@ abstract class AbstractQueryBuilder implements QueryBuilderInterface
1819
*/
1920
private $query;
2021

22+
/**
23+
* @var array|Variable[]
24+
*/
25+
private $variables;
26+
2127
/**
2228
* @var array
2329
*/
@@ -36,6 +42,7 @@ abstract class AbstractQueryBuilder implements QueryBuilderInterface
3642
public function __construct(string $queryObject)
3743
{
3844
$this->query = new Query($queryObject);
45+
$this->variables = [];
3946
$this->selectionSet = [];
4047
$this->argumentsList = [];
4148
}
@@ -56,6 +63,7 @@ public function getQuery(): Query
5663
}
5764
}
5865

66+
$this->query->setVariables($this->variables);
5967
$this->query->setArguments($this->argumentsList);
6068
$this->query->setSelectionSet($this->selectionSet);
6169

@@ -90,4 +98,19 @@ protected function setArgument(string $argumentName, $argumentValue)
9098

9199
return $this;
92100
}
101+
102+
/**
103+
* @param string $name
104+
* @param string $type
105+
* @param bool $isRequired
106+
* @param null $defaultValue
107+
*
108+
* @return $this
109+
*/
110+
protected function setVariable(string $name, string $type, bool $isRequired = false, $defaultValue = null)
111+
{
112+
$this->variables[] = new Variable($name, $type, $isRequired, $defaultValue);
113+
114+
return $this;
115+
}
93116
}

0 commit comments

Comments
 (0)