Completed
Push — master ( 2975e7...46c3ee )
by Rafael
07:49
created

DoctrineORMFilterResolver   A

Complexity

Total Complexity 34

Size/Duplication

Total Lines 158
Duplicated Lines 0 %

Test Coverage

Coverage 90.11%

Importance

Changes 0
Metric Value
wmc 34
dl 0
loc 158
ccs 82
cts 91
cp 0.9011
rs 9.68
c 0
b 0
f 0

3 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 3 1
A createEnumFilter() 0 26 3
F resolve() 0 101 30
1
<?php
2
/*******************************************************************************
3
 *  This file is part of the GraphQL Bundle package.
4
 *
5
 *  (c) YnloUltratech <[email protected]>
6
 *
7
 *  For the full copyright and license information, please view the LICENSE
8
 *  file that was distributed with this source code.
9
 ******************************************************************************/
10
11
namespace Ynlo\GraphQLBundle\Filter\Resolver;
12
13
use Doctrine\Common\Persistence\Mapping\MappingException;
14
use Doctrine\Common\Persistence\ObjectManager;
15
use GraphQL\Type\Definition\EnumType;
16
use Ynlo\GraphQLBundle\Annotation\Filter;
17
use Ynlo\GraphQLBundle\Definition\ClassAwareDefinitionInterface;
18
use Ynlo\GraphQLBundle\Definition\EnumDefinition;
19
use Ynlo\GraphQLBundle\Definition\ExecutableDefinitionInterface;
20
use Ynlo\GraphQLBundle\Definition\FieldDefinition;
21
use Ynlo\GraphQLBundle\Definition\InputObjectDefinition;
22
use Ynlo\GraphQLBundle\Definition\ObjectDefinitionInterface;
23
use Ynlo\GraphQLBundle\Definition\Registry\Endpoint;
24
use Ynlo\GraphQLBundle\Filter\Common\ArrayFilter;
25
use Ynlo\GraphQLBundle\Filter\Common\BooleanFilter;
26
use Ynlo\GraphQLBundle\Filter\Common\DateFilter;
27
use Ynlo\GraphQLBundle\Filter\Common\EnumFilter;
28
use Ynlo\GraphQLBundle\Filter\Common\NodeFilter;
29
use Ynlo\GraphQLBundle\Filter\Common\NumberFilter;
30
use Ynlo\GraphQLBundle\Filter\Common\StringFilter;
31
use Ynlo\GraphQLBundle\Filter\FilterResolverInterface;
32
use Ynlo\GraphQLBundle\Model\Filter\ArrayComparisonExpression;
33
use Ynlo\GraphQLBundle\Model\Filter\DateComparisonExpression;
34
use Ynlo\GraphQLBundle\Model\Filter\DateTimeComparisonExpression;
35
use Ynlo\GraphQLBundle\Model\Filter\EnumComparisonExpression;
36
use Ynlo\GraphQLBundle\Model\Filter\FloatComparisonExpression;
37
use Ynlo\GraphQLBundle\Model\Filter\IntegerComparisonExpression;
38
use Ynlo\GraphQLBundle\Model\Filter\NodeComparisonExpression;
39
use Ynlo\GraphQLBundle\Model\Filter\StringComparisonExpression;
40
use Ynlo\GraphQLBundle\Type\Registry\TypeRegistry;
41
use Ynlo\GraphQLBundle\Type\Types;
42
use Ynlo\GraphQLBundle\Util\TypeUtil;
43
44
/**
45
 * Resolve filters automatically based on GraphQL field definitions directly related to Entity columns
46
 */
