Skip to content

Commit a9099d6

Browse files
committed
GQL-48: Added support for inline fragments
- Added new InlineFragment class to support embedding them in queries - Moved common logic between inline fragments and queries to common FieldTrait - Created InlineFragmentTest to test creation and string conversion - Added test for embedding inline fragments in queries in QueryTest
1 parent e47e6e5 commit a9099d6

File tree

5 files changed

+239
-62
lines changed

5 files changed

+239
-62
lines changed

src/FieldTrait.php

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
<?php
2+
3+
namespace GraphQL;
4+
5+
use GraphQL\Exception\InvalidSelectionException;
6+
7+
trait FieldTrait
8+
{
9+
/**
10+
* Stores the selection set desired to get from the query, can include nested queries
11+
*
12+
* @var array
13+
*/
14+
protected $selectionSet;
15+
16+
/**
17+
* @param array $selectionSet
18+
*
19+
* @return $this
20+
* @throws InvalidSelectionException
21+
*/
22+
public function setSelectionSet(array $selectionSet)
23+
{
24+
$nonStringsFields = array_filter($selectionSet, function($element) {
25+
return !is_string($element) && !$element instanceof Query && !$element instanceof InlineFragment;
26+
});
27+
if (!empty($nonStringsFields)) {
28+
throw new InvalidSelectionException(
29+
'One or more of the selection fields provided is not of type string or Query'
30+
);
31+
}
32+
33+
$this->selectionSet = $selectionSet;
34+
35+
return $this;
36+
}
37+
38+
/**
39+
* @return string
40+
*/
41+
protected function constructSelectionSet(): string
42+
{
43+
$attributesString = " {\n";
44+
$first = true;
45+
foreach ($this->selectionSet as $attribute) {
46+
47+
// Append empty line at the beginning if it's not the first item on the list
48+
if ($first) {
49+
$first = false;
50+
} else {
51+
$attributesString .= "\n";
52+
}
53+
54+
// If query is included in attributes set as a nested query
55+
if ($attribute instanceof Query) {
56+
$attribute->setAsNested();
57+
}
58+
59+
// Append attribute to returned attributes list
60+
$attributesString .= $attribute;
61+
}
62+
$attributesString .= "\n}";
63+
64+
return $attributesString;
65+
}
66+
}

src/InlineFragment.php

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
<?php
2+
3+
namespace GraphQL;
4+
5+
/**
6+
* Class InlineFragment
7+
*
8+
* @package GraphQL
9+
*/
10+
class InlineFragment
11+
{
12+
use FieldTrait;
13+
14+
/**
15+
* Stores the format for the inline fragment format
16+
*
17+
* @var string
18+
*/
19+
protected const FORMAT = '... on %s%s';
20+
21+
/**
22+
* @var string
23+
*/
24+
protected $typeName;
25+
26+
/**
27+
* InlineFragment constructor.
28+
*
29+
* @param string $typeName
30+
*/
31+
public function __construct(string $typeName)
32+
{
33+
$this->typeName = $typeName;
34+
}
35+
36+
/**
37+
*
38+
*/
39+
public function __toString()
40+
{
41+
return sprintf(static::FORMAT, $this->typeName, $this->constructSelectionSet());
42+
}
43+
}

src/Query.php

Lines changed: 2 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
namespace GraphQL;
44

55
use GraphQL\Exception\ArgumentException;
6-
use GraphQL\Exception\InvalidSelectionException;
76
use GraphQL\Exception\InvalidVariableException;
87
use GraphQL\Util\StringLiteralFormatter;
98

@@ -14,6 +13,8 @@
1413
*/
1514
class Query
1615
{
16+
use FieldTrait;
17+
1718
/**
1819
* Stores the GraphQL query format
1920
*
@@ -56,13 +57,6 @@ class Query
5657
*/
5758
protected $arguments;
5859

59-
/**
60-
* Stores the selection set desired to get from the query, can include nested queries
61-
*
62-
* @var array
63-
*/
64-
protected $selectionSet;
65-
6660
/**
6761
* Private member that's not accessible from outside the class, used internally to deduce if query is nested or not
6862
*
@@ -144,28 +138,6 @@ public function setArguments(array $arguments): Query
144138
return $this;
145139
}
146140

147-
/**
148-
* @param array $selectionSet
149-
*
150-
* @return Query
151-
* @throws InvalidSelectionException
152-
*/
153-
public function setSelectionSet(array $selectionSet): Query
154-
{
155-
$nonStringsFields = array_filter($selectionSet, function($element) {
156-
return !is_string($element) && !$element instanceof Query;
157-
});
158-
if (!empty($nonStringsFields)) {
159-
throw new InvalidSelectionException(
160-
'One or more of the selection fields provided is not of type string or Query'
161-
);
162-
}
163-
164-
$this->selectionSet = $selectionSet;
165-
166-
return $this;
167-
}
168-
169141
/**
170142
* @return string
171143
*/
@@ -232,35 +204,6 @@ protected function constructArguments(): string
232204
return $constraintsString;
233205
}
234206

