Skip to content

Commit 21bc850

Browse files
michael-vostrikovMichael Vostrikov
and
Michael Vostrikov
authored
Add description and source field name properties to MagicField and SourceField annotations (#402)
* Add description property to MagicField and SourceField annotations * Add source field name property for MagicField and SourceField annotations * Update docs for MagicField and SourceField Co-authored-by: Michael Vostrikov <mikhail.vostrikov@vseinstrumenti.ru>
1 parent ba3637c commit 21bc850

13 files changed

+159
-7
lines changed

src/Annotations/MagicField.php

+26-1
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,12 @@ class MagicField implements SourceFieldInterface
3434
/** @var string|null */
3535
private $phpType;
3636

37+
/** @var string|null */
38+
private $description;
39+
40+
/** @var string|null */
41+
private $sourceName;
42+
3743
/** @var MiddlewareAnnotations */
3844
private $middlewareAnnotations;
3945

@@ -43,11 +49,14 @@ class MagicField implements SourceFieldInterface
4349
/**
4450
* @param mixed[] $attributes
4551
*/
46-
public function __construct(array $attributes = [], ?string $name = null, ?string $outputType = null, ?string $phpType = null)
52+
public function __construct(array $attributes = [], ?string $name = null, ?string $outputType = null, ?string $phpType = null, ?string $description = null, ?string $sourceName = null)
4753
{
4854
$this->name = $attributes['name'] ?? $name;
4955
$this->outputType = $attributes['outputType'] ?? $outputType ?? null;
5056
$this->phpType = $attributes['phpType'] ?? $phpType ?? null;
57+
$this->description = $attributes['description'] ?? $description ?? null;
58+
$this->sourceName = $attributes['sourceName'] ?? $sourceName ?? null;
59+
5160
if (! $this->name || (! $this->outputType && ! $this->phpType)) {
5261
throw new BadMethodCallException('The @MagicField annotation must be passed a name and an output type or a php type. For instance: "@MagicField(name=\'phone\', outputType=\'String!\')" or "@MagicField(name=\'phone\', phpType=\'string\')"');
5362
}
@@ -101,6 +110,22 @@ public function getPhpType(): ?string
101110
return $this->phpType;
102111
}
103112

113+
/**
114+
* Returns the description of the GraphQL query/mutation/field.
115+
*/
116+
public function getDescription(): ?string
117+
{
118+
return $this->description;
119+
}
120+
121+
/**
122+
* Returns the property name in the source class
123+
*/
124+
public function getSourceName(): ?string
125+
{
126+
return $this->sourceName;
127+
}
128+
104129
public function getMiddlewareAnnotations(): MiddlewareAnnotations
105130
{
106131
return $this->middlewareAnnotations;

src/Annotations/SourceField.php

+26-1
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,12 @@ class SourceField implements SourceFieldInterface
3434
/** @var string|null */
3535
private $phpType;
3636

37+
/** @var string|null */
38+
private $description;
39+
40+
/** @var string */
41+
private $sourceName;
42+
3743
/** @var MiddlewareAnnotations */
3844
private $middlewareAnnotations;
3945

@@ -43,7 +49,7 @@ class SourceField implements SourceFieldInterface
4349
/**
4450
* @param mixed[] $attributes
4551
*/
46-
public function __construct(array $attributes = [], ?string $name = null, ?string $outputType = null, ?string $phpType = null)
52+
public function __construct(array $attributes = [], ?string $name = null, ?string $outputType = null, ?string $phpType = null, ?string $description = null, ?string $sourceName = null)
4753
{
4854
$name = $name ?? $attributes['name'] ?? null;
4955
if ($name === null) {
@@ -53,6 +59,9 @@ public function __construct(array $attributes = [], ?string $name = null, ?strin
5359

5460
$this->outputType = $outputType ?? $attributes['outputType'] ?? null;
5561
$this->phpType = $phpType ?? $attributes['phpType'] ?? null;
62+
$this->description = $description ?? $attributes['description'] ?? null;
63+
$this->sourceName = $sourceName ?? $attributes['sourceName'] ?? null;
64+
5665
if ($this->outputType && $this->phpType) {
5766
throw new BadMethodCallException('In a @SourceField annotation, you cannot use the outputType and the phpType at the same time. For instance: "@SourceField(name=\'phone\', outputType=\'String!\')" or "@SourceField(name=\'phone\', phpType=\'string\')"');
5867
}
@@ -99,6 +108,22 @@ public function getPhpType(): ?string
99108
return $this->phpType;
100109
}
101110

111+
/**
112+
* Returns the description of the GraphQL query/mutation/field.
113+
*/
114+
public function getDescription(): ?string
115+
{
116+
return $this->description;
117+
}
118+
119+
/**
120+
* Returns the property name in the source class
121+
*/
122+
public function getSourceName(): ?string
123+
{
124+
return $this->sourceName;
125+
}
126+
102127
public function getMiddlewareAnnotations(): MiddlewareAnnotations
103128
{
104129
return $this->middlewareAnnotations;

src/Annotations/SourceFieldInterface.php

+10
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,16 @@ public function getOutputType(): ?string;
2626
*/
2727
public function getPhpType(): ?string;
2828

29+
/**
30+
* Returns the description of the GraphQL query/mutation/field.
31+
*/
32+
public function getDescription(): ?string;
33+
34+
/**
35+
* Returns the property name in the source class
36+
*/
37+
public function getSourceName(): ?string;
38+
2939
public function getMiddlewareAnnotations(): MiddlewareAnnotations;
3040

3141
/**

src/FieldsBuilder.php

+6-3
Original file line numberDiff line numberDiff line change
@@ -581,7 +581,7 @@ private function getQueryFieldsFromSourceFields(array $sourceFields, ReflectionC
581581

582582
if (! $sourceField->shouldFetchFromMagicProperty()) {
583583
try {
584-
$refMethod = $this->getMethodFromPropertyName($objectRefClass, $sourceField->getName());
584+
$refMethod = $this->getMethodFromPropertyName($objectRefClass, $sourceField->getSourceName() ?? $sourceField->getName());
585585
} catch (FieldNotFoundException $e) {
586586
throw FieldNotFoundException::wrapWithCallerInfo($e, $refClass->getName());
587587
}
@@ -597,7 +597,8 @@ private function getQueryFieldsFromSourceFields(array $sourceFields, ReflectionC
597597
$fieldDescriptor->setDeprecationReason(trim((string) $deprecated[0]));
598598
}
599599

600-
$fieldDescriptor->setComment($docBlockComment);
600+
$description = $sourceField->getDescription() ?? $docBlockComment;
601+
$fieldDescriptor->setComment($description);
601602
$args = $this->mapParameters($refMethod->getParameters(), $docBlockObj, $sourceField);
602603

603604
$fieldDescriptor->setParameters($args);
@@ -612,7 +613,9 @@ private function getQueryFieldsFromSourceFields(array $sourceFields, ReflectionC
612613
$type = $this->typeMapper->mapReturnType($refMethod, $docBlockObj);
613614
}
614615
} else {
615-
$fieldDescriptor->setMagicProperty($sourceField->getName());
616+
$fieldDescriptor->setMagicProperty($sourceField->getSourceName() ?? $sourceField->getName());
617+
$fieldDescriptor->setComment($sourceField->getDescription());
618+
616619
$outputType = $sourceField->getOutputType();
617620
if ($outputType !== null) {
618621
$type = $this->resolveOutputType($outputType, $refClass, $sourceField);

tests/FieldsBuilderTest.php

+26
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,8 @@
5454
use TheCodingMachine\GraphQLite\Fixtures\TestTypeWithPrefetchMethod;
5555
use TheCodingMachine\GraphQLite\Fixtures\TestTypeWithSourceFieldInterface;
5656
use TheCodingMachine\GraphQLite\Fixtures\TestTypeWithSourceFieldInvalidParameterAnnotation;
57+
use TheCodingMachine\GraphQLite\Fixtures\TestSourceName;
58+
use TheCodingMachine\GraphQLite\Fixtures\TestSourceNameType;
5759
use TheCodingMachine\GraphQLite\Mappers\CannotMapTypeException;
5860
use TheCodingMachine\GraphQLite\Mappers\CannotMapTypeExceptionInterface;
5961
use TheCodingMachine\GraphQLite\Middlewares\AuthorizationFieldMiddleware;
@@ -253,6 +255,7 @@ public function testSourceField(): void
253255
$this->assertInstanceOf(ObjectType::class, $fields['sibling']->getType()->getWrappedType());
254256
$this->assertSame('TestObject', $fields['sibling']->getType()->getWrappedType()->name);
255257
$this->assertSame('This is a test summary', $fields['test']->description);
258+
$this->assertSame('Test SourceField description', $fields['sibling']->description);
256259
}
257260

258261
public function testSourceFieldOnSelfType(): void
@@ -777,6 +780,7 @@ public function testMagicField(): void
777780
$this->assertCount(1, $fields);
778781
$query = $fields['foo'];
779782
$this->assertSame('foo', $query->name);
783+
$this->assertSame('Test MagicField description', $query->description);
780784

781785
$resolve = $query->resolveFn;
782786
$result = $resolve(new TestTypeWithMagicProperty(), [], null, $this->createMock(ResolveInfo::class));
@@ -804,4 +808,26 @@ public function testProxyClassWithMagicPropertyOfPhpType(): void
804808

805809
$this->assertSame('foo', $result);
806810
}
811+
812+
public function testSourceNameInSourceAndMagicFields(): void
813+
{
814+
$controller = new TestSourceNameType();
815+
$queryProvider = $this->buildFieldsBuilder();
816+
$fields = $queryProvider->getFields($controller);
817+
$source = new TestSourceName('foo value', 'bar value');
818+
819+
$this->assertCount(2, $fields);
820+
821+
$query = $fields['foo2'];
822+
$this->assertSame('foo2', $query->name);
823+
$resolve = $query->resolveFn;
824+
$result = $resolve($source, [], null, $this->createMock(ResolveInfo::class));
825+
$this->assertSame('foo value', $result);
826+
827+
$query = $fields['bar2'];
828+
$this->assertSame('bar2', $query->name);
829+
$resolve = $query->resolveFn;
830+
$result = $resolve($source, [], null, $this->createMock(ResolveInfo::class));
831+
$this->assertSame('bar value', $result);
832+
}
807833
}

tests/Fixtures/TestSourceName.php

+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
<?php
2+
3+
namespace TheCodingMachine\GraphQLite\Fixtures;
4+
5+
use Exception;
6+
7+
class TestSourceName
8+
{
9+
/** @var string */
10+
private $foo;
11+
12+
/** @var string */
13+
private $bar;
14+
15+
public function __construct(string $foo, string $bar)
16+
{
17+
$this->foo = $foo;
18+
$this->bar = $bar;
19+
}
20+
21+
public function __get($name)
22+
{
23+
if ($name !== 'foo') {
24+
throw new Exception('Unknown property: ' . $name);
25+
}
26+
27+
return $this->$name;
28+
}
29+
30+
public function getBar(): string
31+
{
32+
return $this->bar;
33+
}
34+
}

tests/Fixtures/TestSourceNameType.php

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<?php
2+
3+
namespace TheCodingMachine\GraphQLite\Fixtures;
4+
5+
use TheCodingMachine\GraphQLite\Fixtures\TestSourceName;
6+
use TheCodingMachine\GraphQLite\Annotations\MagicField;
7+
use TheCodingMachine\GraphQLite\Annotations\SourceField;
8+
use TheCodingMachine\GraphQLite\Annotations\Type;
9+
10+
/**
11+
* @Type(class=TestSourceName::class)
12+
* @MagicField(name="foo2", outputType="String!", sourceName="foo")
13+
* @SourceField(name="bar2", sourceName="bar")
14+
*/
15+
class TestSourceNameType
16+
{
17+
}

tests/Fixtures/TestType.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
* @SourceField(name="test")
1616
* @SourceField(name="testBool", annotations={@Logged, @HideIfUnauthorized})
1717
* @SourceField(name="testRight", annotations={@Right(name="FOOBAR"), @HideIfUnauthorized})
18-
* @SourceField(name="sibling")
18+
* @SourceField(name="sibling", description="Test SourceField description")
1919
*/
2020
class TestType
2121
{

tests/Fixtures/TestTypeWithMagicProperty.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111

1212
/**
1313
* @Type()
14-
* @MagicField(name="foo", outputType="String!")
14+
* @MagicField(name="foo", outputType="String!", description="Test MagicField description")
1515
*/
1616
class TestTypeWithMagicProperty
1717
{

website/docs/annotations-reference.md

+4
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,8 @@ Attribute | Compulsory | Type | Definition
9696
name | *yes* | string | The name of the field.
9797
[outputType](custom-types.mdx) | *no* | string | Forces the GraphQL output type of the field. Otherwise, return type is used.
9898
phpType | *no* | string | The PHP type of the field (as you would write it in a Docblock)
99+
description | *no* | string | Field description displayed in the GraphQL docs. If it's empty PHP doc comment of the method in the source class is used instead.
100+
sourceName | *no* | string | The name of the property in the source class. If not set, the `name` will be used to get property value.
99101
annotations | *no* | array\<Annotations\> | A set of annotations that apply to this field. You would typically used a "@Logged" or "@Right" annotation here. Available in Doctrine annotations only (not available in the #SourceField PHP 8 attribute)
100102

101103
**Note**: `outputType` and `phpType` are mutually exclusive.
@@ -111,6 +113,8 @@ Attribute | Compulsory | Type | Definition
111113
name | *yes* | string | The name of the field.
112114
[outputType](custom-types.mdx) | *no*(*) | string | The GraphQL output type of the field.
113115
phpType | *no*(*) | string | The PHP type of the field (as you would write it in a Docblock)
116+
description | *no* | string | Field description displayed in the GraphQL docs. If not set, no description will be shown.
117+
sourceName | *no* | string | The name of the property in the source class. If not set, the `name` will be used to get property value.
114118
annotations | *no* | array\<Annotations\> | A set of annotations that apply to this field. You would typically used a "@Logged" or "@Right" annotation here. Available in Doctrine annotations only (not available in the #MagicField PHP 8 attribute)
115119

116120
(*) **Note**: `outputType` and `phpType` are mutually exclusive. You MUST provide one of them.

website/docs/external-type-declaration.mdx

+2
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,7 @@ class ProductType
133133
By doing so, you let GraphQLite know that the type exposes the `getName` method of the underlying `Product` object.
134134

135135
Internally, GraphQLite will look for methods named `name()`, `getName()` and `isName()`).
136+
You can set different name to look for with `sourceName` attribute.
136137

137138
## `@MagicField` annotation
138139

@@ -187,6 +188,7 @@ class ProductType
187188
</Tabs>
188189

189190
By doing so, you let GraphQLite know that the type exposes "name" and the "price" magic properties of the underlying `Product` object.
191+
You can set different name to look for with `sourceName` attribute.
190192

191193
This is particularly useful in frameworks like Laravel, where Eloquent is making a very wide use of such properties.
192194

website/versioned_docs/version-5.0/annotations-reference.md

+4
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,8 @@ Attribute | Compulsory | Type | Definition
9696
name | *yes* | string | The name of the field.
9797
[outputType](custom-types.mdx) | *no* | string | Forces the GraphQL output type of the field. Otherwise, return type is used.
9898
phpType | *no* | string | The PHP type of the field (as you would write it in a Docblock)
99+
description | *no* | string | Field description displayed in the GraphQL docs. If it's empty PHP doc comment of the method in the source class is used instead.
100+
sourceName | *no* | string | The name of the property in the source class. If not set, the `name` will be used to get property value.
99101
annotations | *no* | array\<Annotations\> | A set of annotations that apply to this field. You would typically used a "@Logged" or "@Right" annotation here. Available in Doctrine annotations only (not available in the #SourceField PHP 8 attribute)
100102

101103
**Note**: `outputType` and `phpType` are mutually exclusive.
@@ -111,6 +113,8 @@ Attribute | Compulsory | Type | Definition
111113
name | *yes* | string | The name of the field.
112114
[outputType](custom-types.mdx) | *no*(*) | string | The GraphQL output type of the field.
113115
phpType | *no*(*) | string | The PHP type of the field (as you would write it in a Docblock)
116+
description | *no* | string | Field description displayed in the GraphQL docs. If not set, no description will be shown.
117+
sourceName | *no* | string | The name of the property in the source class. If not set, the `name` will be used to get property value.
114118
annotations | *no* | array\<Annotations\> | A set of annotations that apply to this field. You would typically used a "@Logged" or "@Right" annotation here. Available in Doctrine annotations only (not available in the #MagicField PHP 8 attribute)
115119

116120
(*) **Note**: `outputType` and `phpType` are mutually exclusive. You MUST provide one of them.

website/versioned_docs/version-5.0/external-type-declaration.mdx

+2
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,7 @@ class ProductType
133133
By doing so, you let GraphQLite know that the type exposes the `getName` method of the underlying `Product` object.
134134

135135
Internally, GraphQLite will look for methods named `name()`, `getName()` and `isName()`).
136+
You can set different name to look for with `sourceName` attribute.
136137

137138
## `@MagicField` annotation
138139

@@ -187,6 +188,7 @@ class ProductType
187188
</Tabs>
188189

189190
By doing so, you let GraphQLite know that the type exposes "name" and the "price" magic properties of the underlying `Product` object.
191+
You can set different name to look for with `sourceName` attribute.
190192

191193
This is particularly useful in frameworks like Laravel, where Eloquent is making a very wide use of such properties.
192194

0 commit comments

Comments
 (0)