Skip to content

Commit 7738edf

Browse files
authored
Merge pull request #189 from TomHAnderson/feature/ghost-in-the-container
Use LazyGhosts in containers
2 parents 87646bd + 6a51947 commit 7738edf

File tree

5 files changed

+92
-54
lines changed

5 files changed

+92
-54
lines changed

src/Filter/FilterFactory.php

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
use GraphQL\Type\Definition\ScalarType;
1717
use GraphQL\Type\Definition\Type;
1818
use League\Event\EventDispatcher;
19+
use ReflectionClass;
1920

2021
use function array_filter;
2122
use function array_keys;
@@ -88,10 +89,15 @@ public function get(
8889
$fields = $this->addFields($targetEntity, $allowedFilters);
8990
$fields = array_merge($fields, $this->addAssociations($targetEntity, $allowedFilters));
9091

91-
$inputObject = new GraphQLInputObjectType([
92-
'name' => $typeName,
93-
'fields' => static fn () => $fields,
94-
]);
92+
$inputObject = (new ReflectionClass(GraphQLInputObjectType::class))
93+
->newLazyGhost(static function (GraphQLInputObjectType $object) use ($typeName, $fields): void {
94+
$object->__construct(
95+
[
96+
'name' => $typeName,
97+
'fields' => static fn () => $fields,
98+
],
99+
);
100+
});
95101

96102
$this->typeContainer->set($typeName, $inputObject);
97103

src/Hydrator/HydratorContainer.php

Lines changed: 31 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
use Laminas\Hydrator\NamingStrategy\MapNamingStrategy;
1313
use Laminas\Hydrator\Strategy\StrategyInterface;
1414
use Override;
15+
use ReflectionClass;
1516

1617
use function assert;
1718
use function class_implements;
@@ -44,26 +45,39 @@ public function get(string $id): mixed
4445
return parent::get($id);
4546
}
4647

47-
$entity = $this->entityTypeContainer->get($id);
48-
$metadata = $entity->getMetadata();
49-
$hydrator = new DoctrineObject($this->entityManager, $metadata['byValue']);
48+
$self = $this;
49+
// Compose hydrators as Lazy Ghosts
50+
$hydrator = (new ReflectionClass(DoctrineObject::class))
51+
->newLazyGhost(static function (DoctrineObject $object) use ($self, $id): void {
52+
$entityManager = $self->entityManager;
53+
$entity = $self->entityTypeContainer->get($id);
54+
$metadata = $entity->getMetadata();
55+
$byValue = $metadata['byValue'];
5056

51-
// Create field strategy and assign to hydrator
52-
foreach ($metadata['fields'] as $fieldName => $fieldMetadata) {
53-
assert(
54-
in_array(StrategyInterface::class, class_implements($fieldMetadata['hydratorStrategy'])),
55-
'Strategy must implement ' . StrategyInterface::class,
56-
);
57+
$object->__construct(
58+
$entityManager,
59+
$byValue,
60+
);
5761

58-
$hydrator->addStrategy($fieldName, $this->get($fieldMetadata['hydratorStrategy']));
59-
}
62+
// Create field strategy and assign to hydrator
63+
foreach ($metadata['fields'] as $fieldName => $fieldMetadata) {
64+
assert(
65+
in_array(StrategyInterface::class, class_implements($fieldMetadata['hydratorStrategy'])),
66+
'Strategy must implement ' . StrategyInterface::class,
67+
);
6068

61-
// Create naming strategy for aliases and assign to hydrator
62-
if ($entity->getExtractionMap()) {
63-
$hydrator->setNamingStrategy(
64-
MapNamingStrategy::createFromExtractionMap($entity->getExtractionMap()),
65-
);
66-
}
69+
$object->addStrategy($fieldName, $self->get($fieldMetadata['hydratorStrategy']));
70+
}
71+
72+
// Create naming strategy for aliases and assign to hydrator
73+
if (! $entity->getExtractionMap()) {
74+
return;
75+
}
76+
77+
$object->setNamingStrategy(
78+
MapNamingStrategy::createFromExtractionMap($entity->getExtractionMap()),
79+
);
80+
});
6781

6882
$this->set($id, $hydrator);
6983

src/Input/InputFactory.php

Lines changed: 21 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
use GraphQL\Type\Definition\InputObjectField;
1414
use GraphQL\Type\Definition\InputObjectType;
1515
use GraphQL\Type\Definition\Type;
16+
use ReflectionClass;
1617