235-
/**
236-
* @return string
237-
*/
238-
protected function constructSelectionSet(): string
239-
{
240-
$attributesString = " {\n";
241-
$first = true;
242-
foreach ($this->selectionSet as $attribute) {
243-
244-
// Append empty line at the beginning if it's not the first item on the list
245-
if ($first) {
246-
$first = false;
247-
} else {
248-
$attributesString .= "\n";
249-
}
250-
251-
// If query is included in attributes set as a nested query
252-
if ($attribute instanceof Query) {
253-
$attribute->setAsNested();
254-
}
255-
256-
// Append attribute to returned attributes list
257-
$attributesString .= $attribute;
258-
}
259-
$attributesString .= "\n}";
260-
261-
return $attributesString;
262-
}
263-
264207
/**
265208
* @return string
266209
*/

tests/InlineFragmentTest.php

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
<?php
2+
3+
namespace GraphQL\Tests;
4+
5+
use GraphQL\InlineFragment;
6+
use GraphQL\Query;
7+
use PHPUnit\Framework\TestCase;
8+
9+
/**
10+
* Class InlineFragmentTest
11+
*
12+
* @package GraphQL\Tests
13+
*/
14+
class InlineFragmentTest extends TestCase
15+
{
16+
/**
17+
* @covers \GraphQL\InlineFragment::__construct
18+
* @covers \GraphQL\InlineFragment::setSelectionSet
19+
* @covers \GraphQL\InlineFragment::constructSelectionSet
20+
* @covers \GraphQL\InlineFragment::__toString
21+
*/
22+
public function testConvertToString()
23+
{
24+
$fragment = new InlineFragment('Test');
25+
$fragment->setSelectionSet(
26+
[
27+
'field1',
28+
'field2',
29+
]
30+
);
31+
32+
$this->assertEquals(
33+
'... on Test {
34+
field1
35+
field2
36+
}',
37+
(string) $fragment
38+
);
39+
}
40+
/**
41+
* @covers \GraphQL\InlineFragment::__construct
42+
* @covers \GraphQL\InlineFragment::setSelectionSet
43+
* @covers \GraphQL\InlineFragment::constructSelectionSet
44+
* @covers \GraphQL\InlineFragment::__toString
45+
*/
46+
public function testConvertNestedFragmentToString()
47+
{
48+
$fragment = new InlineFragment('Test');
49+
$fragment->setSelectionSet(
50+
[
51+
'field1',
52+
'field2',
53+
(new Query('sub_field'))
54+
->setArguments(
55+
[
56+
'first' => 5
57+
]
58+
)
59+
->setSelectionSet(
60+
[
61+
'sub_field3',
62+
(new InlineFragment('Nested'))
63+
->setSelectionSet(
64+
[
65+
'another_field'
66+
]
67+
),
68+
]
69+
)
70+
]
71+
);
72+
73+
$this->assertEquals(
74+
'... on Test {
75+
field1
76+
field2
77+
sub_field(first: 5) {
78+
sub_field3
79+
... on Nested {
80+
another_field
81+
}
82+
}
83+
}',
84+
(string) $fragment
85+
);
86+
}
87+
}

tests/QueryTest.php

Lines changed: 41 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
use GraphQL\Exception\ArgumentException;
66
use GraphQL\Exception\InvalidSelectionException;
77
use GraphQL\Exception\InvalidVariableException;
8+
use GraphQL\InlineFragment;
89
use GraphQL\Query;
910
use GraphQL\RawObject;
1011
use GraphQL\Variable;
@@ -480,7 +481,7 @@ public function testStringWrappingWorks()
480481
* @depends clone testEmptyQuery
481482
*
482483
* @covers \GraphQL\Query::setSelectionSet
483-
* @covers \GraphQL\Query::constructSelectionSet
484+
* @covers \GraphQL\FieldTrait::constructSelectionSet
484485
*
485486
* @param Query $query
486487
*
@@ -506,7 +507,7 @@ public function testSingleSelectionField(Query $query)
506507
* @depends clone testEmptyQuery
507508
*
508509
* @covers \GraphQL\Query::setSelectionSet
509-
* @covers \GraphQL\Query::constructSelectionSet
510+
* @covers \GraphQL\FieldTrait::constructSelectionSet
510511
*
511512
* @param Query $query
512513
*
@@ -577,7 +578,7 @@ public function testOneLevelQuery(Query $query)
577578
/**
578579
* @depends clone testOneLevelQuery
579580
*
580-
* @covers \GraphQL\Query::constructSelectionSet
581+
* @covers \GraphQL\FieldTrait::constructSelectionSet
581582
* @covers \GraphQL\Query::setAsNested
582583
*
583584
* @param Query $query
@@ -638,4 +639,41 @@ public function testTwoLevelQuery(Query $query)
638639

639640
return $query;
640641
}
642+
643+
/**
644+
* @depends clone testTwoLevelQueryDoesNotContainWordQuery
645+
*
646+
* @param Query $query
647+
*
648+
* @return Query
649+
*/
650+
public function testTwoLevelQueryWithInlineFragment(Query $query)
651+
{
652+
$query->setSelectionSet(
653+
[
654+
'field1',
655+
(new InlineFragment('Object'))
656+
->setSelectionSet(
657+
[
658+
'fragment_field1',
659+
'fragment_field2',
660+
]
661+
),
662+
]
663+
);
664+
$this->assertEquals(
665+
'query {
666+
Object(arg1: "val1" arg2: "val2") {
667+
field1
668+
... on Object {
669+
fragment_field1
670+
fragment_field2
671+
}
672+
}
673+
}',
674+
(string) $query
675+
);
676+
677+
return $query;
678+
}
641679
}

0 commit comments

Comments
 (0)