Passed
Push — main ( 5c74ff...777132 )
by Tom
59s queued 15s
created

MetadataFactory::buildMetadataForAssociations()   B

Complexity

Conditions 7
Paths 6

Size

Total Lines 51
Code Lines 31

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 7
eloc 31
c 0
b 0
f 0
nc 6
nop 2
dl 0
loc 51
rs 8.4906

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
declare(strict_types=1);
4
5
namespace ApiSkeletons\Doctrine\GraphQL\Metadata;
6
7
use ApiSkeletons\Doctrine\GraphQL\Attribute;
8
use ApiSkeletons\Doctrine\GraphQL\Config;
9
use ApiSkeletons\Doctrine\GraphQL\Hydrator\Strategy;
10
use Doctrine\ORM\EntityManager;
11
use Doctrine\ORM\Mapping\ClassMetadata;
12
use Doctrine\ORM\Mapping\ClassMetadataInfo;
13
use ReflectionClass;
14
15
use function assert;
16
17
class MetadataFactory extends AbstractMetadataFactory
18
{
19
    /** @param mixed[] $metadata */
20
    public function __construct(
21
        protected array $metadata,
22
        protected EntityManager $entityManager,
23
        protected Config $config,
24
        protected GlobalEnable $globalEnable,
25
    ) {
26
    }
27
28
    /** @return mixed[]|null */
29
    public function __invoke(): array|null
30
    {
31
        if ($this->metadata) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->metadata of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
32
            return $this->metadata;
33
        }
34
35
        $entityClasses = [];
36
        foreach ($this->entityManager->getMetadataFactory()->getAllMetadata() as $metadata) {
37
            $entityClasses[] = $metadata->getName();
38
        }
39
40
        if ($this->config->getGlobalEnable()) {
41
            $this->metadata = ($this->globalEnable)($entityClasses);
42
43
            return $this->metadata;
44
        }
45
46
        foreach ($entityClasses as $entityClass) {
47
            $reflectionClass     = new ReflectionClass($entityClass);
48
            $entityClassMetadata = $this->entityManager
49
                ->getMetadataFactory()->getMetadataFor($reflectionClass->getName());
50
51
            $this->buildMetadataForEntity($reflectionClass);
52
            $this->buildMetadataForFields($entityClassMetadata, $reflectionClass);
53
            $this->buildMetadataForAssociations($entityClassMetadata, $reflectionClass);
54
        }
55
56
        return $this->metadata;
57
    }
58
59
    /**
60
     * Using the entity class attributes, generate the metadata.
61
     * The buildmetadata* functions exist to simplify the buildMetadata
62
     * function.
63
     */
64
    private function buildMetadataForEntity(ReflectionClass $reflectionClass): void
65
    {
66
        $entityInstance = null;
67
68
        // Fetch attributes for the entity class filterd by Attribute\Entity
69
        foreach ($reflectionClass->getAttributes(Attribute\Entity::class) as $attribute) {
70
            $instance = $attribute->newInstance();
71
72
            // Only process attributes for the Config group
73
            if ($instance->getGroup() !== $this->config->getGroup()) {
74
                continue;
75
            }
76
77
            // Only one matching instance per group is allowed
78
            assert(
79
                ! $entityInstance,
80
                'Duplicate attribute found for entity '
81
                . $reflectionClass->getName() . ', group ' . $instance->getGroup(),
82
            );
83
            $entityInstance = $instance;
84
85
            // Save entity-level metadata
86
            $this->metadata[$reflectionClass->getName()] = [
87
                'entityClass' => $reflectionClass->getName(),
88
                'byValue' => $this->config->getGlobalByValue() ?? $instance->getByValue(),
89
                'namingStrategy' => $instance->getNamingStrategy(),
90
                'fields' => [],
91
                'filters' => $instance->getFilters(),
92
                'excludeCriteria' => $instance->getExcludeCriteria(),
93
                'description' => $instance->getDescription(),
94
                'typeName' => $instance->getTypeName()
95
                    ? $this->appendGroupSuffix($instance->getTypeName()) :
96
                    $this->getTypeName($reflectionClass->getName()),
97
            ];
98
        }
99
    }
