Skip to content

Commit 7a35b82

Browse files
committed
Merge branch 'release/v1.2'
2 parents d76b754 + 8e9e532 commit 7a35b82

15 files changed

+666
-44
lines changed

Changelog.md

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

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

5+
## 2019-07-24
6+
7+
### Added
8+
9+
- Variables support for queries and mutations
10+
- Operation name during in queries
11+
12+
### Fixed
13+
14+
- Issue in mutation string generation when no selection set is provided
515

616
## 1.1: 2019-04-26
717

README.md

Lines changed: 84 additions & 9 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.
@@ -170,6 +221,26 @@ $results = $client->runQuery($mutation);
170221
```
171222
Mutations can be run by the client the same way queries are run.
172223

224+
## Mutations With Variables Example
225+
Mutations can utilize the variables in the same way Queries can. Here's an example on how to use the variables to pass
226+
input objects to the GraphQL server dynamically:
227+
```
228+
$mutation = (new Mutation('createCompany'))
229+
->setVariables([new Variable('company', 'CompanyInputObject', true)])
230+
->setArguments(['companyObject' => '$company']);
231+
232+
$variables = ['company' => ['name' => 'Tech Company', 'type' => 'Testing', 'size' => 'Medium']];
233+
$client->runQuery(
234+
$mutation, true, $variables
235+
);
236+
```
237+
These are the resulting mutation and the variables passed with it:
238+
```
239+
mutation($company: CompanyInputObject!) {
240+
createCompany(companyObject: $company)
241+
}
242+
{"company":{"name":"Tech Company","type":"Testing","size":"Medium"}}
243+
```
173244
# Live API Example
174245
GraphQL Pokemon is a very cool public GraphQL API available to retrieve Pokemon data. The API is available publicly on
175246
the web, we'll use it to demo the capabilities of this client.
@@ -178,10 +249,10 @@ Github Repo link: https://github.com/lucasbento/graphql-pokemon
178249

179250
API link: https://graphql-pokemon.now.sh/
180251

181-
This query retrieves Pikachu's evolutions and their attacks:
252+
This query retrieves any pokemon's evolutions and their attacks:
182253
```
183-
{
184-
pokemon(name: "Pikachu") {
254+
query($name: String!) {
255+
pokemon(name: $name) {
185256
id
186257
number
187258
name
@@ -212,7 +283,8 @@ $client = new Client(
212283
'https://graphql-pokemon.now.sh/'
213284
);
214285
$gql = (new Query('pokemon'))
215-
->setArguments(['name' => 'Pikachu'])
286+
->setVariables([new Variable('name', 'String', true)])
287+
->setArguments(['name' => '$name'])
216288
->setSelectionSet(
217289
[
218290
'id',
@@ -242,7 +314,8 @@ $gql = (new Query('pokemon'))
242314
]
243315
);
244316
try {
245-
$results = $client->runQuery($gql, true);
317+
$name = readline('Enter pokemon name: ');
318+
$results = $client->runQuery($gql, true, ['name' => $name]);
246319
}
247320
catch (QueryError $exception) {
248321
print_r($exception->getErrorDetails());
@@ -256,7 +329,8 @@ $client = new Client(
256329
'https://graphql-pokemon.now.sh/'
257330
);
258331
$builder = (new QueryBuilder('pokemon'))
259-
->setArgument('name', 'Pikachu')
332+
->setVariable('name', 'String', true)
333+
->setArgument('name', '$name')
260334
->selectField('id')
261335
->selectField('number')
262336
->selectField('name')
@@ -276,7 +350,8 @@ $builder = (new QueryBuilder('pokemon'))
276350
)
277351
);
278352
try {
279-
$results = $client->runQuery($builder, true);
353+
$name = readline('Enter pokemon name: ');
354+
$results = $client->runQuery($builder, true, ['name' => $name]);
280355
}
281356
catch (QueryError $exception) {
282357
print_r($exception->getErrorDetails());

src/Client.php

Lines changed: 9 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,26 +60,30 @@ 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)) {
7678
$options['headers'] = $this->authorizationHeaders;
7779
}
7880
$options['headers']['Content-Type'] = 'application/json';
7981

82+
// Convert empty variables array to empty json object
83+
if (empty($variables)) $variables = (object) null;
8084
// Set query in the request body
81-
$options['body'] = json_encode(['query' => (string) $queryString]);
85+
$bodyArray = ['query' => (string) $queryString, 'variables' => $variables];
86+
$options['body'] = json_encode($bodyArray);
8287

8388
// Send api request and get response
8489
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/Mutation.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,14 @@ class Mutation extends Query
1515
* @var string
1616
*/
1717
protected const OPERATION_TYPE = 'mutation';
18+
19+
/**
20+
* @return string
21+
*/
22+
protected function constructSelectionSet(): string
23+
{
24+
if (empty($this->selectionSet)) return '';
25+
26+
return parent::constructSelectionSet();
27+
}
1828
}

0 commit comments

Comments
 (0)