1718
use function count;
1819
use function in_array;
@@ -41,21 +42,26 @@ public function __construct(
4142
*/
4243
public function get(string $id, array $requiredFields = [], array $optionalFields = []): InputObjectType
4344
{
44-
$fields = [];
45-
$targetEntity = $this->entityTypeContainer->get($id);
46-
47-
if (! count($requiredFields) && ! count($optionalFields)) {
48-
$this->addAllFieldsAsRequired($targetEntity, $fields);
49-
} else {
50-
$this->addRequiredFields($targetEntity, $requiredFields, $fields);
51-
$this->addOptionalFields($targetEntity, $optionalFields, $fields);
52-
}
53-
54-
return new InputObjectType([
55-
'name' => $targetEntity->getTypeName() . '_Input_' . uniqid(),
56-
'description' => $targetEntity->getDescription(),
57-
'fields' => static fn () => $fields,
58-
]);
45+
$self = $this;
46+
47+
return (new ReflectionClass(InputObjectType::class))
48+
->newLazyGhost(static function (InputObjectType $object) use ($self, $id, $requiredFields, $optionalFields): void {
49+
$fields = [];
50+
$targetEntity = $self->entityTypeContainer->get($id);
51+
52+
if (! count($requiredFields) && ! count($optionalFields)) {
53+
$self->addAllFieldsAsRequired($targetEntity, $fields);
54+
} else {
55+
$self->addRequiredFields($targetEntity, $requiredFields, $fields);
56+
$self->addOptionalFields($targetEntity, $optionalFields, $fields);
57+
}
58+
59+
$object->__construct([
60+
'name' => $targetEntity->getTypeName() . '_Input_' . uniqid(),
61+
'description' => $targetEntity->getDescription(),
62+
'fields' => static fn () => $fields,
63+
]);
64+
});
5965
}
6066

6167
/**

src/Type/Entity/Entity.php

Lines changed: 8 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66

77
use ApiSkeletons\Doctrine\ORM\GraphQL\Config;
88
use ApiSkeletons\Doctrine\ORM\GraphQL\Container;
9-
use ApiSkeletons\Doctrine\ORM\GraphQL\Driver;
109
use ApiSkeletons\Doctrine\ORM\GraphQL\Event\EntityDefinition;
1110
use ApiSkeletons\Doctrine\ORM\GraphQL\Filter\FilterFactory;
1211
use ApiSkeletons\Doctrine\ORM\GraphQL\Hydrator\HydratorContainer;
@@ -20,14 +19,13 @@
2019
use Doctrine\ORM\Mapping\ClassMetadata;
2120
use Doctrine\ORM\Mapping\MappingException;
2221
use Exception;
23-
use GraphQL\Error\Error;
2422
use GraphQL\Type\Definition\ObjectType;
2523
use Laminas\Hydrator\HydratorInterface;
2624
use League\Event\EventDispatcher;
25+
use ReflectionClass;
2726

2827
use function array_keys;
2928
use function array_merge;
30-
use function assert;
3129
use function count;
3230
use function in_array;
3331
use function ksort;
@@ -60,8 +58,6 @@ public function __construct(
6058
string $typeName,
6159
private string|null $eventName = null,
6260
) {
63-
assert($container instanceof Driver);
64-
6561
$this->config = $container->get(Config::class);
6662
$this->entityManager = $container->get(EntityManager::class);
6763
$this->entityTypeContainer = $container->get(EntityTypeContainer::class);
@@ -71,14 +67,7 @@ public function __construct(
7167
$this->hydratorContainer = $container->get(HydratorContainer::class);
7268
$this->resolveCollectionFactory = $container->get(ResolveCollectionFactory::class);
7369
$this->typeContainer = $container->get(TypeContainer::class);
74-
75-
if (! isset($container->get('metadata')[$typeName])) {
76-
throw new Error(
77-
'Entity ' . $typeName . ' is not mapped in the GraphQL metadata',
78-
);
79-
}
80-
81-
$this->metadata = $container->get('metadata')[$typeName];
70+
$this->metadata = $container->get('metadata')[$typeName];
8271
}
8372

8473
public function getHydrator(): HydratorInterface
@@ -184,7 +173,12 @@ public function getObjectType(): ObjectType
184173
}
185174

186175
/** @psalm-suppress InvalidArgument */
187-
$this->objectType = new ObjectType($arrayObject->getArrayCopy());
176+
$this->objectType = (new ReflectionClass(ObjectType::class))
177+
->newLazyGhost(static function (ObjectType $object) use ($arrayObject): void {
178+
$object->__construct(
179+
$arrayObject->getArrayCopy(),
180+
);
181+
});
188182

189183
return $this->objectType;
190184
}

src/Type/Entity/EntityTypeContainer.php

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,10 @@
55
namespace ApiSkeletons\Doctrine\ORM\GraphQL\Type\Entity;
66

77
use ApiSkeletons\Doctrine\ORM\GraphQL\Container;
8-
use ApiSkeletons\Doctrine\ORM\GraphQL\Driver;
8+
use GraphQL\Error\Error;
99
use Override;
10+
use ReflectionClass;
1011

11-
use function assert;
1212
use function strtolower;
1313

1414
/**
@@ -20,7 +20,6 @@ class EntityTypeContainer extends Container
2020
public function __construct(
2121
protected readonly Container $container,
2222
) {
23-
assert($container instanceof Driver);
2423
}
2524

2625
/**
@@ -45,7 +44,26 @@ public function get(string $id, string|null $eventName = null): mixed
4544
return $this->register[$key];
4645
}
4746

48-
$this->set($key, new Entity($this->container, $id, $eventName));
47+
if (! $this->has($id)) {
48+
throw new Error(
49+
'Entity ' . $id . ' is not mapped in the GraphQL metadata',
50+
);
51+
}
52+
53+
$container = $this->container;
54+
55+
// Use a Lazy Ghost object
56+
$this->set(
57+
$key,
58+
(new ReflectionClass(Entity::class))
59+
->newLazyGhost(static function (Entity $object) use ($container, $id, $eventName): void {
60+
$object->__construct(
61+
$container,
62+
$id,
63+
$eventName,
64+
);
65+
}),
66+
);
4967

5068
return $this->get($id, $eventName);
5169
}

0 commit comments

Comments
 (0)