From 831ff950c56dfa67b5133db9490f2c98f2587562 Mon Sep 17 00:00:00 2001 From: Benedikt Franke Date: Tue, 25 Jan 2022 16:29:07 +0100 Subject: [PATCH 1/4] Combine relation loading in nested toMany relations --- src/Execution/Arguments/NestedOneToMany.php | 28 +++++++++++++--- src/Execution/Arguments/UpdateModel.php | 37 +++++++++++++++++++-- 2 files changed, 58 insertions(+), 7 deletions(-) diff --git a/src/Execution/Arguments/NestedOneToMany.php b/src/Execution/Arguments/NestedOneToMany.php index 754a5bc710..874bbc8f9b 100644 --- a/src/Execution/Arguments/NestedOneToMany.php +++ b/src/Execution/Arguments/NestedOneToMany.php @@ -50,11 +50,31 @@ public static function createUpdateUpsert(ArgumentSet $args, Relation $relation) } if ($args->has('update')) { - $updateModel = new ResolveNested(new UpdateModel(new SaveModel($relation))); + $updateModel = new ResolveNested(new SaveModel($relation)); - foreach ($args->arguments['update']->value as $childArgs) { - // @phpstan-ignore-next-line Relation&Builder mixin not recognized - $updateModel($relation->make(), $childArgs); + // @phpstan-ignore-next-line Relation&Builder mixin not recognized + $model = $relation->make(); + + $ids = []; + + $updateArgs = $args->arguments['update']->value; + + foreach ($updateArgs as $childArgs) { + $ids[UpdateModel::getId($childArgs, $model)] = true; + } + + $models = $model->newModelQuery() + ->where($model->getKeyName(), array_keys($ids)) + ->get(); + + foreach ($models as $instance) { + $id = $instance->getKey(); + $ids[$id] = $instance; + } + + foreach($updateArgs as $childArgs) { + $instance = $ids[UpdateModel::pullId($childArgs, $model)]; + $updateModel($instance, $childArgs); } } diff --git a/src/Execution/Arguments/UpdateModel.php b/src/Execution/Arguments/UpdateModel.php index 65b8775ee2..6bca33dd87 100644 --- a/src/Execution/Arguments/UpdateModel.php +++ b/src/Execution/Arguments/UpdateModel.php @@ -3,6 +3,7 @@ namespace Nuwave\Lighthouse\Execution\Arguments; use GraphQL\Error\Error; +use Illuminate\Database\Eloquent\Model; use Illuminate\Support\Arr; use Nuwave\Lighthouse\Support\Contracts\ArgResolver; @@ -24,10 +25,23 @@ public function __construct(callable $previous) } /** - * @param \Illuminate\Database\Eloquent\Model $model + * @param Model $model * @param \Nuwave\Lighthouse\Execution\Arguments\ArgumentSet $args */ public function __invoke($model, $args) + { + $id = self::pullId($args, $model); + $instance = $model->newQuery()->findOrFail($id); + + return ($this->previous)($instance, $args); + } + + /** + * Extract and remove the model ID from the given args. + * + * @return mixed any non-null ID value + */ + public static function pullId(ArgumentSet $args, Model $model) { /** @var \Nuwave\Lighthouse\Execution\Arguments\Argument|null $id */ $id = Arr::pull($args->arguments, 'id') @@ -38,8 +52,25 @@ public function __invoke($model, $args) throw new Error(self::MISSING_PRIMARY_KEY_FOR_UPDATE); } - $model = $model->newQuery()->findOrFail($id->value); + return $id->value; + } + + /** + * Extract and remove the model ID from the given args. + * + * @return mixed any non-null ID value + */ + public static function getId(ArgumentSet $args, Model $model) + { + /** @var \Nuwave\Lighthouse\Execution\Arguments\Argument|null $id */ + $id = Arr::get($args->arguments, 'id') + ?? Arr::get($args->arguments, $model->getKeyName()) + ?? null; + + if (null === $id) { + throw new Error(self::MISSING_PRIMARY_KEY_FOR_UPDATE); + } - return ($this->previous)($model, $args); + return $id->value; } } From db350c7661d937a52e99b37c296fb7626b5ac470 Mon Sep 17 00:00:00 2001 From: spawnia Date: Tue, 25 Jan 2022 15:30:23 +0000 Subject: [PATCH 2/4] Apply php-cs-fixer changes --- src/Execution/Arguments/NestedOneToMany.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Execution/Arguments/NestedOneToMany.php b/src/Execution/Arguments/NestedOneToMany.php index 874bbc8f9b..0d7016438d 100644 --- a/src/Execution/Arguments/NestedOneToMany.php +++ b/src/Execution/Arguments/NestedOneToMany.php @@ -72,7 +72,7 @@ public static function createUpdateUpsert(ArgumentSet $args, Relation $relation) $ids[$id] = $instance; } - foreach($updateArgs as $childArgs) { + foreach ($updateArgs as $childArgs) { $instance = $ids[UpdateModel::pullId($childArgs, $model)]; $updateModel($instance, $childArgs); } From 49acd209d8a85a4b28737b370b699235ebb58fc0 Mon Sep 17 00:00:00 2001 From: Benedikt Franke Date: Tue, 25 Jan 2022 16:41:12 +0100 Subject: [PATCH 3/4] whereIn --- src/Execution/Arguments/NestedOneToMany.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Execution/Arguments/NestedOneToMany.php b/src/Execution/Arguments/NestedOneToMany.php index 874bbc8f9b..93f088ccda 100644 --- a/src/Execution/Arguments/NestedOneToMany.php +++ b/src/Execution/Arguments/NestedOneToMany.php @@ -64,7 +64,7 @@ public static function createUpdateUpsert(ArgumentSet $args, Relation $relation) } $models = $model->newModelQuery() - ->where($model->getKeyName(), array_keys($ids)) + ->whereIn($model->getKeyName(), array_keys($ids)) ->get(); foreach ($models as $instance) { @@ -73,6 +73,7 @@ public static function createUpdateUpsert(ArgumentSet $args, Relation $relation) } foreach($updateArgs as $childArgs) { + dump($ids); $instance = $ids[UpdateModel::pullId($childArgs, $model)]; $updateModel($instance, $childArgs); } From 0f77d5652ed10792e2df2a7ad5fb3ac79c94949c Mon Sep 17 00:00:00 2001 From: Benedikt Franke Date: Tue, 25 Jan 2022 16:44:05 +0100 Subject: [PATCH 4/4] test --- .../MutationExecutor/HasManyTest.php | 40 ++++++++++++++----- 1 file changed, 30 insertions(+), 10 deletions(-) diff --git a/tests/Integration/Execution/MutationExecutor/HasManyTest.php b/tests/Integration/Execution/MutationExecutor/HasManyTest.php index 5b8854454b..76f644b4eb 100644 --- a/tests/Integration/Execution/MutationExecutor/HasManyTest.php +++ b/tests/Integration/Execution/MutationExecutor/HasManyTest.php @@ -373,8 +373,8 @@ public function testUpdateHasMany(string $action): void $user = factory(User::class)->create(); $user->tasks() - ->save( - factory(Task::class)->create() + ->saveMany( + factory(Task::class, 2)->create() ); $this->graphQL(/** @lang GraphQL */ << [ [ 'id' => '1', + 'name' => 'foo', + ], + [ + 'id' => '2', 'name' => 'bar', ], ], @@ -433,10 +443,16 @@ public function testUpsertHasMany(string $action): void id: 1 name: "foo" tasks: { - upsert: [{ - id: 1 - name: "bar" - }] + upsert: [ + { + id: 1 + name: "foo" + }, + { + id: 2 + name: "bar" + }, + ] } }) { id @@ -456,6 +472,10 @@ public function testUpsertHasMany(string $action): void 'tasks' => [ [ 'id' => '1', + 'name' => 'foo', + ], + [ + 'id' => '2', 'name' => 'bar', ], ],