Laravel Lighthouse中如何通过字段指令操作GraphQL查询构建器?
问题描述
需要给Variant类型添加crossLocationId字段,当查询包含该字段时,让查询执行交叉连接以扩充结果数量。计划通过自定义指令操作查询构建器,参考文档后选择实现FieldBuilderDirective接口并编写handleFieldBuilder方法,但该方法始终无法触发。尝试过FieldMiddleware接口(handleField方法可触发,但无法满足操作查询构建器的需求),核心问题仍未解决。
Schema 代码
extend type Query { Variants: [Variant!]! @paginate(defaultCount: 50, scopes: ["company"]) @guard @softDeletes } type Variant { id: ID! crossLocationId: Int @variantCrossLocation(argument:"a") }
自定义指令代码
<?php namespace Nuwave\Lighthouse\Schema\Directives; use Closure; use GraphQL\Type\Definition\ResolveInfo; use GraphQL\Language\AST\FieldDefinitionNode; use Nuwave\Lighthouse\Schema\AST\DocumentAST; use Nuwave\Lighthouse\Schema\Values\FieldValue; use GraphQL\Language\AST\ObjectTypeDefinitionNode; use Nuwave\Lighthouse\Support\Contracts\GraphQLContext; use Nuwave\Lighthouse\Support\Contracts\FieldMiddleware; use Nuwave\Lighthouse\Support\Contracts\FieldManipulator; use Nuwave\Lighthouse\Support\Contracts\ArgBuilderDirective; use Nuwave\Lighthouse\Support\Contracts\FieldBuilderDirective; class VariantCrossLocationDirective extends BaseDirective implements FieldBuilderDirective // ArgBuilderDirective // FieldManipulator, // FieldMiddleware // \Nuwave\Lighthouse\Support\Contracts\ArgBuilderDirective { use RelationDirectiveHelpers; public static function definition(): string { return /* @lang GraphQL */ <<<'GRAPHQL' directive @variantCrossLocation( argument: String )repeatable on ARGUMENT_DEFINITION | INPUT_FIELD_DEFINITION GRAPHQL; } public function handleField(FieldValue $fieldValue, Closure $next) { dump('a'); $resolver = $fieldValue->getResolver(); return $next( $fieldValue->setResolver(function ( $root, array $args, GraphQLContext $context, ResolveInfo $resolveInfo ) use ($resolver) { $result = $resolver($root, $args, $context, $resolveInfo); return $result; }) ); } public function handleFieldBuilder(object $builder): object { dump('b'); return $builder; } public function handleBuilder(object $builder): object { dump('c'); return $builder; } public function manipulateFieldDefinition(DocumentAST &$documentAST, FieldDefinitionNode &$fieldDefinition, ObjectTypeDefinitionNode &$parentType) { dump('d'); } }
GraphQL 查询
{ Variants{ data{ id crossLocationId } } }
问题分析与解决方案
核心问题原因
- 指令应用位置不匹配:当前
@variantCrossLocation指令的定义中,on仅包含ARGUMENT_DEFINITION | INPUT_FIELD_DEFINITION,但你将其用在Variant类型的字段(属于FIELD_DEFINITION)上,Lighthouse无法识别该指令为字段指令,导致FieldBuilderDirective的方法无法触发。 - 接口适用性不符:
FieldBuilderDirective仅负责单个字段的查询构建过程,而你的需求是修改父级Variants列表查询的构建器,该接口无法直接实现此需求。
解决方案步骤
1. 修正指令应用范围
修改指令的definition()方法,将on属性添加FIELD_DEFINITION,确保指令能被应用到对象类型的字段上:
public static function definition(): string { return /* @lang GraphQL */ <<<'GRAPHQL' directive @variantCrossLocation( argument: String ) repeatable on FIELD_DEFINITION | ARGUMENT_DEFINITION | INPUT_FIELD_DEFINITION GRAPHQL; }
2. 使用FieldMiddleware实现需求
通过FieldMiddleware接口,结合ResolveInfo判断是否请求了crossLocationId字段,进而修改父查询的构建器。以下是调整后的完整指令代码:
<?php namespace Nuwave\Lighthouse\Schema\Directives; use Closure; use GraphQL\Type\Definition\ResolveInfo; use Nuwave\Lighthouse\Schema\Values\FieldValue; use Nuwave\Lighthouse\Support\Contracts\GraphQLContext; use Nuwave\Lighthouse\Support\Contracts\FieldMiddleware; class VariantCrossLocationDirective extends BaseDirective implements FieldMiddleware { public static function definition(): string { return /* @lang GraphQL */ <<<'GRAPHQL' directive @variantCrossLocation( argument: String ) repeatable on FIELD_DEFINITION GRAPHQL; } public function handleField(FieldValue $fieldValue, Closure $next) { $resolver = $fieldValue->getResolver(); return $next( $fieldValue->setResolver(function ( $root, array $args, GraphQLContext $context, ResolveInfo $resolveInfo ) use ($resolver) { // 检查当前查询是否包含crossLocationId字段 $hasCrossLocationField = collect($resolveInfo->fieldNodes) ->flatMap(fn($node) => $node->selectionSet->selections) ->contains(fn($selection) => $selection->name->value === 'crossLocationId'); // 针对分页查询结果,获取并修改基础查询构建器 if ($hasCrossLocationField && method_exists($root, 'getQuery')) { $queryBuilder = $root->getQuery(); // 根据业务需求调整交叉连接的表名与关联条件 $queryBuilder->crossJoin('locations', 'variants.location_id', '=', 'locations.id'); } return $resolver($root, $args, $context, $resolveInfo); }) ); } }
3. 注册指令
在config/lighthouse.php的directives数组中添加指令注册:
'directives' => [ // 其他已注册指令 'variantCrossLocation' => \Nuwave\Lighthouse\Schema\Directives\VariantCrossLocationDirective::class, ],
验证
执行原GraphQL查询,此时查询构建器会自动添加交叉连接,返回符合需求的结果。
内容的提问来源于stack exchange,提问作者Antoine Drobecq
相关产品推荐
相关产品推荐

