/**
* Given list of parent type values returns corresponding list of field values
*
* In particular, this
* figures out the value that the field returns by calling its `resolve` or `map` function,
* then calls `completeValue` on each value to serialize scalars, or execute the sub-selection-set
* for objects.
*
* @param ExecutionContext $exeContext
* @param ObjectType $parentType
* @param $sourceValueList
* @param $fieldASTs
* @return array
* @throws Error
*/
private static function resolveField(ExecutionContext $exeContext, ObjectType $parentType, $sourceValueList, $fieldASTs, $responseName, &$resolveResult)
{
$fieldAST = $fieldASTs[0];
$fieldName = $fieldAST->name->value;
$fieldDef = self::getFieldDef($exeContext->schema, $parentType, $fieldName);
if (!$fieldDef) {
return;
}
$returnType = $fieldDef->getType();
// Build hash of arguments from the field.arguments AST, using the
// variables scope to fulfill any variable references.
// TODO: find a way to memoize, in case this field is within a List type.
$args = Values::getArgumentValues($fieldDef->args, $fieldAST->arguments, $exeContext->variableValues);
// The resolve function's optional third argument is a collection of
// information about the current execution state.
$info = new ResolveInfo(['fieldName' => $fieldName, 'fieldASTs' => $fieldASTs, 'returnType' => $returnType, 'parentType' => $parentType, 'schema' => $exeContext->schema, 'fragments' => $exeContext->fragments, 'rootValue' => $exeContext->rootValue, 'operation' => $exeContext->operation, 'variableValues' => $exeContext->variableValues]);
$mapFn = $fieldDef->mapFn;
// If an error occurs while calling the field `map` or `resolve` function, ensure that
// it is wrapped as a GraphQLError with locations. Log this error and return
// null if allowed, otherwise throw the error so the parent field can handle
// it.
if ($mapFn) {
try {
$mapped = call_user_func($mapFn, $sourceValueList, $args, $info);
$validType = is_array($mapped) || $mapped instanceof \Traversable && $mapped instanceof \Countable;
$mappedCount = count($mapped);
$sourceCount = count($sourceValueList);
Utils::invariant($validType && count($mapped) === count($sourceValueList), "Function `map` of {$parentType}.{$fieldName} is expected to return array or " . "countable traversable with exact same number of items as list being mapped. " . "Got '%s' with count '{$mappedCount}' against '{$sourceCount}' expected.", Utils::getVariableType($mapped));
} catch (\Exception $error) {
$reportedError = Error::createLocatedError($error, $fieldASTs);
if ($returnType instanceof NonNull) {
throw $reportedError;
}
$exeContext->addError($reportedError);
return null;
}
foreach ($mapped as $index => $value) {
$resolveResult[$index][$responseName] = self::completeValueCatchingError($exeContext, $returnType, $fieldASTs, $info, $value);
}
} else {
if (isset($fieldDef->resolveFn)) {
$resolveFn = $fieldDef->resolveFn;
} else {
if (isset($parentType->resolveFieldFn)) {
$resolveFn = $parentType->resolveFieldFn;
} else {
$resolveFn = self::$defaultResolveFn;
}
}
foreach ($sourceValueList as $index => $value) {
try {
$resolved = call_user_func($resolveFn, $value, $args, $info);
} catch (\Exception $error) {
$reportedError = Error::createLocatedError($error, $fieldASTs);
if ($returnType instanceof NonNull) {
throw $reportedError;
}
$exeContext->addError($reportedError);
$resolved = null;
}
$resolveResult[$index][$responseName] = self::completeValueCatchingError($exeContext, $returnType, $fieldASTs, $info, $resolved);
}
}
}