Skip to content

Commit

Permalink
Merge pull request #133 from TomHAnderson/hotfix/code-review
Browse files Browse the repository at this point in the history
Refactor for 8.0
  • Loading branch information
TomHAnderson committed May 18, 2023
2 parents 5c74ffc + 4340097 commit 777132c
Show file tree
Hide file tree
Showing 25 changed files with 257 additions and 282 deletions.
26 changes: 26 additions & 0 deletions src/AbstractContainer.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,10 @@
use Closure;
use GraphQL\Error\Error;
use Psr\Container\ContainerInterface;
use ReflectionClass;
use ReflectionException;

use function assert;
use function strtolower;

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

return $this;
}

/**
* This function allows for buildable types. The Type\Connection type is created this way
* because it relies on the entity object type. To create a custom buildable object type
* it must implement the Buildable interface.
*
* @param mixed[] $params
*
* @throws Error
* @throws ReflectionException
*/
public function build(string $typeClassName, string $typeName, mixed ...$params): mixed
{
if ($this->has($typeName)) {
return $this->get($typeName);
}

assert((new ReflectionClass($typeClassName))->implementsInterface(Buildable::class));

return $this
->set($typeName, new $typeClassName($this, $typeName, $params))
->get($typeName);
}
}
14 changes: 14 additions & 0 deletions src/Buildable.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<?php

declare(strict_types=1);

namespace ApiSkeletons\Doctrine\GraphQL;

/**
* Types that can be built must implement this interface
*/
interface Buildable
{
/** @param mixed[] $params */
public function __construct(AbstractContainer $container, string $typeName, array $params);
}
14 changes: 7 additions & 7 deletions src/Config.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,15 @@
namespace ApiSkeletons\Doctrine\GraphQL;

/**
* This class is used for parameter differentiation when creating the driver
* $partialContext->setLimit(1000);
* This class is used for setting parameters when
* creating the driver
*/
class Config
{
/**
* @var string The GraphQL group. This allows multiple GraphQL
* configurations within the same application or
* even within the same group of entities and Object Manager.
* configurations within the same application or
* even within the same group of entities and Object Manager.
*/
protected string $group = 'default';

Expand Down Expand Up @@ -48,9 +48,9 @@ class Config

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

Expand Down
6 changes: 3 additions & 3 deletions src/Criteria/CriteriaFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ public function get(
}

$fields = [];
$entityMetadata = $targetEntity->getMetadataConfig();
$entityMetadata = $targetEntity->getMetadata();
$allowedFilters = Filters::toArray();

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

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

// Add eq filter for to-one associations
foreach ($classMetadata->getAssociationNames() as $associationName) {
Expand Down
2 changes: 1 addition & 1 deletion src/Criteria/Type/BetweenInputObjectType.php
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ public function __construct(string $typeName, string $fieldName, ScalarType|List
. FiltersDef::BETWEEN
. '_fields',
'description' => 'Between `from` and `to`',
'fields' => static fn () => $fields,
'fields' => static fn () => $fields,
]);
}
}
18 changes: 13 additions & 5 deletions src/Driver.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ class Driver extends AbstractContainer
*/
public function connection(ObjectType $objectType): ObjectType
{
/**
* Connections rely on the entity ObjectType so the build() method is used
*/
return $this->get(Type\TypeManager::class)
->build(Type\Connection::class, $objectType->name . '_Connection', $objectType);
}
Expand All @@ -32,7 +35,7 @@ public function connection(ObjectType $objectType): ObjectType
*/
public function type(string $entityClass): ObjectType
{
return $this->get(Metadata\Metadata::class)->get($entityClass)->getGraphQLType();
return $this->get(Type\TypeManager::class)->build(Type\Entity::class, $entityClass)();
}

/**
Expand All @@ -42,8 +45,10 @@ public function type(string $entityClass): ObjectType
*/
public function filter(string $entityClass): object
{
return $this->get(Criteria\CriteriaFactory::class)
->get($this->get(Metadata\Metadata::class)->get($entityClass));
return $this->get(Criteria\CriteriaFactory::class)->get(
$this->get(Type\TypeManager::class)
->build(Type\Entity::class, $entityClass),
);
}

