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

DatabaseProcessor::getScalarResult()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 6
ccs 0
cts 3
cp 0
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 1
crap 2
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\Having::class    => DatabaseProcessor\HavingBuilder::class,
29
        Criteria\Join::class      => DatabaseProcessor\JoinBuilder::class,
30
        Criteria\Limit::class     => DatabaseProcessor\LimitBuilder::class,
31
        Criteria\Offset::class    => DatabaseProcessor\OffsetBuilder::class,
32
        Criteria\OrderBy::class   => DatabaseProcessor\OrderByBuilder::class,
33
        Criteria\Relation::class  => DatabaseProcessor\RelationBuilder::class,
34
        Criteria\Selection::class => DatabaseProcessor\SelectBuilder::class,
35
        Criteria\Where::class     => DatabaseProcessor\WhereBuilder::class,
36
    ];
37
38
    /**
39
     * @param Query $query
40
     * @return mixed
41
     */
42
    public function getScalarResult(Query $query)
43
    {
44
        return $this->exec($query, function (DoctrineQuery $query) {
45
            return $query->getSingleScalarResult();
46
        });
47
    }
48
49
    /**
50
     * @param Query $query
51
     * @param \Closure $execute
52
     * @return mixed
53
     */
54 30
    private function exec(Query $query, \Closure $execute)
55
    {
56 30
        $queue = new Queue();
57
58
        /** @var QueryBuilder $builder */
59 30
        $builder = $this->fillQueueThrough($queue, $this->toBuilder($query));
60
61 30
        $result = $execute($builder->getQuery());
62
63 30
        foreach ($queue->reduce($result, $this->meta) as $out) {
64
            $children = $this->bypass($out, $query, $this->applicator($builder));
65
66
            $this->fillQueueThrough($queue, $children);
67
        }
68
69 30
        return $result;
70
    }
71
72
    /**
73
     * @param Queue $queue
74
     * @param \Generator $generator
75
     * @return QueryBuilder|mixed
76
     */
77 30
    private function fillQueueThrough(Queue $queue, \Generator $generator)
78
    {
79 30
        foreach ($generator as $result) {
80
            if ($result instanceof \Closure) {
81
                $queue->push($result);
82
            }
83
        }
84
85 30
        return $generator->getReturn();
86
    }
87
88
    /**
89
     * Creates a new QueryBuilder and applies all necessary operations.
90
     * Returns a set of pending operations (Deferred) and QueryBuilder.
91
     *
92
     * @param Query $query
93
     * @return \Generator|\Closure[]|QueryBuilder
94
     */
95 30
    protected function toBuilder(Query $query): \Generator
96
    {
97 30
        $builder = $this->em->createQueryBuilder();
98 30
        $builder->setCacheable(false);
99
100 30
        yield from $generator = $this->apply($builder, $query);
101
102 30
        return $generator->getReturn();
103
    }
104
105
    /**
106
     * Applies all necessary operations to the QueryBuilder.
107
     * Returns a set of pending operations (Deferred) and QueryBuilder.
108
     *
109
     * @param QueryBuilder $builder
110
     * @param Query $query
111
     * @return \Generator
112
     */
113 30
    protected function apply(QueryBuilder $builder, Query $query): \Generator
114
    {
115
        // Add an alias and indicate that this
116
        // alias is relevant to the entity of a repository.
117 30
        $builder->addSelect($query->getAlias());
118 30
        $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...
119
120
        //
121
        //
122 30
        $response = $this->bypass($this->forEach($builder, $query), $query, $this->applicator($builder));
123
124 30
        foreach ($response as $field => $value) {
125 8
            if ($value instanceof \Closure) {
126
                yield $value;
127
                continue;
128
            }
129
130 8
            $builder->setParameter($field, $value);
131
        }
132
133 30
        return $builder;
134
    }
135
136
    /**
137
     * A set of coroutine operations valid for the current DatabaseProcessor.
138
     *
139
     * @param QueryBuilder $builder
140
     * @return \Closure
141
     */
142
    private function applicator(QueryBuilder $builder): \Closure
143
    {
144 30
        return function ($response) use ($builder) {
145
            // Send the context (the builder) in case the
146
            // answer contains an empty value.
147
            if ($response === null) {
148
                return $builder;
149
            }
150
151
            // In the case that the response is returned to the Query
152
            // instance - we need to fulfill this query and return a response.
153
            if ($response instanceof Query) {
154
                /** @var DatabaseProcessor $processor */
155
                $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...
156
157
                return $processor->getResult($response);
158
            }
159
160
            return $response;
161 30
        };
162
    }
163
164
    /**
165
     * @param Query $query
166
     * @return iterable
167
     */
168
    public function getResult(Query $query): iterable
169
    {
170 20
        return $this->exec($query, function (DoctrineQuery $query) {
171 20
            return $query->getResult();
172 20
        });
173
    }
174
175
    /**
176
     * @param Query $query
177
     * @return array
178
     */
179
    public function getArrayResult(Query $query): array
180
    {
181 12
        return $this->exec($query, function (DoctrineQuery $query) {
182 12
            return $query->getArrayResult();
183 12
        });
184
    }
185
}
186