Skip to content

Commit 777132c

Browse files
authored
Merge pull request #133 from TomHAnderson/hotfix/code-review
Refactor for 8.0
2 parents 5c74ffc + 4340097 commit 777132c

25 files changed

Lines changed: 257 additions & 282 deletions

src/AbstractContainer.php

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,10 @@
77
use Closure;
88
use GraphQL\Error\Error;
99
use Psr\Container\ContainerInterface;
10+
use ReflectionClass;
11+
use ReflectionException;
1012

13+
use function assert;
1114
use function strtolower;
1215

1316
abstract class AbstractContainer implements ContainerInterface
@@ -49,4 +52,27 @@ public function set(string $id, mixed $value): self
4952

5053
return $this;
5154
}
55+
56+
/**
57+
* This function allows for buildable types. The Type\Connection type is created this way
58+
* because it relies on the entity object type. To create a custom buildable object type
59+
* it must implement the Buildable interface.
60+
*
61+
* @param mixed[] $params
62+
*
63+
* @throws Error
64+
* @throws ReflectionException
65+
*/
66+
public function build(string $typeClassName, string $typeName, mixed ...$params): mixed
67+
{
68+
if ($this->has($typeName)) {
69+
return $this->get($typeName);
70+
}
71+
72+
assert((new ReflectionClass($typeClassName))->implementsInterface(Buildable::class));
73+
74+
return $this
75+
->set($typeName, new $typeClassName($this, $typeName, $params))
76+
->get($typeName);
77+
}
5278
}

src/Buildable.php

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace ApiSkeletons\Doctrine\GraphQL;
6+
7+
/**
8+
* Types that can be built must implement this interface
9+
*/
10+
interface Buildable
11+
{
12+
/** @param mixed[] $params */
13+
public function __construct(AbstractContainer $container, string $typeName, array $params);
14+
}

src/Config.php

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,15 @@
55
namespace ApiSkeletons\Doctrine\GraphQL;
66