100
101
    private function buildMetadataForFields(
102
        ClassMetadata $entityClassMetadata,
103
        ReflectionClass $reflectionClass,
104
    ): void {
105
        foreach ($entityClassMetadata->getFieldNames() as $fieldName) {
106
            $fieldInstance   = null;
107
            $reflectionField = $reflectionClass->getProperty($fieldName);
108
109
            foreach ($reflectionField->getAttributes(Attribute\Field::class) as $attribute) {
110
                $instance = $attribute->newInstance();
111
112
                // Only process attributes for the same group
113
                if ($instance->getGroup() !== $this->config->getGroup()) {
114
                    continue;
115
                }
116
117
                // Only one matching instance per group is allowed
118
                assert(
119
                    ! $fieldInstance,
120
                    'Duplicate attribute found for field '
121
                    . $fieldName . ', group ' . $instance->getGroup(),
122
                );
123
                $fieldInstance = $instance;
124
125
                $this->metadata[$reflectionClass->getName()]['fields'][$fieldName]['description'] =
126
                    $instance->getDescription();
127
128
                $this->metadata[$reflectionClass->getName()]['fields'][$fieldName]['type'] =
129
                    $instance->getType() ?? $entityClassMetadata->getTypeOfField($fieldName);
130
131
                if ($instance->getStrategy()) {
132
                    $this->metadata[$reflectionClass->getName()]['fields'][$fieldName]['strategy'] =
133
                        $instance->getStrategy();
134
135
                    continue;
136
                }
137
138
                $this->metadata[$reflectionClass->getName()]['fields'][$fieldName]['excludeCriteria'] =
139
                    $instance->getExcludeCriteria();
140
141
                // Set default strategy based on field type
142
                $this->metadata[$reflectionClass->getName()]['fields'][$fieldName]['strategy'] =
143
                    $this->getDefaultStrategy($entityClassMetadata->getTypeOfField($fieldName));
144
            }
145
        }
146
    }
147
148
    private function buildMetadataForAssociations(
149
        ClassMetadata $entityClassMetadata,
150
        ReflectionClass $reflectionClass,
151
    ): void {
152
        // Fetch attributes for associations
153
        $associationNames = $this->entityManager->getMetadataFactory()
154
            ->getMetadataFor($reflectionClass->getName())->getAssociationNames();
155
156
        foreach ($associationNames as $associationName) {
157
            $associationInstance   = null;
158
            $reflectionAssociation = $reflectionClass->getProperty($associationName);
159
160
            foreach ($reflectionAssociation->getAttributes(Attribute\Association::class) as $attribute) {
161
                $instance = $attribute->newInstance();
162
163
                // Only process attributes for the same group
164
                if ($instance->getGroup() !== $this->config->getGroup()) {
165
                    continue;
166
                }
167
168
                // Only one matching instance per group is allowed
169
                assert(
170
                    ! $associationInstance,
171
                    'Duplicate attribute found for association '
172
                    . $associationName . ', group ' . $instance->getGroup(),
173
                );
174
                $associationInstance = $instance;
175
176
                $this->metadata[$reflectionClass->getName()]['fields'][$associationName]['description']             =
177
                    $instance->getDescription();
178
                $this->metadata[$reflectionClass->getName()]['fields'][$associationName]['excludeCriteria']         =
179
                    $instance->getExcludeCriteria();
180
                $this->metadata[$reflectionClass->getName()]['fields'][$associationName]['filterCriteriaEventName'] =
181
                    $instance->getFilterCriteriaEventName();
182
183
                if ($instance->getStrategy()) {
184
                    $this->metadata[$reflectionClass->getName()]['fields'][$associationName]['strategy']
185
                        = $instance->getStrategy();
186
187
                    continue;
188
                }
189
190
                $mapping = $entityClassMetadata->getAssociationMapping($associationName);
191
192
                // See comment on NullifyOwningAssociation for details of why this is done
193
                if ($mapping['type'] === ClassMetadataInfo::MANY_TO_MANY && $mapping['isOwningSide']) {
194
                    $this->metadata[$reflectionClass->getName()]['fields'][$associationName]['strategy'] =
195
                        Strategy\NullifyOwningAssociation::class;
196
                } else {
197
                    $this->metadata[$reflectionClass->getName()]['fields'][$associationName]['strategy'] =
198
                        Strategy\AssociationDefault::class;
199
                }
200
            }
201
        }
202
    }
203
}
204