Skip to content
Draft
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ You can find and compare releases at the [GitHub release page](https://github.co

## Unreleased

### Changed

- Override the default field resolver `GraphQL\Executor\Executor::defaultFieldResolver()` with `Nuwave\Lighthouse\LighthouseServiceProvider::defaultFieldResolver()` https://github.com/nuwave/lighthouse/pull/2693

## v6.58.0

### Changed
Expand Down
10 changes: 10 additions & 0 deletions UPGRADE.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,16 @@ Compare your `lighthouse.php` against the latest [default configuration](src/lig

## v6 to v7

### Default field resolver changed

The default field resolver was changed from `GraphQL\Executor\Executor::defaultFieldResolver()` to `Nuwave\Lighthouse\LighthouseServiceProvider::defaultFieldResolver()`.
The new default fields resolver is expected to be mostly compatible with the previous one, but introduces some behavior changes when resolving models:

| Scenario | Previous behavior | New behavior |
|-----------------------------|---------------------------------------------------------------------------|-----------------------------------------------------------------|
| Accessing a magic attribute | Call `isset()` first, then get the value, resulting in duplicate accesses | Attempt to get the value directly, resulting in a single access |
| Accessing a PHP property | Call `isset()` first which goes into `__isset` and returns `null` | Attempt to get the value directly, returning the property value |

### Leverage automatic test trait setup

Methods you need to explicitly call to set up test traits were removed in favor of automatically set up test traits.
Expand Down
3 changes: 2 additions & 1 deletion docs/master/digging-deeper/extending-lighthouse.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,9 @@ final class SomePackageServiceProvider extends ServiceProvider

## Changing the default resolver

Lighthouse will fall back to using [webonyx's default resolver](https://webonyx.github.io/graphql-php/data-fetching/#default-field-resolver)
Lighthouse overrides [webonyx's default resolver](https://webonyx.github.io/graphql-php/data-fetching#default-field-resolver)
for non-root fields, [see resolver precedence](../the-basics/fields.md#resolver-precedence).
See `Nuwave\Lighthouse\LighthouseServiceProvider::defaultFieldResolver()` for the implementation.

You may override this by calling `GraphQL\Executor\Executor::setDefaultFieldResolver()` in your service provider's `boot()` method.

Expand Down
36 changes: 36 additions & 0 deletions src/LighthouseServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,12 @@
use GraphQL\Error\Error;
use GraphQL\Error\ProvidesExtensions;
use GraphQL\Executor\ExecutionResult;
use GraphQL\Executor\Executor;
use GraphQL\Utils\Utils;
use Illuminate\Contracts\Config\Repository as ConfigRepository;
use Illuminate\Contracts\Debug\ExceptionHandler as ExceptionHandlerContract;
use Illuminate\Contracts\Events\Dispatcher as EventsDispatcher;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
use Illuminate\Http\JsonResponse;
use Illuminate\Support\ServiceProvider;
Expand All @@ -31,6 +34,7 @@
use Nuwave\Lighthouse\Execution\ContextFactory;
use Nuwave\Lighthouse\Execution\ContextSerializer;
use Nuwave\Lighthouse\Execution\ErrorPool;
use Nuwave\Lighthouse\Execution\ResolveInfo;
use Nuwave\Lighthouse\Execution\SingleResponse;
use Nuwave\Lighthouse\Http\Responses\ResponseStream;
use Nuwave\Lighthouse\Schema\AST\ASTBuilder;
Expand All @@ -45,6 +49,7 @@
use Nuwave\Lighthouse\Support\Contracts\CanStreamResponse;
use Nuwave\Lighthouse\Support\Contracts\CreatesContext;
use Nuwave\Lighthouse\Support\Contracts\CreatesResponse;
use Nuwave\Lighthouse\Support\Contracts\GraphQLContext;
use Nuwave\Lighthouse\Support\Contracts\ProvidesResolver;
use Nuwave\Lighthouse\Support\Contracts\ProvidesSubscriptionResolver;
use Nuwave\Lighthouse\Support\Contracts\ProvidesValidationRules;
Expand Down Expand Up @@ -142,6 +147,37 @@ public function boot(ConfigRepository $configRepository, EventsDispatcher $dispa
return new JsonResponse($serializableResult);
});
}

Executor::setDefaultFieldResolver([static::class, 'defaultFieldResolver']); // @phpstan-ignore argument.type (callable not recognized)
}

/**
* The default field resolver for GraphQL queries.
*
* This method is used to resolve fields on the object-like value returned by a resolver.
* It checks if the value is an Eloquent model and retrieves the attribute or property accordingly.
* Otherwise, it falls back to the default behavior from webonyx/graphql-php's default field resolver.
*
* @see \GraphQL\Executor\Executor::defaultFieldResolver()
*
* @param array<string, mixed> $args
*/
public static function defaultFieldResolver(mixed $root, array $args, GraphQLContext $context, ResolveInfo $resolveInfo): mixed
{
$fieldName = $resolveInfo->fieldName;

if ($root instanceof Model) {
$property = $root->getAttribute($fieldName);
if ($property === null && property_exists($root, $fieldName)) {
$property = $root->{$fieldName};
}
} else {
$property = Utils::extractKey($root, $fieldName);
}

return $property instanceof \Closure
? $property($root, $args, $context, $resolveInfo)
: $property;
}

protected function loadRoutesFrom($path): void
Expand Down
8 changes: 3 additions & 5 deletions tests/Integration/Models/PropertyAccessTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -105,8 +105,7 @@ public function testPhpProperty(): void
])->assertJson([
'data' => [
'user' => [
// TODO consider changing the default resolver so this returns User::PHP_PROPERTY_VALUE
'php_property' => null,
'php_property' => User::PHP_PROPERTY_VALUE,
],
],
]);
Expand Down Expand Up @@ -174,7 +173,7 @@ public function testPrefersAttributeAccessorNullThatShadowsPhpProperty(): void
])->assertJson([
'data' => [
'user' => [
'exists' => null,
'exists' => true,
],
],
]);
Expand Down Expand Up @@ -208,8 +207,7 @@ public function testExpensivePropertyIsOnlyCalledOnce(): void
])->assertJson([
'data' => [
'user' => [
// TODO consider changing the default resolver so this returns 1
'expensive_property' => 2,
'expensive_property' => 1,
],
],
]);
Expand Down