Failed Conditions
Push — refactor/improve-static-analys... ( 45b897...d71351 )
by Bas
05:25
created

CompilesColumns::compileColumns()   C

Complexity

Conditions 14
Paths 16

Size

Total Lines 52
Code Lines 26

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 25
CRAP Score 14

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 14
eloc 26
c 2
b 0
f 0
nc 16
nop 2
dl 0
loc 52
rs 6.2666
ccs 25
cts 25
cp 1
crap 14

How to fix   Long Method    Complexity   

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 LaravelFreelancerNL\Aranguent\Query\Concerns;
6
7
use Exception;
8
use Illuminate\Database\Query\Builder as IlluminateQueryBuilder;
9
use Illuminate\Database\Query\Expression;
10
use LaravelFreelancerNL\Aranguent\Query\Builder;
11
12
trait CompilesColumns
13
{
14
    /**
15
     * Compile the "select *" portion of the query.
16
     *
17 145
     * @param IlluminateQueryBuilder $query
18
     * @param array $columns
19 145
     * @return string|null
20 145
     * @throws Exception
21 145
     */
22
    protected function compileColumns(IlluminateQueryBuilder $query, $columns)
23
    {
24 145
        assert($query instanceof Builder);
25
26 145
        $returnDocs = [];
27 12
        $returnAttributes = [];
28 12
29
        $columns = $this->convertJsonFields($columns);
0 ignored issues
show
Bug introduced by
It seems like convertJsonFields() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

29
        /** @scrutinizer ignore-call */ 
30
        $columns = $this->convertJsonFields($columns);
Loading history...
30 12
31
        // Prepare columns
32
        foreach ($columns as $key => $column) {
33
            // Extract complete documents
34 145
            if (is_string($column) && substr($column, strlen($column) - 2)  === '.*') {
35 5
                $table = substr($column, 0, strlen($column) - 2);
36
                $returnDocs[] = $query->getTableAlias($table);
37 5
38
                continue;
39
            }
40 140
41 34
            // Extract groups
42
            if (is_array($query->groups) && in_array($column, $query->groups)) {
43 34
                $returnAttributes[$column] = $this->normalizeColumn($query, $column);
44
45
                continue;
46 145
            }
47
48 145
            if (is_string($column) && $column != null && $column != '*') {
49
                [$column, $alias] = $this->normalizeStringColumn($query, $key, $column);
50 145
                if (isset($returnAttributes[$alias]) && is_array($column)) {
51
                    $returnAttributes[$alias] = array_merge_recursive(
52
                        $returnAttributes[$alias],
53 145
                        $this->normalizeColumn($query, $column)
54
                    );
55 145
                    continue;
56
                }
57 145
                $returnAttributes[$alias] = $column;
58
            }
59 145
60 24
            if (is_string($column) && $column === '*') {
61
                //TODO place table alias and join aliases in returnDocuments?
62
            }
63 145
64 118
        }
65 118
66 4
        $returnValues = $this->determineReturnValues($query, $returnAttributes, $returnDocs);
67
68
        $return = 'RETURN ';
69
        if ($query->distinct) {
70 145
            $return .= 'DISTINCT ';
71
        }
72
73 145
        return $return . $returnValues;
74
    }
75 145
76 145
    /**
77 39
     * @throws Exception
78
     */
79
    protected function normalizeColumn(IlluminateQueryBuilder $query, mixed $column, string $table = null): mixed
80
    {
81
        assert($query instanceof Builder);
82 145
83 17
        if ($column instanceof Expression) {
84
            return $column;
85
        }
86 145
87
        if (
88
            is_array($query->groups)
89 145
            && in_array($column, $query->groups)
90
            && debug_backtrace(DEBUG_BACKTRACE_PROVIDE_OBJECT)[1]['function'] !== "compileGroups"
91 145
        ) {
92 12
            return $this->wrap($query->convertIdToKey($column));
0 ignored issues
show
Bug introduced by
It seems like wrap() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

92
            return $this->/** @scrutinizer ignore-call */ wrap($query->convertIdToKey($column));
Loading history...
93
        }
94
95 145
        if (is_array($column)) {
96 12
            $column = $this->convertJsonFields($column);
97
98 145
            foreach ($column as $key => $value) {
99
                $column[$key] = $this->normalizeColumn($query, $value, $table);
100
            }
101
            return $column;
102 4
        }
103
104 4
        if ($query->isVariable($column)) {
105 4
            return $this->wrap($column);
106 4
        }
107
108 4
109 4
        $column = $this->convertJsonFields($column);
110
        $column = $query->convertIdToKey($column);
111 4
112
        //We check for an existing alias to determine of the first reference is a table.
113
        // In which case we replace it with the alias.
114
        return $this->wrap($this->normalizeColumnReferences($query, $column, $table));
0 ignored issues
show
Bug introduced by
It seems like $column can also be of type array and array and array<mixed,mixed> and array<mixed,mixed>; however, parameter $column of LaravelFreelancerNL\Aran...alizeColumnReferences() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

114
        return $this->wrap($this->normalizeColumnReferences($query, /** @scrutinizer ignore-type */ $column, $table));
Loading history...
115
    }
116
117
    /**
118
     * @param string $column
119
     * @return array<mixed>
120
     * @throws Exception
121
     */
122
    protected function normalizeStringColumn(Builder $query, int|string $key, string $column, string $table = null): array
123
    {
124
        [$column, $alias] = $query->extractAlias($column, $key);
125
126
        $column = $query->convertIdToKey($column);
127
128
        $column = $this->wrap($this->normalizeColumnReferences($query, $column, $table));
0 ignored issues
show
Bug introduced by
It seems like $column can also be of type array and array and array<mixed,mixed> and array<mixed,mixed>; however, parameter $column of LaravelFreelancerNL\Aran...alizeColumnReferences() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

128
        $column = $this->wrap($this->normalizeColumnReferences($query, /** @scrutinizer ignore-type */ $column, $table));
Loading history...
129
130
        $alias = $this->cleanAlias($query, $alias);
131
132
        return [$column, $alias];
133
    }
134
135
136
    /**
137
     * @param IlluminateQueryBuilder $query
138
     * @param string $column
139
     * @param string|null $table
140
     * @return string
141
     */
142
    protected function normalizeColumnReferences(IlluminateQueryBuilder $query, string $column, string $table = null): string
143
    {
144
        assert($query instanceof Builder);
145
146
        if ($query->isReference($column)) {
147
            return $column;
148
        }
149
150
        if ($table == null) {
0 ignored issues
show
Bug introduced by
It seems like you are loosely comparing $table of type null|string against null; this is ambiguous if the string can be empty. Consider using a strict comparison === instead.
Loading history...
151
            $table = $query->from;
152
        }
153
154
        $references = explode('.', $column);
155
156
157
        $tableAlias = $query->getTableAlias($references[0]);
158
159
        if (isset($tableAlias)) {
160
            $references[0] = $tableAlias;
161
        }
162
163
        if (array_key_exists('groupsVariable', $query->tableAliases)) {
164
            $tableAlias = 'groupsVariable';
165
            array_unshift($references, $tableAlias);
166
        }
167
168
        if ($tableAlias === null  && array_key_exists($table, $query->tableAliases)) {
169
            array_unshift($references, $query->tableAliases[$table]);
170
        }
171
172
        if ($tableAlias === null && !$query->isReference($references[0])) {
173
            $tableAlias = $query->generateTableAlias($table);
174
            array_unshift($references, $tableAlias);
175
        }
176
177
        return implode('.', $references);
178
    }
179
180
    protected function cleanAlias(IlluminateQueryBuilder $query, int|null|string $alias): int|string|null
181
    {
182
        assert($query instanceof Builder);
183
184
        if (!is_string($alias)) {
185
            return $alias;
186
        }
187
188
        if (!str_contains($alias, '.')) {
189
            return $alias;
190
        }
191
192
        $elements = explode('.', $alias);
193
194
        if(
195
            !$query->isTable($elements[0])
196
            && !$query->isVariable($elements[0])
197
        ) {
198
            return $alias;
199
        }
200
201
        array_shift($elements);
202
203
        return implode($elements);
204
    }
205
206
207
    protected function determineReturnValues($query, $returnAttributes = [], $returnDocs = []): string
208
    {
209
        // If nothing was specifically requested, we return everything.
210
        if (empty($returnAttributes) && empty($returnDocs)) {
211
            $returnDocs[] = $query->getTableAlias($query->from);
212
213
            if ($query->joins !== null) {
214
                $returnDocs = $this->mergeJoinResults($query, $returnDocs);
215
            }
216
        }
217
218
        // clean up returnAttributes?
219
220
        // Aggregate functions only return the aggregate, so we can clear out everything else.
221
        if ($query->aggregate !== null) {
222
            $returnDocs = [];
223
            $returnAttributes = ['`aggregate`' => 'aggregateResult'];
224
        }
225
226
        // Return a single value for certain subqueries
227
        if (
228
            $query->returnSingleValue === true
229
            && count($returnAttributes) === 1
230
            && empty($returnDocs)
231
        ) {
232
            return reset($returnAttributes);
233
        }
234
235
        if (!empty($returnAttributes)) {
236
            $returnDocs[] = $this->generateAqlObject($returnAttributes);
0 ignored issues
show
Bug introduced by
It seems like generateAqlObject() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

236
            /** @scrutinizer ignore-call */ 
237
            $returnDocs[] = $this->generateAqlObject($returnAttributes);
Loading history...
237
        }
238
239
        $values = $this->mergeReturnDocs($returnDocs);
240
241
        return $values;
242
    }
243
244
    protected function mergeReturnDocs($returnDocs)
245
    {
246
        if (sizeOf($returnDocs) > 1) {
247
            return 'MERGE(' . implode(', ', $returnDocs) . ')';
248
        }
249
250
        return reset($returnDocs);
251
    }
252
253
    protected function mergeJoinResults(IlluminateQueryBuilder $query, $returnDocs = []): array
254
    {
255
        assert($query instanceof Builder);
256
257
        foreach ($query->joins as $key => $join) {
258
            $tableAlias = $query->getTableAlias($join->table);
259
260
            if (!isset($tableAlias)) {
261
                $tableAlias = $query->generateTableAlias($join->table);
262
            }
263
            $returnDocs[] = $tableAlias;
264
        }
265
266
        return $returnDocs;
267
    }
268
}
269