Passed
Push — master ( c80e12...1c5165 )
by Kirill
04:32
created

Processor   A

Complexity

Total Complexity 15

Size/Duplication

Total Lines 162
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 5

Test Coverage

Coverage 86%

Importance

Changes 0
Metric Value
wmc 15
lcom 1
cbo 5
dl 0
loc 162
ccs 43
cts 50
cp 0.86
rs 10
c 0
b 0
f 0

7 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 8 1
A forEach() 0 8 2
A getBuilder() 0 21 3
A bypass() 0 16 2
A yieldThrough() 0 24 5
A applyField() 0 4 1
A applyFieldWithValue() 0 8 1
1
<?php
2
/**
3
 * This file is part of Hydrogen package.
4
 *
5
 * For the full copyright and license information, please view the LICENSE
6
 * file that was distributed with this source code.
7
 */
8
declare(strict_types=1);
9
10
namespace RDS\Hydrogen\Processor;
11
12
use Doctrine\Common\Persistence\ObjectRepository;
13
use Doctrine\ORM\EntityManagerInterface;
14
use Doctrine\ORM\EntityRepository;
15
use Doctrine\ORM\Mapping\ClassMetadata;
16
use RDS\Hydrogen\Criteria\Common\Field;
17
use RDS\Hydrogen\Criteria\CriterionInterface;
18
use RDS\Hydrogen\Query;
19
20
/**
21
 * Class Processor
22
 */
23
abstract class Processor implements ProcessorInterface
24
{
25
    /**
26
     * @var string[]|BuilderInterface[]
27
     */
28
    protected const CRITERIA_MAPPINGS = [];
29
30
    /**
31
     * @var ObjectRepository|EntityRepository
32
     */
33
    protected $repository;
34
35
    /**
36
     * @var EntityManagerInterface
37
     */
38
    protected $em;
39
40
    /**
41
     * @var ClassMetadata
42
     */
43
    protected $meta;
44
45
    /**
46
     * @var array|BuilderInterface[]
47
     */
48
    private $builderInstances = [];
49
50
    /**
51
     * DatabaseProcessor constructor.
52
     * @param ObjectRepository $repository
53
     * @param EntityManagerInterface $em
54
     */
55 30
    public function __construct(ObjectRepository $repository, EntityManagerInterface $em)
56
    {
57 30
        $this->em = $em;
58 30
        $this->meta = $em->getClassMetadata($repository->getClassName());
59 30
        $this->repository = $repository;
60
61 30
        \assert(\count(static::CRITERIA_MAPPINGS));
62 30
    }
63
64
    /**
65
     * @param mixed $context
66
     * @param Query $query
67
     * @return \Generator
68
     */
69 30
    protected function forEach($context, Query $query): \Generator
70
    {
71 30
        foreach ($query->getCriteria() as $criterion) {
72 28
            $builder = $this->getBuilder($criterion);
73
74 28
            yield from $builder->apply($context, $criterion);
75
        }
76 30
    }
77
78
    /**
79
     * @param CriterionInterface $criterion
80
     * @return BuilderInterface
81
     */
82 28
    protected function getBuilder(CriterionInterface $criterion): BuilderInterface
83
    {
84 28
        $key = \get_class($criterion);
85
86 28
        if (isset($this->builderInstances[$key])) {
87 2
            return $this->builderInstances[$key];
88
        }
89
90 28
        $processor = static::CRITERIA_MAPPINGS[\get_class($criterion)] ?? null;
91
92 28
        if ($processor === null) {
93
            $error = \vsprintf('%s processor does not support the "%s" criterion', [
94
                \str_replace_last('Processor', '', \class_basename($this)),
95
                \class_basename($criterion),
96
            ]);
97
98
            throw new \InvalidArgumentException($error);
99
        }
100
101 28
        return $this->builderInstances[$key] = new $processor($this->meta, $this->em);
102
    }
103
104
    /**
105
     * @param \Generator $generator
106
     * @param Query $query
107
     * @param \Closure $each
108
     * @return \Generator
109
     */
110 30
    protected function bypass(\Generator $generator, Query $query, \Closure $each): \Generator
111
    {
112 30
        $this->builderInstances = [];
113
114 30
        while ($generator->valid()) {
115 24
            [$key, $value] = [$generator->key(), $generator->current()];
0 ignored issues
show
Bug introduced by
The variable $key does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
Bug introduced by
The variable $value does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
116
117 24
            yield from $result = $this->yieldThrough($key, $value, $query, $each);
118
119 24
            $response = $result->getReturn();
120
121 24
            \assert($response !== null);
122
123 24
            $generator->send($response);
124
        }
125 30
    }
126
127
    /**
128
     * @param mixed $key
129
     * @param mixed $value
130
     * @param Query $query
131
     * @param \Closure $each
132
     * @return \Generator
133
     */
134 24
    private function yieldThrough($key, $value, Query $query, \Closure $each): \Generator
135
    {
136
        switch (true) {
137 24
            case $value instanceof Field:
138 22
                return $this->applyField($query, $value);
139
                break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
140
141 10
            case $key instanceof Field:
142 8
                yield from $co = $this->applyFieldWithValue($query, $key, $value);
143 8
                return $co->getReturn();
144
                break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
145
146 2
            case $value instanceof \Closure:
147
                yield $value;
148
                return $value;
149
150 2
            case $value instanceof Query:
151 2
                $query->merge($value);
152 2
                return $query;
153
154
            default:
155
                return $each($value, $key);
156
        }
157
    }
158
159
    /**
160
     * @param Query $query
161
     * @param Field $field
162
     * @return string
163
     */
164 22
    private function applyField(Query $query, Field $field): string
165
    {
166 22
        return $field->withQuery($query)->toString();
167
    }
168
169
    /**
170
     * @param Query $query
171
     * @param Field $field
172
     * @param $value
173
     * @return \Generator
174
     * @throws \Exception
175
     */
176 8
    private function applyFieldWithValue(Query $query, Field $field, $value): \Generator
177
    {
178 8
        $placeholder = $query->placeholder($field->getName());
179
180 8
        yield $placeholder => $value;
181
182 8
        return $placeholder;
183
    }
184
}
185