You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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
    }
  }
}

问题分析与解决方案

核心问题原因

  1. 指令应用位置不匹配:当前@variantCrossLocation指令的定义中,on仅包含ARGUMENT_DEFINITION | INPUT_FIELD_DEFINITION,但你将其用在Variant类型的字段(属于FIELD_DEFINITION)上,Lighthouse无法识别该指令为字段指令,导致FieldBuilderDirective的方法无法触发。
  2. 接口适用性不符: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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.13 11:40:30