47
class DoctrineORMFilterResolver implements FilterResolverInterface
48
{
49
    /**
50
     * @var ObjectManager
51
     */
52
    private $manager;
53
54
    /**
55
     * DoctrineORMFilterResolver constructor.
56
     *
57
     * @param ObjectManager $manager
58
     */
59 1
    public function __construct(ObjectManager $manager)
60
    {
61 1
        $this->manager = $manager;
62 1
    }
63
64
    /**
65
     * @inheritDoc
66
     */
67 1
    public function resolve(ExecutableDefinitionInterface $executableDefinition,ObjectDefinitionInterface $node, Endpoint $endpoint): array
68
    {
69 1
        $class = $node->getClass();
70 1
        $filters = [];
71
        try {
72 1
            if (!$class || !($metaData = $this->manager->getClassMetadata($class))) {
73
                return $filters;
74
            }
75
76 1
            foreach ($node->getFields() as $field) {
77 1
                if (!$metaData->hasField($field->getName())
78 1
                    && !$metaData->hasAssociation($field->getName())
79 1
                    && !$metaData->hasField($field->getOriginName())
80 1
                    && !$metaData->hasAssociation($field->getOriginName())) {
81 1
                    continue;
82
                }
83
84
                //ignore ID
85 1
                if ($field->getName() === 'id') {
86 1
                    continue;
87
                }
88
89 1
                $filter = new Filter();
90 1
                $filter->name = $filter->field = $field->getName();
91
92
                //simple fields
93 1
                switch (TypeUtil::normalize($field->getType())) {
94 1
                    case Types::STRING:
95 1
                        $filter->resolver = StringFilter::class;
96 1
                        $filter->type = StringComparisonExpression::class;
97 1
                        if ($field->isList()) {
98 1
                            $filter->resolver = ArrayFilter::class;
99 1
                            $filter->type = ArrayComparisonExpression::class;
100
                        }
101 1
                        break;
102 1
                    case Types::BOOLEAN:
103 1
                        $filter->resolver = BooleanFilter::class;
104 1
                        $filter->type = Types::BOOLEAN;
105 1
                        break;
106 1
                    case Types::DATE:
107
                        $filter->resolver = DateFilter::class;
108
                        $filter->type = DateComparisonExpression::class;
109
                        break;
110 1
                    case Types::DATETIME:
111 1
                        $filter->resolver = DateFilter::class;
112 1
                        $filter->type = DateTimeComparisonExpression::class;
113 1
                        break;
114 1
                    case Types::INT:
115 1
                        $filter->resolver = NumberFilter::class;
116 1
                        $filter->type = IntegerComparisonExpression::class;
117 1
                        break;
118 1
                    case Types::FLOAT:
119 1
                        $filter->resolver = NumberFilter::class;
120 1
                        $filter->type = FloatComparisonExpression::class;
121 1
                        break;
122
                }
123
124
                //relations and enums
125 1
                if (!$filter->resolver && $endpoint->hasType($field->getType())) {
126 1
                    $relatedNode = $endpoint->getType($field->getType());
127
128
                    //enum registered using enum definition
129 1
                    if ($relatedNode instanceof EnumDefinition) {
130 1
                        $enumName = $relatedNode->getName();
131 1
                        foreach ($relatedNode->getValues() as $value) {
132 1
                            $enumValues[] = $value->getName();
133
                        }
134 1
                        $filter = $this->createEnumFilter($endpoint, $field, $enumName, $enumValues);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $enumValues does not seem to be defined for all execution paths leading up to this point.
Loading history...
135 1
                        $filter->field = $field->getName();
136 1
                    } elseif ($relatedNode instanceof ClassAwareDefinitionInterface) {
137 1
                        $relatedEntity = $relatedNode->getClass();
138
                        try {
139 1
                            if ($this->manager->getClassMetadata($relatedEntity)) {
140 1
                                $filter->resolver = NodeFilter::class;
141 1
                                $filter->type = NodeComparisonExpression::class;
142
                            }
143
                        } catch (MappingException $exception) {
144
                            //ignore
145
                        }
146
                    }
147
                }
148
149
                //enum registered as internal GraphQL type
150 1
                if (!$filter->resolver && TypeRegistry::has($field->getType())
151 1
                    && ($enumType = TypeRegistry::get($field->getType()))
152 1
                    && $enumType instanceof EnumType) {
153 1
                    $enumName = $enumType->name;
154 1
                    foreach ($enumType->getValues() as $value) {
155 1
                        $enumValues[] = $value->name;
156
                    }
157 1
                    $filter = $this->createEnumFilter($endpoint, $field, $enumName, $enumValues);
158
                }
159
160 1
                if ($filter->resolver) {
161 1
                    $filters[] = $filter;
162
                }
163
            }
164
165 1
            return $filters;
166
        } catch (MappingException $exception) {
167
            return [];
168
        }
169
    }
170
171
    /**
172
     * @param Endpoint        $endpoint
173
     * @param FieldDefinition $field
174
     * @param string          $enumName
175
     * @param array           $enumValues
176
     *
177
     * @return Filter
178
     */
179 1
    private function createEnumFilter(Endpoint $endpoint, FieldDefinition $field, string $enumName, array $enumValues): Filter
180
    {
181 1
        $name = "{$enumName}ComparisonExpression";
182 1
        if ($endpoint->hasType($name)) {
183
            $condition = $endpoint->getType($name);
184
        } else {
185
            /** @var InputObjectDefinition $condition */
186 1
            $condition = unserialize(serialize($endpoint->getType(EnumComparisonExpression::class)), ['allowed_classes' => true]);
187 1
            $condition->setName($name);
188 1
            $condition->getField('values')->setType($enumName)->setList(true);
189 1
            $description = $condition->getDescription();
190 1
            $description = str_replace('\'{VALUE_1}\'', array_values($enumValues)[0], $description);
191 1
            if (isset(array_values($enumValues)[1])) {
192 1
                $description = str_replace('\'{VALUE_2}\'', array_values($enumValues)[1], $description);
193
            } else {
194
                $description = str_replace(', \'{VALUE_2}\'', null, $description);
195
            }
196 1
            $condition->setDescription($description);
197 1
            $endpoint->add($condition);
198
        }
199 1
        $filter = new Filter();
200 1
        $filter->name = $filter->field = $field->getName();
201 1
        $filter->resolver = EnumFilter::class;
202 1
        $filter->type = $condition->getName();
203
204 1
        return $filter;
205
    }
206
}
207