77
/**
8-
* This class is used for parameter differentiation when creating the driver
9-
* $partialContext->setLimit(1000);
8+
* This class is used for setting parameters when
9+
* creating the driver
1010
*/
1111
class Config
1212
{
1313
/**
1414
* @var string The GraphQL group. This allows multiple GraphQL
15-
* configurations within the same application or
16-
* even within the same group of entities and Object Manager.
15+
* configurations within the same application or
16+
* even within the same group of entities and Object Manager.
1717
*/
1818
protected string $group = 'default';
1919

@@ -48,9 +48,9 @@ class Config
4848

4949
/**
5050
* @var bool|null When set to true, all entities will be extracted by value
51-
* across all hydrators in the driver. When set to false,
52-
* all hydrators will extract by reference. This overrides
53-
* per-entity attribute configuration.
51+
* across all hydrators in the driver. When set to false,
52+
* all hydrators will extract by reference. This overrides
53+
* per-entity attribute configuration.
5454
*/
5555
protected bool|null $globalByValue = null;
5656

src/Criteria/CriteriaFactory.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ public function get(
4545
}
4646

4747
$fields = [];
48-
$entityMetadata = $targetEntity->getMetadataConfig();
48+
$entityMetadata = $targetEntity->getMetadata();
4949
$allowedFilters = Filters::toArray();
5050

5151
// Limit entity filters
@@ -84,7 +84,7 @@ public function get(
8484
protected function addFields(Entity $targetEntity, string $typeName, array $allowedFilters, array &$fields): void
8585
{
8686
$classMetadata = $this->entityManager->getClassMetadata($targetEntity->getEntityClass());
87-
$entityMetadata = $targetEntity->getMetadataConfig();
87+
$entityMetadata = $targetEntity->getMetadata();
8888

8989
foreach ($classMetadata->getFieldNames() as $fieldName) {
9090
// Only process fields that are in the graphql metadata
@@ -128,7 +128,7 @@ static function ($value) use ($fieldExcludeCriteria) {
128128
protected function addAssociations(Entity $targetEntity, string $typeName, array $allowedFilters, array &$fields): void
129129
{
130130
$classMetadata = $this->entityManager->getClassMetadata($targetEntity->getEntityClass());
131-
$entityMetadata = $targetEntity->getMetadataConfig();
131+
$entityMetadata = $targetEntity->getMetadata();
132132

133133
// Add eq filter for to-one associations
134134
foreach ($classMetadata->getAssociationNames() as $associationName) {

src/Criteria/Type/BetweenInputObjectType.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ public function __construct(string $typeName, string $fieldName, ScalarType|List
3434
. FiltersDef::BETWEEN
3535
. '_fields',
3636
'description' => 'Between `from` and `to`',
37-
'fields' => static fn () => $fields,
37+
'fields' => static fn () => $fields,
3838
]);
3939
}
4040
}

src/Driver.php

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@ class Driver extends AbstractContainer
2121
*/
2222
public function connection(ObjectType $objectType): ObjectType
2323
{
24+
/**
25+
* Connections rely on the entity ObjectType so the build() method is used
26+
*/
2427
return $this->get(Type\TypeManager::class)
2528
->build(Type\Connection::class, $objectType->name . '_Connection', $objectType);
2629
}
@@ -32,7 +35,7 @@ public function connection(ObjectType $objectType): ObjectType
3235
*/
3336
public function type(string $entityClass): ObjectType
3437
{
35-
return $this->get(Metadata\Metadata::class)->get($entityClass)->getGraphQLType();
38+
return $this->get(Type\TypeManager::class)->build(Type\Entity::class, $entityClass)();
3639
}
3740

3841
/**
@@ -42,8 +45,10 @@ public function type(string $entityClass): ObjectType
4245
*/
4346
public function filter(string $entityClass): object
4447
{
45-
return $this->get(Criteria\CriteriaFactory::class)
46-
->get($this->get(Metadata\Metadata::class)->get($entityClass));
48+
return $this->get(Criteria\CriteriaFactory::class)->get(
49+
$this->get(Type\TypeManager::class)
50+
->build(Type\Entity::class, $entityClass),
51+
);
4752
}
4853

4954
/**
@@ -63,8 +68,11 @@ public function pagination(): object
6368
*/
6469
public function resolve(string $entityClass, string $eventName = 'filter.querybuilder'): Closure
6570
{
66-
return $this->get(Resolve\ResolveEntityFactory::class)
67-
->get($this->get(Metadata\Metadata::class)->get($entityClass), $eventName);
71+
return $this->get(Resolve\ResolveEntityFactory::class)->get(
72+
$this->get(Type\TypeManager::class)
73+
->build(Type\Entity::class, $entityClass),
74+
$eventName,
75+
);
6876
}
6977

7078
/**

src/Event/EntityDefinition.php

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,10 @@ class EntityDefinition implements
1111
HasEventName
1212
{
1313
/** @param string[] $entityAliasMap */
14-
public function __construct(protected ArrayObject $definition, protected string $eventName)
15-
{
14+
public function __construct(
15+
protected ArrayObject $definition,
16+
protected string $eventName,
17+
) {
1618
}
1719

1820
public function eventName(): string

src/Hydrator/HydratorFactory.php

Lines changed: 15 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -6,19 +6,12 @@
66

77
use ApiSkeletons\Doctrine\GraphQL\AbstractContainer;
88
use ApiSkeletons\Doctrine\GraphQL\Hydrator\Filter\Password;
9-
use ApiSkeletons\Doctrine\GraphQL\Hydrator\Strategy\AssociationDefault;
10-
use ApiSkeletons\Doctrine\GraphQL\Hydrator\Strategy\FieldDefault;
11-
use ApiSkeletons\Doctrine\GraphQL\Hydrator\Strategy\NullifyOwningAssociation;
12-
use ApiSkeletons\Doctrine\GraphQL\Hydrator\Strategy\ToBoolean;
13-
use ApiSkeletons\Doctrine\GraphQL\Hydrator\Strategy\ToFloat;
14-
use ApiSkeletons\Doctrine\GraphQL\Hydrator\Strategy\ToInteger;
15-
use ApiSkeletons\Doctrine\GraphQL\Metadata\Metadata;
9+
use ApiSkeletons\Doctrine\GraphQL\Type\Entity;
10+
use ApiSkeletons\Doctrine\GraphQL\Type\TypeManager;
1611
use Doctrine\Laminas\Hydrator\DoctrineObject;
1712
use Doctrine\ORM\EntityManager;
1813
use GraphQL\Error\Error;
19-
use Laminas\Hydrator\Filter\FilterComposite;
20-
use Laminas\Hydrator\Filter\FilterEnabledInterface;
21-
use Laminas\Hydrator\Filter\FilterInterface;
14+
use Laminas\Hydrator\Filter;
2215
use Laminas\Hydrator\NamingStrategy\NamingStrategyEnabledInterface;
2316
use Laminas\Hydrator\NamingStrategy\NamingStrategyInterface;
2417
use Laminas\Hydrator\Strategy\StrategyEnabledInterface;
@@ -34,16 +27,16 @@
3427
*/
3528
class HydratorFactory extends AbstractContainer
3629
{
37-
public function __construct(protected EntityManager $entityManager, protected Metadata $metadata)
30+
public function __construct(protected EntityManager $entityManager, protected TypeManager $typeManager)
3831
{
3932
// Register project defaults
4033
$this
41-
->set(AssociationDefault::class, new AssociationDefault())
42-
->set(FieldDefault::class, new FieldDefault())
43-
->set(NullifyOwningAssociation::class, new NullifyOwningAssociation())
44-
->set(ToBoolean::class, new ToBoolean())
45-
->set(ToFloat::class, new ToFloat())
46-
->set(ToInteger::class, new ToInteger())
34+
->set(Strategy\AssociationDefault::class, new Strategy\AssociationDefault())
35+
->set(Strategy\FieldDefault::class, new Strategy\FieldDefault())
36+
->set(Strategy\NullifyOwningAssociation::class, new Strategy\NullifyOwningAssociation())
37+
->set(Strategy\ToBoolean::class, new Strategy\ToBoolean())
38+
->set(Strategy\ToFloat::class, new Strategy\ToFloat())
39+
->set(Strategy\ToInteger::class, new Strategy\ToInteger())
4740
->set(Password::class, new Password());
4841
}
4942

@@ -55,8 +48,8 @@ public function get(string $id): mixed
5548
return parent::get($id);
5649
}
5750

58-
$entity = $this->metadata->get($id);
59-
$config = $entity->getMetadataConfig();
51+
$entity = $this->typeManager->build(Entity::class, $id);
52+
$config = $entity->getMetadata();
6053
$hydrator = new DoctrineObject($this->entityManager, $config['byValue']);
6154

6255
// Create field strategy and assign to hydrator
@@ -72,13 +65,13 @@ public function get(string $id): mixed
7265
}
7366

7467
// Create filters and assign to hydrator
75-
if ($hydrator instanceof FilterEnabledInterface) {
68+
if ($hydrator instanceof Filter\FilterEnabledInterface) {
7669
foreach ($config['filters'] as $name => $filterConfig) {
7770
// Default filters to AND
78-
$condition = $filterConfig['condition'] ?? FilterComposite::CONDITION_AND;
71+
$condition = $filterConfig['condition'] ?? Filter\FilterComposite::CONDITION_AND;
7972
$filterClass = $filterConfig['filter'];
8073
assert(
81-
in_array(FilterInterface::class, class_implements($filterClass)),
74+
in_array(Filter\FilterInterface::class, class_implements($filterClass)),
8275
'Filter must implement ' . StrategyInterface::class,
8376
);
8477

src/Input/InputFactory.php

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

77
use ApiSkeletons\Doctrine\GraphQL\AbstractContainer;
88
use ApiSkeletons\Doctrine\GraphQL\Config;
9-
use ApiSkeletons\Doctrine\GraphQL\Metadata\Metadata;
9+
use ApiSkeletons\Doctrine\GraphQL\Type\Entity;
1010
use ApiSkeletons\Doctrine\GraphQL\Type\TypeManager;
1111
use Doctrine\ORM\EntityManager;
1212
use Exception;
@@ -25,7 +25,6 @@ public function __construct(
2525
protected Config $config,
2626
protected EntityManager $entityManager,
2727
protected TypeManager $typeManager,
28-
protected Metadata $metadata,
2928
) {
3029
}
3130

@@ -40,7 +39,7 @@ public function __construct(
4039
public function get(string $id, array $requiredFields = [], array $optionalFields = []): InputObjectType
4140
{
4241
$fields = [];
43-
$targetEntity = $this->metadata->get($id);
42+
$targetEntity = $this->typeManager->build(Entity::class, $id);
4443

4544
if (! count($requiredFields) && ! count($optionalFields)) {
4645
$this->addAllFieldsAsRequired($targetEntity, $fields);
@@ -81,8 +80,8 @@ protected function addOptionalFields(
8180

8281
$fields[$fieldName] = new InputObjectField([
8382
'name' => $fieldName,
84-
'description' => (string) $targetEntity->getMetadataConfig()['fields'][$fieldName]['description'],
85-
'type' => $this->typeManager->get($targetEntity->getMetadataConfig()['fields'][$fieldName]['type']),
83+
'description' => (string) $targetEntity->getMetadata()['fields'][$fieldName]['description'],
84+
'type' => $this->typeManager->get($targetEntity->getMetadata()['fields'][$fieldName]['type']),
8685
]);
8786
}
8887
}
@@ -119,9 +118,9 @@ protected function addRequiredFields(
119118

120119
$fields[$fieldName] = new InputObjectField([
121120
'name' => $fieldName,
122-
'description' => (string) $targetEntity->getMetadataConfig()['fields'][$fieldName]['description'],
121+
'description' => (string) $targetEntity->getMetadata()['fields'][$fieldName]['description'],
123122
'type' => Type::nonNull($this->typeManager->get(
124-
$targetEntity->getMetadataConfig()['fields'][$fieldName]['type'],
123+
$targetEntity->getMetadata()['fields'][$fieldName]['type'],
125124
)),
126125
]);
127126
}
@@ -142,8 +141,8 @@ protected function addAllFieldsAsRequired(mixed $targetEntity, array &$fields):
142141

143142
$fields[$fieldName] = new InputObjectField([
144143
'name' => $fieldName,
145-
'description' => (string) $targetEntity->getMetadataConfig()['fields'][$fieldName]['description'],
146-
'type' => Type::nonNull($this->typeManager->get($targetEntity->getMetadataConfig()['fields'][$fieldName]['type'])),
144+
'description' => (string) $targetEntity->getMetadata()['fields'][$fieldName]['description'],
145+
'type' => Type::nonNull($this->typeManager->get($targetEntity->getMetadata()['fields'][$fieldName]['type'])),
147146
]);
148147
}
149148
}

src/Metadata/GlobalEnable.php

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
final class GlobalEnable extends AbstractMetadataFactory
1414
{
1515
/** @var mixed[] */
16-
private array $metadataConfig = [];
16+
private array $metadata = [];
1717

1818
public function __construct(
1919
private EntityManager $entityManager,
@@ -33,7 +33,7 @@ public function __invoke(array $entityClasses): array
3333
$byValue = $this->config->getGlobalByValue() ?? true;
3434

3535
// Save entity-level metadata
36-
$this->metadataConfig[$entityClass] = [
36+
$this->metadata[$entityClass] = [
3737
'entityClass' => $entityClass,
3838
'byValue' => $byValue,
3939
'namingStrategy' => null,
@@ -48,7 +48,7 @@ public function __invoke(array $entityClasses): array
4848
$this->buildAssociationMetadata($entityClass);
4949
}
5050

51-
return $this->metadataConfig;
51+
return $this->metadata;
5252
}
5353

5454
private function buildFieldMetadata(string $entityClass): void
@@ -60,7 +60,7 @@ private function buildFieldMetadata(string $entityClass): void
6060
continue;
6161
}
6262

63-
$this->metadataConfig[$entityClass]['fields'][$fieldName] = [
63+
$this->metadata[$entityClass]['fields'][$fieldName] = [
6464
'description' => $fieldName,
6565
'type' => $entityClassMetadata->getTypeOfField($fieldName),
6666
'strategy' => $this->getDefaultStrategy($entityClassMetadata->getTypeOfField($fieldName)),
@@ -78,7 +78,7 @@ private function buildAssociationMetadata(string $entityClass): void
7878
continue;
7979
}
8080

81-
$this->metadataConfig[$entityClass]['fields'][$associationName] = [
81+
$this->metadata[$entityClass]['fields'][$associationName] = [
8282
'excludeCriteria' => [],
8383
'description' => $associationName,
8484
'filterCriteriaEventName' => null,

0 commit comments

Comments
 (0)