/**
Expand All @@ -63,8 +68,11 @@ public function pagination(): object
*/
public function resolve(string $entityClass, string $eventName = 'filter.querybuilder'): Closure
{
return $this->get(Resolve\ResolveEntityFactory::class)
->get($this->get(Metadata\Metadata::class)->get($entityClass), $eventName);
return $this->get(Resolve\ResolveEntityFactory::class)->get(
$this->get(Type\TypeManager::class)
->build(Type\Entity::class, $entityClass),
$eventName,
);
}

/**
Expand Down
6 changes: 4 additions & 2 deletions src/Event/EntityDefinition.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,10 @@ class EntityDefinition implements
HasEventName
{
/** @param string[] $entityAliasMap */
public function __construct(protected ArrayObject $definition, protected string $eventName)
{
public function __construct(
protected ArrayObject $definition,
protected string $eventName,
) {
}

public function eventName(): string
Expand Down
37 changes: 15 additions & 22 deletions src/Hydrator/HydratorFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,12 @@

use ApiSkeletons\Doctrine\GraphQL\AbstractContainer;
use ApiSkeletons\Doctrine\GraphQL\Hydrator\Filter\Password;
use ApiSkeletons\Doctrine\GraphQL\Hydrator\Strategy\AssociationDefault;
use ApiSkeletons\Doctrine\GraphQL\Hydrator\Strategy\FieldDefault;
use ApiSkeletons\Doctrine\GraphQL\Hydrator\Strategy\NullifyOwningAssociation;
use ApiSkeletons\Doctrine\GraphQL\Hydrator\Strategy\ToBoolean;
use ApiSkeletons\Doctrine\GraphQL\Hydrator\Strategy\ToFloat;
use ApiSkeletons\Doctrine\GraphQL\Hydrator\Strategy\ToInteger;
use ApiSkeletons\Doctrine\GraphQL\Metadata\Metadata;
use ApiSkeletons\Doctrine\GraphQL\Type\Entity;
use ApiSkeletons\Doctrine\GraphQL\Type\TypeManager;
use Doctrine\Laminas\Hydrator\DoctrineObject;
use Doctrine\ORM\EntityManager;
use GraphQL\Error\Error;
use Laminas\Hydrator\Filter\FilterComposite;
use Laminas\Hydrator\Filter\FilterEnabledInterface;
use Laminas\Hydrator\Filter\FilterInterface;
use Laminas\Hydrator\Filter;
use Laminas\Hydrator\NamingStrategy\NamingStrategyEnabledInterface;
use Laminas\Hydrator\NamingStrategy\NamingStrategyInterface;
use Laminas\Hydrator\Strategy\StrategyEnabledInterface;
Expand All @@ -34,16 +27,16 @@
*/
class HydratorFactory extends AbstractContainer
{
public function __construct(protected EntityManager $entityManager, protected Metadata $metadata)
public function __construct(protected EntityManager $entityManager, protected TypeManager $typeManager)
{
// Register project defaults
$this
->set(AssociationDefault::class, new AssociationDefault())
->set(FieldDefault::class, new FieldDefault())
->set(NullifyOwningAssociation::class, new NullifyOwningAssociation())
->set(ToBoolean::class, new ToBoolean())
->set(ToFloat::class, new ToFloat())
->set(ToInteger::class, new ToInteger())
->set(Strategy\AssociationDefault::class, new Strategy\AssociationDefault())
->set(Strategy\FieldDefault::class, new Strategy\FieldDefault())
->set(Strategy\NullifyOwningAssociation::class, new Strategy\NullifyOwningAssociation())
->set(Strategy\ToBoolean::class, new Strategy\ToBoolean())
->set(Strategy\ToFloat::class, new Strategy\ToFloat())
->set(Strategy\ToInteger::class, new Strategy\ToInteger())
->set(Password::class, new Password());
}

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

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

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

// Create filters and assign to hydrator
if ($hydrator instanceof FilterEnabledInterface) {
if ($hydrator instanceof Filter\FilterEnabledInterface) {
foreach ($config['filters'] as $name => $filterConfig) {
// Default filters to AND
$condition = $filterConfig['condition'] ?? FilterComposite::CONDITION_AND;
$condition = $filterConfig['condition'] ?? Filter\FilterComposite::CONDITION_AND;
$filterClass = $filterConfig['filter'];
assert(
in_array(FilterInterface::class, class_implements($filterClass)),
in_array(Filter\FilterInterface::class, class_implements($filterClass)),
'Filter must implement ' . StrategyInterface::class,
);

Expand Down
17 changes: 8 additions & 9 deletions src/Input/InputFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

use ApiSkeletons\Doctrine\GraphQL\AbstractContainer;
use ApiSkeletons\Doctrine\GraphQL\Config;
use ApiSkeletons\Doctrine\GraphQL\Metadata\Metadata;
use ApiSkeletons\Doctrine\GraphQL\Type\Entity;
use ApiSkeletons\Doctrine\GraphQL\Type\TypeManager;
use Doctrine\ORM\EntityManager;
use Exception;
Expand All @@ -25,7 +25,6 @@ public function __construct(
protected Config $config,
protected EntityManager $entityManager,
protected TypeManager $typeManager,
protected Metadata $metadata,
) {
}

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

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

$fields[$fieldName] = new InputObjectField([
'name' => $fieldName,
'description' => (string) $targetEntity->getMetadataConfig()['fields'][$fieldName]['description'],
'type' => $this->typeManager->get($targetEntity->getMetadataConfig()['fields'][$fieldName]['type']),
'description' => (string) $targetEntity->getMetadata()['fields'][$fieldName]['description'],
'type' => $this->typeManager->get($targetEntity->getMetadata()['fields'][$fieldName]['type']),
]);
}
}
Expand Down Expand Up @@ -119,9 +118,9 @@ protected function addRequiredFields(

$fields[$fieldName] = new InputObjectField([
'name' => $fieldName,
'description' => (string) $targetEntity->getMetadataConfig()['fields'][$fieldName]['description'],
'description' => (string) $targetEntity->getMetadata()['fields'][$fieldName]['description'],
'type' => Type::nonNull($this->typeManager->get(
$targetEntity->getMetadataConfig()['fields'][$fieldName]['type'],
$targetEntity->getMetadata()['fields'][$fieldName]['type'],
)),
]);
}
Expand All @@ -142,8 +141,8 @@ protected function addAllFieldsAsRequired(mixed $targetEntity, array &$fields):

$fields[$fieldName] = new InputObjectField([
'name' => $fieldName,
'description' => (string) $targetEntity->getMetadataConfig()['fields'][$fieldName]['description'],
'type' => Type::nonNull($this->typeManager->get($targetEntity->getMetadataConfig()['fields'][$fieldName]['type'])),
'description' => (string) $targetEntity->getMetadata()['fields'][$fieldName]['description'],
'type' => Type::nonNull($this->typeManager->get($targetEntity->getMetadata()['fields'][$fieldName]['type'])),
]);
}
}
Expand Down
10 changes: 5 additions & 5 deletions src/Metadata/GlobalEnable.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
final class GlobalEnable extends AbstractMetadataFactory
{
/** @var mixed[] */
private array $metadataConfig = [];
private array $metadata = [];

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

// Save entity-level metadata
$this->metadataConfig[$entityClass] = [
$this->metadata[$entityClass] = [
'entityClass' => $entityClass,
'byValue' => $byValue,
'namingStrategy' => null,
Expand All @@ -48,7 +48,7 @@ public function __invoke(array $entityClasses): array
$this->buildAssociationMetadata($entityClass);
}

return $this->metadataConfig;
return $this->metadata;
}

private function buildFieldMetadata(string $entityClass): void
Expand All @@ -60,7 +60,7 @@ private function buildFieldMetadata(string $entityClass): void
continue;
}

$this->metadataConfig[$entityClass]['fields'][$fieldName] = [
$this->metadata[$entityClass]['fields'][$fieldName] = [
'description' => $fieldName,
'type' => $entityClassMetadata->getTypeOfField($fieldName),
'strategy' => $this->getDefaultStrategy($entityClassMetadata->getTypeOfField($fieldName)),
Expand All @@ -78,7 +78,7 @@ private function buildAssociationMetadata(string $entityClass): void
continue;
}

$this->metadataConfig[$entityClass]['fields'][$associationName] = [
$this->metadata[$entityClass]['fields'][$associationName] = [
'excludeCriteria' => [],
'description' => $associationName,
'filterCriteriaEventName' => null,
Expand Down
Loading

0 comments on commit 777132c

Please sign in to comment.