Test Failed
Push — master ( b17eba...69b436 )
by Kirill
06:37
created

DatabaseProcessor::apply()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 22

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 12

Importance

Changes 0
Metric Value
dl 0
loc 22
ccs 0
cts 14
cp 0
rs 9.568
c 0
b 0
f 0
cc 3
nc 3
nop 2
crap 12
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\ORM\Query as DoctrineQuery;
13
use Doctrine\ORM\QueryBuilder;
14
use RDS\Hydrogen\Criteria;
15
use RDS\Hydrogen\Query;
16
17
/**
18
 * Class DatabaseProcessor
19
 */
20
class DatabaseProcessor extends Processor
21
{
22
    /**
23
     * @var string[]|BuilderInterface[]
24
     */
25
    protected const CRITERIA_MAPPINGS = [
26
        Criteria\Group::class     => DatabaseProcessor\GroupBuilder::class,
27
        Criteria\GroupBy::class   => DatabaseProcessor\GroupByBuilder::class,
28
        Criteria\Limit::class     => DatabaseProcessor\LimitBuilder::class,
29
        Criteria\Offset::class    => DatabaseProcessor\OffsetBuilder::class,
30
        Criteria\OrderBy::class   => DatabaseProcessor\OrderByBuilder::class,
31
        Criteria\Relation::class  => DatabaseProcessor\RelationBuilder::class,
32
        Criteria\Selection::class => DatabaseProcessor\SelectBuilder::class,
33
        Criteria\Where::class     => DatabaseProcessor\WhereBuilder::class,
34
    ];
35
36
    /**
37
     * @param Query $query
38
     * @return mixed
39
     */
40
    public function getScalarResult(Query $query)
41
    {
42
        return $this->exec($query, function (DoctrineQuery $query) {
43
            return $query->getSingleScalarResult();
44
        });
45
    }
46
47
    /**
48
     * @param Query $query
49
     * @param \Closure $execute
50
     * @return mixed
51
     */
52
    private function exec(Query $query, \Closure $execute)
53
    {
54
        $metadata = $this->em->getClassMetadata($this->repository->getClassName());
55
56
        $queue = new Queue();
57
58
        /** @var QueryBuilder $builder */
59
        $builder = $this->fillQueueThrough($queue, $this->toBuilder($query));
60
61
        $result = $execute($builder->getQuery());
62
63
        foreach ($queue->reduce($result, $metadata) as $out) {
64
            $children = $this->bypass($out, $query, $this->applicator($builder));
65
66
            $this->fillQueueThrough($queue, $children);
67
        }
68
69
        return $result;
70
    }
71
72
    /**
73
     * @param Queue $queue
74
     * @param \Generator $generator
75
     * @return QueryBuilder|mixed
76
     */
77
    private function fillQueueThrough(Queue $queue, \Generator $generator)
78
    {
79
        foreach ($generator as $result) {
80
            if ($result instanceof \Closure) {
81
                $queue->push($result);
82
            }
83
        }
84
85
        return $generator->getReturn();
86
    }
87
88
    /**
89
     * A set of coroutine operations valid for the current DatabaseProcessor.
90
     *
91
     * @param QueryBuilder $builder
92
     * @return \Closure
93
     */
94
    private function applicator(QueryBuilder $builder): \Closure
95
    {
96
        return function ($response) use ($builder) {
97
            // Send the context (the builder) in case the
98
            // answer contains an empty value.
99
            if ($response === null) {
100
                return $builder;
101
            }
102
103
            // In the case that the response is returned to the Query
104
            // instance - we need to fulfill this query and return a response.
105
            if ($response instanceof Query) {
106
                /** @var DatabaseProcessor $processor */
107
                $processor = $response->getRepository()->getProcessor();
0 ignored issues
show
Bug introduced by
The method getProcessor does only exist in RDS\Hydrogen\Hydrogen, but not in Doctrine\Common\Persistence\ObjectRepository.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
108
109
                return $processor->getResult($response);
110
            }
111
112
            return $response;
113
        };
114
    }
115
116
    /**
117
     * Applies all necessary operations to the QueryBuilder.
118
     * Returns a set of pending operations (Deferred) and QueryBuilder.
119
     *
120
     * @param QueryBuilder $builder
121
     * @param Query $query
122
     * @return \Generator
123
     */
124
    protected function apply(QueryBuilder $builder, Query $query): \Generator
125
    {
126
        // Add an alias and indicate that this
127
        // alias is relevant to the entity of a repository.
128
        $builder->addSelect($query->getAlias());
129
        $builder->from($query->getRepository()->getClassName(), $query->getAlias());
0 ignored issues
show
Bug introduced by
The method getClassName does only exist in Doctrine\Common\Persistence\ObjectRepository, but not in RDS\Hydrogen\Hydrogen.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
130
131
        //
132
        //
133
        $response = $this->bypass($this->forEach($builder, $query), $query, $this->applicator($builder));
134
135
        foreach ($response as $field => $value) {
136
            if ($value instanceof \Closure) {
137
                yield $value;
138
                continue;
139
            }
140
141
            $builder->setParameter($field, $value);
142
        }
143
144
        return $builder;
145
    }
146
147
    /**
148
     * Creates a new QueryBuilder and applies all necessary operations.
149
     * Returns a set of pending operations (Deferred) and QueryBuilder.
150
     *
151
     * @param Query $query
152
     * @return \Generator|\Closure[]|QueryBuilder
153
     */
154
    protected function toBuilder(Query $query): \Generator
155
    {
156
        yield from $generator = $this->apply($this->em->createQueryBuilder(), $query);
157
158
        return $generator->getReturn();
159
    }
160
161
    /**
162
     * @param Query $query
163
     * @return iterable
164
     */
165
    public function getResult(Query $query): iterable
166
    {
167
        return $this->exec($query, function (DoctrineQuery $query) {
168
            return $query->getResult();
169
        });
170
    }
171
172
    /**
173
     * @param Query $query
174
     * @return array
175
     */
176
    public function getArrayResult(Query $query): array
177
    {
178
        return $this->exec($query, function (DoctrineQuery $query) {
179
            return $query->getArrayResult();
180
        });
181
    }
182
}
183