PHP Lighthouse get parameter from root query in FieldResolver

930 Views Asked by At

I have the following setup of queries:

extend type Query {
    positionGroups(quoteId: ID): [PositionGroup!]! @all 
}

type PositionGroup {
    ...
    positions: [Position!]!
}

type Position {
    ...
    amount: Amount @amountEnhancedWithQuoteAmountInfo
}

The amount in a Position normally returns a default, but if we are in the context of a specific quote it could change. That's what the AmountEnhancedWithQuoteAmountInfoDerictive should do.

So inside the AmountEnhancedWithQuoteAmountInfoDerictive I need the quoteId value (if any). And then I can apply some extra logic to get the quote-specific amount from the database. If no quoteId was given, I don't have to do anything extra.

My directive looks like this:

class AmountEnhancedWithLBHQuoteAmountInfoDirective extends BaseDirective implements FieldResolver
{
    /**
     * @inheritDoc
     */
    public function resolveField(FieldValue $fieldValue)
    {
        $this->$fieldValue->setResolver(function ($root, $args, GraphQLContext $context, ResolveInfo $resolveInfo) {
            $value = $root->amount;

            $something->quoteId; // What should this be?
            // Change $value based on the quote

            return $value;
        });

        return $fieldValue;
    }
}

The $root variable is just my Position, and I couldn't find the quoteId in the other parameters either.

So is there a way to access the quoteId there?

One way I could think of is writing custom queries for each part, and simply passing quoteId along. Though is there a better way of doing it?

Note: Position is in no way related to a Quote, however, in the context of a quote I want to add some extra information to it essentially. So there is no way knowing what Quote the query is about without the user giving a quoteId parameter.

1

There are 1 best solutions below

0
On BEST ANSWER

I build a solution myself, I created a PassAlongDirective which will pass along arguments or fields to the children:

class PassAlongDirective extends BaseDirective implements FieldMiddleware, DefinedDirective
{
    public static function definition(): string
    {
        return 'Passes along parameters to children.';
    }

    public function handleField(FieldValue $fieldValue, Closure $next): FieldValue
    {
        $resolver = $fieldValue->getResolver();
        $fieldsToPassAlong = $this->directiveArgValue('fields', []);

        $fieldValue->setResolver(function ($root, array $args, GraphQLContext $context, ResolveInfo $resolveInfo) use ($resolver, $fieldsToPassAlong) {
            $result = $resolver($root, $args, $context, $resolveInfo);

            foreach ($fieldsToPassAlong as $field) {
                $value = $args[$field] ?? $root->{$field} ?? null;
                if ($value) {
                    $this->passAlongTo($result, $field, $value);
                }
            }

            return $result;
        });

        return $next($fieldValue);
    }

    private function passAlongTo($result, $field, $value)
    {
        if ($result instanceof Collection || $result instanceof Paginator) {
            foreach ($result as $item) {
                $this->setField($item, $field, $value);
            }
        } else {
            $this->setField($result, $field, $value);
        }
    }

    private function setField($item, $field, $value)
    {
        $item->{$field} = $value;
    }
}

It can be used like:

positionGroups(quoteId: ID): [PositionGroup!]! @all @passAlong(fields: ["quoteId"])