Skip to content

Commit 3ecc207

Browse files
spawniaclaude
andcommitted
Fix upsert for HasOne when existing record has no ID in input
When using `upsert` on a HasOne relation without providing an ID, the existing implementation always created a fresh model via `$relation->make()`. This caused duplicate records to be created instead of updating the existing related record. Now checks if a record already exists via `$relation->first()` before falling back to creating a new model instance. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent f606f12 commit 3ecc207

File tree

2 files changed

+50
-1
lines changed

2 files changed

+50
-1
lines changed

src/Execution/Arguments/NestedOneToOne.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,8 @@ public function __invoke($model, $args): void
3636
if ($args->has('upsert')) {
3737
$upsertModel = new ResolveNested(new UpsertModel(new SaveModel($relation)));
3838

39-
$upsertModel($relation->make(), $args->arguments['upsert']->value);
39+
$existing = $relation->first();
40+
$upsertModel($existing ?? $relation->make(), $args->arguments['upsert']->value);
4041
}
4142

4243
if ($args->has('delete')) {

tests/Integration/Execution/MutationExecutor/HasOneTest.php

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -423,6 +423,54 @@ public function testUpdateAndUpsertHasOne(string $action): void
423423
]);
424424
}
425425

426+
/** @dataProvider existingModelMutations */
427+
#[DataProvider('existingModelMutations')]
428+
public function testUpdateAndUpsertExistingHasOneWithoutID(string $action): void
429+
{
430+
$task = factory(Task::class)->create();
431+
$this->assertInstanceOf(Task::class, $task);
432+
433+
$task->post()
434+
->save(
435+
factory(Post::class)->create(),
436+
);
437+
438+
$this->graphQL(/** @lang GraphQL */ <<<GRAPHQL
439+
mutation {
440+
{$action}Task(input: {
441+
id: 1
442+
name: "foo"
443+
post: {
444+
upsert: {
445+
title: "bar"
446+
}
447+
}
448+
}) {
449+
id
450+
name
451+
post {
452+
id
453+
title
454+
}
455+
}
456+
}
457+
GRAPHQL)->assertJson([
458+
'data' => [
459+
"{$action}Task" => [
460+
'id' => '1',
461+
'name' => 'foo',
462+
'post' => [
463+
'id' => '1',
464+
'title' => 'bar',
465+
],
466+
],
467+
],
468+
]);
469+
470+
// Assert that the existing post was updated, not a new one created
471+
$this->assertSame(1, Post::count());
472+
}
473+
426474
/** @dataProvider existingModelMutations */
427475
#[DataProvider('existingModelMutations')]
428476
public function testUpdateAndDeleteHasOne(string $action): void

0 commit comments

Comments
 (0)