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
5 changes: 5 additions & 0 deletions src/DependencyInjection/Configuration.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

namespace GraphqlOrm\DependencyInjection;

use GraphqlOrm\Dialect\DefaultDialect;
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
use Symfony\Component\Config\Definition\ConfigurationInterface;

Expand All @@ -20,6 +21,10 @@ public function getConfigTreeBuilder(): TreeBuilder
->cannotBeEmpty()
->end()

->scalarNode('dialect')
->defaultValue(DefaultDialect::class)
->end()

->arrayNode('headers')
->scalarPrototype()->end()
->defaultValue([])
Expand Down
5 changes: 5 additions & 0 deletions src/DependencyInjection/GraphqlOrmExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,11 @@ public function load(array $configs, ContainerBuilder $container): void
$config['endpoint']
);

$container->setParameter(
'graphql_orm.dialect',
$config['dialect']
);

$container->setParameter(
'graphql_orm.headers',
$config['headers']
Expand Down
41 changes: 41 additions & 0 deletions src/Dialect/DataApiBuilderDialect.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<?php

declare(strict_types=1);

namespace GraphqlOrm\Dialect;

final class DataApiBuilderDialect implements GraphqlQueryDialect
{
public function wrapCollection(string $selection, int $indentLevel): string
{
$indent = str_repeat(' ', $indentLevel);
$innerIndent = str_repeat(' ', $indentLevel - 1);

$selection = $this->indent($selection, $innerIndent);

return <<<GQL
{$indent}items {
$selection
{$indent}}
GQL;
}

public function extractCollection(array $data): array
{
/** @var array<string|int, mixed> $items */
$items = $data['items'] ?? [];

return $items;
}

private function indent(string $text, string $indent): string
{
return implode(
"\n",
array_map(
fn ($line) => $line !== '' ? $indent . $line : $line,
explode("\n", $text)
)
);
}
}
18 changes: 18 additions & 0 deletions src/Dialect/DefaultDialect.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?php

declare(strict_types=1);

namespace GraphqlOrm\Dialect;

final class DefaultDialect implements GraphqlQueryDialect
{
public function wrapCollection(string $selection, int $indentLevel): string
{
return $selection;
}

public function extractCollection(array $data): array
{
return $data;
}
}
17 changes: 17 additions & 0 deletions src/Dialect/GraphqlQueryDialect.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?php

declare(strict_types=1);

namespace GraphqlOrm\Dialect;

interface GraphqlQueryDialect
{
public function wrapCollection(string $selection, int $indentLevel): string;

/**
* @param array<string, mixed> $data
*
* @return array<string|int, mixed>
*/
public function extractCollection(array $data): array;
}
8 changes: 8 additions & 0 deletions src/GraphqlManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@

use GraphqlOrm\Client\GraphqlClientInterface;
use GraphqlOrm\DataCollector\GraphqlOrmDataCollector;
use GraphqlOrm\Dialect\DefaultDialect;
use GraphqlOrm\Dialect\GraphqlQueryDialect;
use GraphqlOrm\Execution\GraphqlExecutionContext;
use GraphqlOrm\Hydrator\EntityHydrator;
use GraphqlOrm\Metadata\GraphqlEntityMetadataFactory;
Expand All @@ -27,6 +29,7 @@ public function __construct(
public EntityHydrator $hydrator,
public GraphqlOrmDataCollector $collector,
public int $maxDepth,
public GraphqlQueryDialect $dialect = new DefaultDialect(),
) {
}

Expand Down Expand Up @@ -62,4 +65,9 @@ public function execute(

return $entities;
}

public function getDialect(): GraphqlQueryDialect
{
return $this->dialect;
}
}
22 changes: 20 additions & 2 deletions src/Query/GraphqlQuery.php
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,25 @@ public function getResult(): array
return $this
->manager
->execute($this->graphql, hydration: function ($result, $context) use ($metadata) {
$rows = $result['data'][$metadata->name] ?? null;
$dialect = $this->manager->getDialect();
$data = $result['data'] ?? [];

if (!\array_key_exists($metadata->name, $data)) {
return [];
}

$root = $data[$metadata->name];

if ($root === null) {
return [];
}

if (!\is_array($root)) {
throw InvalidGraphqlResponseException::expectedArray($root);
}

$collection = $dialect->extractCollection($root);
$rows = !empty($collection) ? $collection : null;

if ($rows === null) {
return [];
Expand All @@ -51,7 +69,7 @@ public function getResult(): array
throw InvalidGraphqlResponseException::expectedArray($rows);
}

if ($rows && array_is_list($rows) === false) {
if (array_is_list($rows) === false) {
$rows = [$rows];
}

Expand Down
14 changes: 13 additions & 1 deletion src/Query/GraphqlQueryStringBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,8 @@ public function build(): string
}

$args = $this->buildArguments();
$dialect = $this->manager->getDialect();
$selection = $dialect->wrapCollection($selection, 2);

return <<<GRAPHQL
query {
Expand All @@ -128,6 +130,7 @@ private function buildAllFields(string $entityClass, int $level): ?string

try {
$metadata = $this->manager->metadataFactory->getMetadata($entityClass);
$dialect = $this->manager->getDialect();
$lines = [];

foreach ($metadata->fields as $field) {
Expand All @@ -141,7 +144,11 @@ private function buildAllFields(string $entityClass, int $level): ?string
continue;
}

$lines[] = $indent . $field->mappedFrom . " {\n" . $nested . "\n" . $indent . '}';
if ($field->isCollection) {
$lines[] = $indent . $field->mappedFrom . " {\n" . $dialect->wrapCollection($nested, $level) . "\n" . $indent . '}';
} else {
$lines[] = $indent . $field->mappedFrom . " {\n" . $nested . "\n" . $indent . '}';
}

continue;
}
Expand Down Expand Up @@ -274,6 +281,7 @@ private function relationFallbackSelection(GraphqlFieldMetadata $field, int $lev
if ($field->relation === null) {
return $field->mappedFrom;
}
$dialect = $this->manager->getDialect();

$relationMetadata = $this->manager->metadataFactory->getMetadata($field->relation);

Expand All @@ -283,6 +291,10 @@ private function relationFallbackSelection(GraphqlFieldMetadata $field, int $lev

$inner = str_repeat(' ', $level + 1);

if ($field->isCollection) {
return $field->mappedFrom . " {\n" . $inner . $dialect->wrapCollection($identifier, $level) . "\n" . $indent . '}';
}

return $field->mappedFrom . " {\n" . $inner . $identifier . "\n" . $indent . '}';
}

Expand Down
22 changes: 20 additions & 2 deletions src/Repository/GraphqlEntityRepository.php
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,25 @@ public function findBy(array $criteria): array
return $this
->manager
->execute($graphql, hydration: function (array $result, GraphqlExecutionContext $context) use ($metadata) {
$rows = $result['data'][$metadata->name] ?? null;
$dialect = $this->manager->getDialect();
$data = $result['data'] ?? [];

if (!\array_key_exists($metadata->name, $data)) {
return [];
}

$root = $data[$metadata->name];

if ($root === null) {
return [];
}

if (!\is_array($root)) {
throw InvalidGraphqlResponseException::expectedArray($root);
}

$collection = $dialect->extractCollection($root);
$rows = !empty($collection) ? $collection : null;

if ($rows === null) {
return [];
Expand All @@ -73,7 +91,7 @@ public function findBy(array $criteria): array
throw InvalidGraphqlResponseException::expectedArray($rows);
}

if ($rows && array_is_list($rows) === false) {
if (array_is_list($rows) === false) {
$rows = [$rows];
}

Expand Down
12 changes: 11 additions & 1 deletion src/Resources/config/services.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
use GraphqlOrm\Metadata\GraphqlEntityMetadataFactory;
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;

use function Symfony\Component\DependencyInjection\Loader\Configurator\service;

return static function (ContainerConfigurator $config) {
$services = $config->services()
->defaults()
Expand All @@ -20,8 +22,16 @@

$services->set(GraphqlEntityMetadataFactory::class);
$services->set(EntityHydrator::class);

$services
->set('graphql_orm.dialect', '%graphql_orm.dialect%')
->autowire()
->autoconfigure();

$services->set(GraphqlManager::class)
->arg('$maxDepth', '%graphql_orm.max_depth%');
->arg('$maxDepth', '%graphql_orm.max_depth%')
->arg('$dialect', service('graphql_orm.dialect'));

$services->set(GraphqlClientInterface::class);

$services->set(GraphqlClient::class)
Expand Down
40 changes: 40 additions & 0 deletions tests/Dialect/DataApiBuilderDialectTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
<?php

declare(strict_types=1);

namespace GraphqlOrm\Tests\Dialect;

use GraphqlOrm\Dialect\DataApiBuilderDialect;
use PHPUnit\Framework\TestCase;

final class DataApiBuilderDialectTest extends TestCase
{
public function testExtractCollectionReturnsItems(): void
{
$dialect = new DataApiBuilderDialect();
$data = [
'items' => [
['id' => 1],
['id' => 2],
],
];

self::assertSame($data['items'], $dialect->extractCollection($data));
}

public function testExtractCollectionReturnsEmptyWhenNoItems(): void
{
$dialect = new DataApiBuilderDialect();

self::assertSame([], $dialect->extractCollection([]));
}

public function testExtractCollectionReturnsEmptyWhenItemsNull(): void
{
$dialect = new DataApiBuilderDialect();

self::assertSame([], $dialect->extractCollection([
'items' => null,
]));
}
}
29 changes: 29 additions & 0 deletions tests/Dialect/DefaultDialectTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<?php

declare(strict_types=1);

namespace GraphqlOrm\Tests\Dialect;

use GraphqlOrm\Dialect\DefaultDialect;
use PHPUnit\Framework\TestCase;

final class DefaultDialectTest extends TestCase
{
public function testExtractCollectionReturnsSameArray(): void
{
$dialect = new DefaultDialect();
$data = [
['id' => 1],
['id' => 2],
];

self::assertSame($data, $dialect->extractCollection($data));
}

public function testExtractCollectionReturnsEmptyArray(): void
{
$dialect = new DefaultDialect();

self::assertSame([], $dialect->extractCollection([]));
}
}
5 changes: 5 additions & 0 deletions tests/Query/GraphqlQueryTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

namespace GraphqlOrm\Tests\Query;

use GraphqlOrm\Dialect\DefaultDialect;
use GraphqlOrm\Execution\GraphqlExecutionContext;
use GraphqlOrm\GraphqlManager;
use GraphqlOrm\Hydrator\EntityHydrator;
Expand Down Expand Up @@ -145,6 +146,10 @@ private function createManager(array $response, ?EntityHydrator $hydrator = null
->method('execute')
->willReturnCallback(fn ($_, $hydration) => $hydration($response, new GraphqlExecutionContext(), []));

$manager
->method('getDialect')
->willReturn(new DefaultDialect());

return $manager;
}
}
5 changes: 5 additions & 0 deletions tests/Repository/GraphqlEntityRepositoryTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

namespace GraphqlOrm\Tests\Repository;

use GraphqlOrm\Dialect\DefaultDialect;
use GraphqlOrm\Exception\InvalidGraphqlResponseException;
use GraphqlOrm\Execution\GraphqlExecutionContext;
use GraphqlOrm\GraphqlManager;
Expand Down Expand Up @@ -149,6 +150,10 @@ private function createManager(array $response, ?EntityHydrator $hydrator = null
->method('execute')
->willReturnCallback(fn ($_, $hydration) => $hydration($response, $this->createStub(GraphqlExecutionContext::class)));

$manager
->method('getDialect')
->willReturn(new DefaultDialect());

return $manager;
}
}