Skip to content

Commit

Permalink
Native PHP Enums registered through the TypeRegistry may be used as…
Browse files Browse the repository at this point in the history
… morph type in nested `MorphTo` relations
  • Loading branch information
spawnia committed May 1, 2024
1 parent c674847 commit 6f3cd35
Show file tree
Hide file tree
Showing 5 changed files with 110 additions and 2 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ You can find and compare releases at the [GitHub release page](https://github.co

## Unreleased

### Fixed

- Native PHP Enums registered through the `TypeRegistry` may be used as morph type in nested `MorphTo` relations https://github.com/nuwave/lighthouse/pull/2544

## v6.36.0

### Added
Expand Down
3 changes: 3 additions & 0 deletions phpstan.neon
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ parameters:
- src/Tracing/FederatedTracing/Proto # Generated classes from protobuf
# Ignore errors caused by the absence of Lumen in the dev dependencies
- tests/Unit/Testing/TestingTraitDummyLumen.php
# Native enums
- tests/Utils/Enums/ImageableType.php
- tests/Utils/Enums/ImageableType.php
ignoreErrors:
# PHPStan does not get it
- '#Parameter \#1 \$callback of static method Closure::fromCallable\(\) expects callable\(\): mixed, array{object, .*} given\.#'
Expand Down
27 changes: 25 additions & 2 deletions src/Execution/Arguments/NestedMorphTo.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace Nuwave\Lighthouse\Execution\Arguments;

use Illuminate\Database\Eloquent\Relations\MorphTo;
use Nuwave\Lighthouse\Exceptions\DefinitionException;
use Nuwave\Lighthouse\Support\Contracts\ArgResolver;

class NestedMorphTo implements ArgResolver
Expand All @@ -24,18 +25,40 @@ public function __invoke($parent, $args): void

if ($args->has('connect')) {
$connectArgs = $args->arguments['connect']->value;
$connectArgsArguments = $connectArgs->arguments;

$morphToModel = $this->relation->createModelByType(
(string) $connectArgs->arguments['type']->value,
$this->morphTypeValue($connectArgsArguments['type']->value),
);
$morphToModel->setAttribute(
$morphToModel->getKeyName(),
$connectArgs->arguments['id']->value,
$connectArgsArguments['id']->value,
);

$this->relation->associate($morphToModel);
}

NestedBelongsTo::disconnectOrDelete($this->relation, $args);
}

protected function morphTypeValue(mixed $morphType): string
{
if (PHP_VERSION_ID >= 80100) {
if ($morphType instanceof \BackedEnum) {
$value = $morphType->value;
if (! is_string($value)) {
$enumClass = $morphType::class;
throw new DefinitionException("Enum {$enumClass} must be string backed.");
}

return $value;
}

if ($morphType instanceof \UnitEnum) {
return $morphType->name;
}
}

return (string) $morphType;
}
}
68 changes: 68 additions & 0 deletions tests/Integration/Execution/MutationExecutor/MorphToTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@

namespace Tests\Integration\Execution\MutationExecutor;

use GraphQL\Type\Definition\PhpEnumType;
use Nuwave\Lighthouse\Schema\TypeRegistry;
use Tests\DBTestCase;
use Tests\Utils\Enums\ImageableType;
use Tests\Utils\Models\Image;
use Tests\Utils\Models\Task;

Expand All @@ -22,6 +25,7 @@ final class MorphToTest extends DBTestCase
type Mutation {
createImage(input: CreateImageInput! @spread): Image @create
createImageWithEnumType(input: CreateImageWithEnumTypeInput! @spread): Image @create
updateImage(input: UpdateImageInput! @spread): Image @update
upsertImage(input: UpsertImageInput! @spread): Image @upsert
}
Expand All @@ -42,6 +46,22 @@ final class MorphToTest extends DBTestCase
id: ID!
}
input CreateImageWithEnumTypeInput {
from: String
to: String
url: String
imageable: CreateImageableOperationsWithEnumType
}
input CreateImageableOperationsWithEnumType {
connect: ConnectImageableWithEnumTypeInput
}
input ConnectImageableWithEnumTypeInput {
type: ImageableType!
id: ID!
}
input UpdateImageInput {
id: ID!
from: String
Expand Down Expand Up @@ -111,6 +131,54 @@ public function testConnectsMorphTo(): void
]);
}

public function testConnectsMorphToWithEnumType(): void
{
if (PHP_VERSION_ID < 80100) {
$this->markTestSkipped('Requires native enums.');
}

$typeRegistry = $this->app->make(TypeRegistry::class);
$phpEnumType = new PhpEnumType(ImageableType::class); // @phpstan-ignore-line native enums not supported in all versions
$typeRegistry->register($phpEnumType);

$task = factory(Task::class)->make();
assert($task instanceof Task);
$task->name = 'first_task';
$task->save();

$this->graphQL(/** @lang GraphQL */ '
mutation {
createImageWithEnumType(input: {
url: "foo"
imageable: {
connect: {
type: TASK
id: 1
}
}
}) {
id
url
imageable {
id
name
}
}
}
')->assertJson([
'data' => [
'createImageWithEnumType' => [
'id' => '1',
'url' => 'foo',
'imageable' => [
'id' => '1',
'name' => 'first_task',
],
],
],
]);
}

public function testConnectsMorphToWithUpsert(): void
{
$task = factory(Task::class)->make();
Expand Down
10 changes: 10 additions & 0 deletions tests/Utils/Enums/ImageableType.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?php declare(strict_types=1);

namespace Tests\Utils\Enums;

use Tests\Utils\Models\Task;

enum ImageableType: string
{
case TASK = Task::class;
}

0 comments on commit 6f3cd35

Please sign in to comment.