Skip to content

Commit 47bb5bc

Browse files
authored
Merge pull request #592 from stevelacey/patch-1
GraphQL Macroable
2 parents 1eaf041 + 5a47b1b commit 47bb5bc

File tree

4 files changed

+71
-26
lines changed

4 files changed

+71
-26
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ CHANGELOG
66

77
### Added
88
- Add support for custom authorization message [\#578 / Sh1d0w](https://github.com/rebing/graphql-laravel/pull/578)
9+
- Add support for macros on the GraphQL service/facade [\#592 / stevelacey](https://github.com/rebing/graphql-laravel/pull/592)
910
### Fixed
1011
- Fix the infinite loop as well as sending the correct matching input data to the rule-callback [\#579 / crissi](https://github.com/rebing/graphql-laravel/pull/579)
1112
### Changed

Readme.md README.md

+61-26
Original file line numberDiff line numberDiff line change
@@ -116,23 +116,24 @@ To work this around:
116116
- [Resolve method](#resolve-method)
117117
- [Authorization](#authorization)
118118
- [Privacy](#privacy)
119-
- [Query Variables](#query-variables)
119+
- [Query variables](#query-variables)
120120
- [Custom field](#custom-field)
121121
- [Even better reusable fields](#even-better-reusable-fields)
122122
- [Eager loading relationships](#eager-loading-relationships)
123123
- [Type relationship query](#type-relationship-query)
124124
- [Pagination](#pagination)
125125
- [Batching](#batching)
126-
- [Scalar Types](#scalar-types)
126+
- [Scalar types](#scalar-types)
127127
- [Enums](#enums)
128128
- [Unions](#unions)
129129
- [Interfaces](#interfaces)
130-
- [Sharing Interface fields](#sharing-interface-fields)
130+
- [Sharing interface fields](#sharing-interface-fields)
131131
- [Input Object](#input-object)
132-
- [Input Alias](#input-alias)
133-
- [JSON Columns](#json-columns)
134-
- [Field deprecation](#field-deprecation)
135-
- [Default Field Resolver](#default-field-resolver)
132+
- [Field and input alias](#field-and-input-alias)
133+
- [JSON columns](#json-columns)
134+
- [Field deprecation](#field-deprecation)
135+
- [Default field resolver](#default-field-resolver)
136+
- [Macros](#macros)
136137
- [Guides](#guides)
137138
- [Upgrading from v1 to v2](#upgrading-from-v1-to-v2)
138139
- [Migrating from Folklore](#migrating-from-folklore)
@@ -186,7 +187,7 @@ use GraphQL\Type\Definition\Type;
186187
use Rebing\GraphQL\Support\Type as GraphQLType;
187188

188189
class UserType extends GraphQLType
189-
{
190+
{
190191
protected $attributes = [
191192
'name' => 'User',
192193
'description' => 'A user',
@@ -209,7 +210,7 @@ class UserType extends GraphQLType
209210
'type' => Type::string(),
210211
'description' => 'The email of user',
211212
'resolve' => function($root, $args) {
212-
// If you want to resolve the field yourself,
213+
// If you want to resolve the field yourself,
213214
// it can be done here
214215
return strtolower($root->email);
215216
}
@@ -228,7 +229,7 @@ class UserType extends GraphQLType
228229
protected function resolveEmailField($root, $args)
229230
{
230231
return strtolower($root->email);
231-
}
232+
}
232233
}
233234
```
234235

@@ -541,7 +542,7 @@ public function validationErrorMessages(array $args = []): array
541542
'name.string' => 'Your name must be a valid string',
542543
'email.required' => 'Please enter your email address',
543544
'email.email' => 'Please enter a valid email address',
544-
'email.exists' => 'Sorry, this email address is already in use',
545+
'email.exists' => 'Sorry, this email address is already in use',
545546
];
546547
}
547548
````
@@ -819,7 +820,7 @@ class UserType extends GraphQLType
819820
}
820821
```
821822

822-
### Query Variables
823+
### Query variables
823824

824825
GraphQL offers you the possibility to use variables in your query so you don't need to "hardcode" value. This is done like that:
825826

@@ -856,7 +857,7 @@ use GraphQL\Type\Definition\Type;
856857
use Rebing\GraphQL\Support\Field;
857858

858859
class PictureField extends Field
859-
{
860+
{
860861
protected $attributes = [
861862
'description' => 'A picture',
862863
];
@@ -943,7 +944,7 @@ use GraphQL\Type\Definition\Type;
943944
use Rebing\GraphQL\Support\Field;
944945

945946
class FormattableDate extends Field
946-
{
947+
{
947948
protected $attributes = [
948949
'description' => 'A field that can output a date in all sorts of ways.',
949950
];
@@ -976,15 +977,15 @@ class FormattableDate extends Field
976977
protected function resolve($root, $args): ?string
977978
{
978979
$date = $root->{$this->getProperty()};
979-
980+
980981
if (!$date instanceof Carbon) {
981982
return null;
982983
}
983984

984985
if ($args['relative']) {
985986
return $date->diffForHumans();
986987
}
987-
988+
988989
return $date->format($args['format']);
989990
}
990991

@@ -1032,7 +1033,7 @@ class UserType extends GraphQLType
10321033

10331034
// Because the constructor of `FormattableDate` accepts our the array of parameters,
10341035
// we can override them very easily.
1035-
// Imagine we want our field to be called `createdAt`, but our database column
1036+
// Imagine we want our field to be called `createdAt`, but our database column
10361037
// is called `created_at`:
10371038
'createdAt' => new FormattableDate([
10381039
'alias' => 'created_at',
@@ -1050,7 +1051,7 @@ Only the required fields will be queried from the database.
10501051

10511052
The class can be instanciated by typehinting `SelectFields $selectField` in your resolve method.
10521053

1053-
You can also construct the class by typehinting a `Closure`.
1054+
You can also construct the class by typehinting a `Closure`.
10541055
The Closure accepts an optional parameter for the depth of the query to analyse.
10551056

10561057
Your Query would look like:
@@ -1332,7 +1333,7 @@ within a certain interval of time.
13321333

13331334
There are tools that help with this and can handle the batching for you, e.g [Apollo](http://www.apollodata.com/)
13341335

1335-
### Scalar Types
1336+
### Scalar types
13361337

13371338
GraphQL comes with built-in scalar types for string, int, boolean, etc. It's possible to create custom scalar types to special purpose fields.
13381339

@@ -1567,7 +1568,7 @@ Based on the previous code example, the method would look like:
15671568
}
15681569
```
15691570

1570-
#### Sharing Interface fields
1571+
#### Sharing interface fields
15711572

15721573
Since you often have to repeat many of the field definitons of the Interface in the concrete types, it makes sense to share the definitions of the Interface.
15731574
You can access and reuse specific interface fields with the method `getField(string fieldName): FieldDefinition`. To get all fields as an array use `getFields(): array`
@@ -1675,7 +1676,7 @@ class TestMutation extends GraphQLType {
16751676
}
16761677
```
16771678

1678-
### Input Alias
1679+
### Field and input alias
16791680

16801681
It is possible to alias query and mutation arguments as well as input object fields.
16811682

@@ -1768,7 +1769,7 @@ class UpdateUserMutation extends Mutation
17681769
```
17691770

17701771

1771-
### JSON Columns
1772+
### JSON columns
17721773

17731774
When using JSON columns in your database, the field won't be defined as a "relationship",
17741775
but rather a simple column with nested data. To get a nested object that's not a database relationship,
@@ -1801,7 +1802,7 @@ class UserType extends GraphQLType
18011802
}
18021803
```
18031804

1804-
#### Field deprecation
1805+
### Field deprecation
18051806

18061807
Sometimes you would want to deprecate a field but still have to maintain backward compatibility
18071808
until clients completely stop using that field. You can deprecate a field using
@@ -1820,7 +1821,7 @@ use GraphQL\Type\Definition\Type;
18201821
use Rebing\GraphQL\Support\Type as GraphQLType;
18211822

18221823
class UserType extends GraphQLType
1823-
{
1824+
{
18241825
protected $attributes = [
18251826
'name' => 'User',
18261827
'description' => 'A user',
@@ -1856,7 +1857,7 @@ class UserType extends GraphQLType
18561857
}
18571858
```
18581859

1859-
#### Default Field Resolver
1860+
### Default field resolver
18601861

18611862
It's possible to override the default field resolver provided by the underlying
18621863
webonyx/graphql-php library using the config option `defaultFieldResolver`.
@@ -1869,6 +1870,40 @@ You can define any valid callable (static class method, closure, etc.) for it:
18691870

18701871
The parameters received are your regular "resolve" function signature.
18711872

1873+
### Macros
1874+
1875+
If you would like to define some helpers that you can re-use in a variety of your
1876+
queries, mutations and types, you may use the macro method on the `GraphQL` facade.
1877+
1878+
For example, from a service provider's boot method:
1879+
1880+
```php
1881+
<?php
1882+
1883+
namespace App\Providers;
1884+
1885+
use GraphQL\Type\Definition\Type;
1886+
use Illuminate\Support\ServiceProvider;
1887+
use Rebing\GraphQL\Support\Facades\GraphQL;
1888+
1889+
class AppServiceProvider extends ServiceProvider
1890+
{
1891+
/**
1892+
* Bootstrap any application services.
1893+
*
1894+
* @return void
1895+
*/
1896+
public function boot()
1897+
{
1898+
GraphQL::macro('listOf', function (string $name) : Type {
1899+
return Type::listOf(GraphQL::type($name));
1900+
});
1901+
}
1902+
}
1903+
```
1904+
1905+
The `macro` function accepts a name as its first argument, and a `Closure` as its second.
1906+
18721907
## Guides
18731908

18741909
### Upgrading from v1 to v2
@@ -1940,7 +1975,7 @@ The following is not a bullet-proof list but should serve as a guide. It's not a
19401975
- Change namespace references:
19411976
- from `Folklore\`
19421977
- to `Rebing\`
1943-
- See [Upgrade guide from v1 to v2 for all the function signature changes](#upgrading-from-v1-to-v2)
1978+
- See [Upgrade guide from v1 to v2 for all the function signature changes](#upgrading-from-v1-to-v2)
19441979
- The trait `ShouldValidate` does not exist anymore; the provided features are baked into `Field`
19451980
- The first argument to the resolve method for queries/mutations is now `null` (previously its default was an empty array)
19461981

src/GraphQL.php

+3
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
use GraphQL\Type\Schema;
1717
use Illuminate\Contracts\Container\Container;
1818
use Illuminate\Contracts\Debug\ExceptionHandler;
19+
use Illuminate\Support\Traits\Macroable;
1920
use Rebing\GraphQL\Error\AuthorizationError;
2021
use Rebing\GraphQL\Error\ValidationError;
2122
use Rebing\GraphQL\Exception\SchemaNotFound;
@@ -26,6 +27,8 @@
2627

2728
class GraphQL
2829
{
30+
use Macroable;
31+
2932
/** @var Container */
3033
protected $app;
3134

tests/Unit/GraphQLTest.php

+6
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
use GraphQL\Type\Definition\ObjectType;
99
use GraphQL\Type\Definition\Type;
1010
use GraphQL\Type\Schema;
11+
use Illuminate\Support\Traits\Macroable;
1112
use Rebing\GraphQL\Error\ValidationError;
1213
use Rebing\GraphQL\Exception\SchemaNotFound;
1314
use Rebing\GraphQL\Exception\TypeNotFound;
@@ -395,4 +396,9 @@ public function testAddSchemaObjectAndExecuteQueryWithRootValue(): void
395396

396397
$this->assertSame($expectedResult, $result);
397398
}
399+
400+
public function testIsMacroable(): void
401+
{
402+
$this->assertContains(Macroable::class, class_uses_recursive(GraphQL::getFacadeRoot()));
403+
}
398404
}

0 commit comments

Comments
 (0)