Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/TypeMapper.php
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ public function mapGraphQLTypeToPHPType(Type $type, ?bool $nullable = null, bool
}

if ($type instanceof ListOfType) {
return SymfonyType::list($this->mapGraphQLTypeToPHPType($type->getWrappedType(), $builtInOnly));
return SymfonyType::list($this->mapGraphQLTypeToPHPType($type->getWrappedType(), null, $builtInOnly));
}

if ($type instanceof ScalarType) {
Expand Down
50 changes: 50 additions & 0 deletions tests/ListScalar/Generated/Query/Test/Data.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
<?php

declare(strict_types=1);

namespace Ruudk\GraphQLCodeGenerator\ListScalar\Generated\Query\Test;

use Ruudk\GraphQLCodeGenerator\ListScalar\Generated\Query\Test\Data\Wallet;
use Ruudk\GraphQLCodeGenerator\ListScalar\ValueObjects\Currency;

// This file was automatically generated and should not be edited.

final class Data
{
/**
* @var list<Currency>
*/
public array $supportedCurrencies {
get => $this->supportedCurrencies ??= array_map(fn($item) => new Currency($item), $this->data['supportedCurrencies']);
}

public Wallet $wallet {
get => $this->wallet ??= new Wallet($this->data['wallet']);
}

/**
* @var list<Error>
*/
public readonly array $errors;

/**
* @param array{
* 'supportedCurrencies': list<string>,
* 'wallet': array{
* 'currencies': list<string>,
* 'name': string,
* },
* } $data
* @param list<array{
* 'code': string,
* 'debugMessage'?: string,
* 'message': string,
* }> $errors
*/
public function __construct(
private readonly array $data,
array $errors,
) {
$this->errors = array_map(fn(array $error) => new Error($error), $errors);
}
}
33 changes: 33 additions & 0 deletions tests/ListScalar/Generated/Query/Test/Data/Wallet.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<?php

declare(strict_types=1);

namespace Ruudk\GraphQLCodeGenerator\ListScalar\Generated\Query\Test\Data;

use Ruudk\GraphQLCodeGenerator\ListScalar\ValueObjects\Currency;

// This file was automatically generated and should not be edited.

final class Wallet
{
/**
* @var list<Currency>
*/
public array $currencies {
get => $this->currencies ??= array_map(fn($item) => new Currency($item), $this->data['currencies']);
}

public string $name {
get => $this->name ??= $this->data['name'];
}

/**
* @param array{
* 'currencies': list<string>,
* 'name': string,
* } $data
*/
public function __construct(
private readonly array $data,
) {}
}
23 changes: 23 additions & 0 deletions tests/ListScalar/Generated/Query/Test/Error.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?php

declare(strict_types=1);

namespace Ruudk\GraphQLCodeGenerator\ListScalar\Generated\Query\Test;

// This file was automatically generated and should not be edited.

final readonly class Error
{
public string $message;

/**
* @param array{
* 'debugMessage'?: string,
* 'message': string,
* } $error
*/
public function __construct(array $error)
{
$this->message = $error['debugMessage'] ?? $error['message'];
}
}
42 changes: 42 additions & 0 deletions tests/ListScalar/Generated/Query/Test/TestQuery.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<?php

declare(strict_types=1);

namespace Ruudk\GraphQLCodeGenerator\ListScalar\Generated\Query\Test;

use Ruudk\GraphQLCodeGenerator\TestClient;

// This file was automatically generated and should not be edited.

final readonly class TestQuery {
public const string OPERATION_NAME = 'Test';
public const string OPERATION_DEFINITION = <<<'GRAPHQL'
query Test {
supportedCurrencies
wallet {
name
currencies
}
}

GRAPHQL;

public function __construct(
private TestClient $client,
) {}

public function execute() : Data
{
$data = $this->client->graphql(
self::OPERATION_DEFINITION,
[
],
self::OPERATION_NAME,
);

return new Data(
$data['data'] ?? [], // @phpstan-ignore argument.type
$data['errors'] ?? [] // @phpstan-ignore argument.type
);
}
}
61 changes: 61 additions & 0 deletions tests/ListScalar/ListScalarTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
<?php

declare(strict_types=1);

namespace Ruudk\GraphQLCodeGenerator\ListScalar;

use Override;
use Ruudk\GraphQLCodeGenerator\Config\Config;
use Ruudk\GraphQLCodeGenerator\GraphQLTestCase;
use Ruudk\GraphQLCodeGenerator\ListScalar\Generated\Query\Test\TestQuery;
use Ruudk\GraphQLCodeGenerator\ListScalar\ValueObjects\Currency;
use Symfony\Component\TypeInfo\Type;

/**
* Test that lists of custom scalars are correctly typed in the generated code.
*
* This tests the fix for a bug where the $builtInOnly parameter was not correctly
* passed when processing ListOfType in TypeMapper::mapGraphQLTypeToPHPType().
*
* The bug caused the $data parameter docblock to incorrectly use the PHP mapped type
* (e.g., Currency) instead of the primitive JSON type (e.g., string) for list items.
*/
final class ListScalarTest extends GraphQLTestCase
{
#[Override]
public function getConfig() : Config
{
return parent::getConfig()
->withScalar('Currency', Type::string(), Type::object(Currency::class));
}

public function testGenerate() : void
{
$this->assertActualMatchesExpected();
}

public function testQueryWithListOfScalars() : void
{
$result = new TestQuery($this->getClient([
'data' => [
'supportedCurrencies' => ['EUR', 'USD', 'GBP'],
'wallet' => [
'name' => 'My Wallet',
'currencies' => ['EUR', 'USD'],
],
],
]))->execute();

// Verify the list of currencies is correctly converted to Currency objects
self::assertCount(3, $result->supportedCurrencies);
self::assertSame('EUR', $result->supportedCurrencies[0]->code);
self::assertSame('USD', $result->supportedCurrencies[1]->code);
self::assertSame('GBP', $result->supportedCurrencies[2]->code);

// Verify nested list of currencies
self::assertSame('My Wallet', $result->wallet->name);
self::assertCount(2, $result->wallet->currencies);
self::assertSame('EUR', $result->wallet->currencies[0]->code);
self::assertSame('USD', $result->wallet->currencies[1]->code);
}
}
11 changes: 11 additions & 0 deletions tests/ListScalar/Schema.graphql
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
scalar Currency

type Query {
supportedCurrencies: [Currency!]!
wallet: Wallet!
}

type Wallet {
name: String!
currencies: [Currency!]!
}
7 changes: 7 additions & 0 deletions tests/ListScalar/Test.graphql
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
query Test {
supportedCurrencies
wallet {
name
currencies
}
}
33 changes: 33 additions & 0 deletions tests/ListScalar/ValueObjects/Currency.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<?php

declare(strict_types=1);

namespace Ruudk\GraphQLCodeGenerator\ListScalar\ValueObjects;

use InvalidArgumentException;
use JsonSerializable;
use Override;
use Stringable;

final readonly class Currency implements JsonSerializable, Stringable
{
public function __construct(
public string $code,
) {
if (strlen($code) !== 3) {
throw new InvalidArgumentException('Currency code must be 3 characters');
}
}

#[Override]
public function jsonSerialize() : string
{
return $this->code;
}

#[Override]
public function __toString() : string
{
return $this->code;
}
}