Failed Conditions
Push — 186-self-joins-fail-in-relatio... ( ba3e0e...5bcaaa )
by Bas
08:01 queued 40s
created

CompilesColumns::prepareColumns()   B

Complexity

Conditions 11
Paths 6

Size

Total Lines 40
Code Lines 22

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 18
CRAP Score 12.2432

Importance

Changes 0
Metric Value
cc 11
eloc 22
c 0
b 0
f 0
nc 6
nop 2
dl 0
loc 40
ccs 18
cts 23
cp 0.7826
crap 12.2432
rs 7.3166

How to fix   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
     * @param mixed[] $returnAttributes
16
     * @param mixed[] $returnDocs
17
     * @param Builder $query
18
     * @return mixed[]
19
     */
20 389
    public function processEmptyReturnValues(array $returnAttributes, array $returnDocs, Builder $query): array
21
    {
22 389
        if (empty($returnAttributes) && empty($returnDocs)) {
23 304
            $returnDocs[] = (string) $query->getTableAlias($query->from);
24
25 304
            if ($query->joins !== null) {
26 6
                $returnDocs = $this->mergeJoinResults($query, $returnDocs);
27
            }
28
        }
29 389
        return $returnDocs;
30
    }
31
32
    /**
33
     * @param Builder $query
34
     * @param mixed[] $returnDocs
35
     * @param mixed[] $returnAttributes
36
     * @return mixed[]
37
     */
38 389
    public function processAggregateReturnValues(Builder $query, array $returnDocs, array $returnAttributes): array
39
    {
40 389
        if ($query->aggregate !== null && $query->unions === null) {
41 77
            $returnDocs = [];
42 77
            $returnAttributes = ['aggregate' => 'aggregateResult'];
43
        }
44 389
        return [$returnDocs, $returnAttributes];
45
    }
46
47
    /**
48
     * Compile the "select *" portion of the query.
49
     *
50
     * @param IlluminateQueryBuilder $query
51
     * @param array<mixed> $columns
52
     * @return string|null
53
     * @throws Exception
54
     */
55 389
    protected function compileColumns(IlluminateQueryBuilder $query, $columns)
56
    {
57
        assert($query instanceof Builder);
58
59 389
        if (count($columns) === 1  && !is_string($columns[0]) && is_scalar($columns[0])) {
60
            return 'RETURN '.var_export($columns[0], true);
61 389
        }
62
63 389
        $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

63
        /** @scrutinizer ignore-call */ 
64
        $columns = $this->convertJsonFields($columns);
Loading history...
64
65 389
        [$returnAttributes, $returnDocs] = $this->prepareColumns($query, $columns);
66 389
67 4
        $returnValues = $this->determineReturnValues($query, $returnAttributes, $returnDocs);
68
69
        $return = 'RETURN ';
70 389
        if ($query->distinct) {
71
            $return .= 'DISTINCT ';
72
        }
73
74
        return $return . $returnValues;
75
    }
76
77
    /**
78
     * @param IlluminateQueryBuilder $query
79
     * @param array<mixed> $columns
80
     * @return array<mixed>
81 389
     * @throws Exception
82
     *
83
     * @SuppressWarnings(PHPMD.CyclomaticComplexity)
84
     */
85 389
    protected function prepareColumns(IlluminateQueryBuilder $query, array $columns)
86 389
    {
87
        assert($query instanceof Builder);
88 389
89
        $returnDocs = [];
90 389
        $returnAttributes = [];
91 21
92 21
        foreach ($columns as $key => $column) {
93
            // Extract complete documents
94 21
            if (is_string($column) && substr($column, strlen($column) - 2)  === '.*') {
95
                $table = substr($column, 0, strlen($column) - 2);
96
                $returnDocs[] = $query->getTableAlias($table);
97
98 389
                continue;
99 10
            }
100
101 10
            // Extract groups
102
            if (is_array($query->groups) && in_array($column, $query->groups)) {
103
                $returnAttributes[$column] = $this->normalizeColumn($query, $column);
104 380
105 151
                continue;
106
            }
107 151
108
            if (is_string($column) && $column != null && $column != '*') {
109
                [$column, $alias] = $this->normalizeStringColumn($query, $key, $column);
110
111
                if (isset($returnAttributes[$alias]) && is_array($column)) {
112
                    $returnAttributes[$alias] = array_merge_recursive(
113
                        $returnAttributes[$alias],
114 151
                        $this->normalizeColumn($query, $column),
115
                    );
116
                    continue;
117
                }
118 389
                $returnAttributes[$alias] = $column;
119 389
            }
120 389
        }
121 389
122
        return [
123
            $returnAttributes,
124
            $returnDocs,
125
        ];
126
    }
127 330
128
    /**
129
     * @throws Exception
130
     */
131 330
    protected function normalizeColumn(IlluminateQueryBuilder $query, mixed $column, ?string $table = null): mixed
132
    {
133
        assert($query instanceof Builder);
134
135
        if ($column instanceof Expression) {
136 330
            return $column;
137 330
        }
138 330
139
        if (
140 10
            is_array($query->groups)
141
            && in_array($column, $query->groups)
142
            && debug_backtrace(DEBUG_BACKTRACE_PROVIDE_OBJECT)[1]['function'] !== "compileGroups"
143 330
        ) {
144
            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

144
            return $this->/** @scrutinizer ignore-call */ wrap($query->convertIdToKey($column));
Loading history...
145
        }
146
147
        if (is_array($column)) {
148
            $column = $this->convertJsonFields($column);
149
150
            foreach ($column as $key => $value) {
151
                $column[$key] = $this->normalizeColumn($query, $value, $table);
152 330
            }
153 1
            return $column;
154
        }
155
156
        if ($query->isVariable($column)) {
157 330
            return $this->wrap($column);
158 330
        }
159
160
161
        $column = $this->convertJsonFields($column);
162 330
163
        $column = $query->convertIdToKey($column);
164
165
        //We check for an existing alias to determine of the first reference is a table.
166
        // In which case we replace it with the alias.
167
        return $this->wrap($this->normalizeColumnReferences($query, $column, $table));
168
    }
169
170
    /**
171
     * @param Builder $query
172
     * @param int|string $key
173 151
     * @param string $column
174
     * @param string|null $table
175 151
     * @return array<mixed>
176
     * @throws Exception
177 151
     */
178
    protected function normalizeStringColumn(Builder $query, int|string $key, string $column, ?string $table = null): array
179 151
    {
180
        [$column, $alias] = $query->extractAlias($column, $key);
181
182 151
        $column = $query->convertIdToKey($column);
183
184 151
        $column = $this->wrap($this->normalizeColumnReferences($query, $column, $table));
185
186
        /** @phpstan-ignore-next-line */
187
        $alias = $this->cleanAlias($query, $alias);
188
189
        return [$column, $alias];
190
    }
191
192
193
    /**
194 353
     * @param IlluminateQueryBuilder $query
195
     * @param string $column
196
     * @param string|null $table
197
     * @return string
198 353
     */
199 121
    protected function normalizeColumnReferences(IlluminateQueryBuilder $query, string $column, ?string $table = null): string
200
    {
201
        assert($query instanceof Builder);
202 322
203 322
        // if the given column is a table alias or variable return it.
204
        if ($query->isReference($column)) {
205
            return $column;
206 322
        }
207
208 322
        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...
209
            $table = (string) $this->getValue($query->from);
0 ignored issues
show
Bug introduced by
It seems like getValue() 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

209
            $table = (string) $this->/** @scrutinizer ignore-call */ getValue($query->from);
Loading history...
210 322
        }
211 50
212
        $references = explode('.', $column);
213
214 322
        // If an alias exists for the first element, replace it by the alias.
215 1
        $tableAlias = $query->getTableAlias($references[0]);
216 1
217
        if (isset($tableAlias)) {
218
            $references[0] = $tableAlias;
219
        }
220 322
221 310
        if (array_key_exists('groupsVariable', $query->tableAliases)) {
222
            $tableAlias = 'groupsVariable';
223
            array_unshift($references, $tableAlias);
224 322
        }
225 3
226 3
        // geen tableAlias, table is parent...waarom geen tableAlias?
227
        if ($tableAlias === null  && array_key_exists($table, $query->tableAliases)) {
228
            array_unshift($references, $query->tableAliases[$table]);
229
        }
230 322
231
        if ($tableAlias === null && !$query->isReference($references[0])) {
232
            $tableAlias = $query->generateTableAlias($table);
233 151
            array_unshift($references, $tableAlias);
234
        }
235
236
        /** @phpstan-ignore-next-line */
237 151
        return implode('.', $references);
238
    }
239
240
    /**
241 151
     * Remove any table, tableAlias or variable prefixes from the alias.
242 149
     *
243
     * @param IlluminateQueryBuilder $query
244
     * @param int|string|null $alias
245 8
     * @return int|string|null
246
     */
247
    protected function cleanAlias(IlluminateQueryBuilder $query, int|null|string $alias): int|string|null
248 8
    {
249 8
        assert($query instanceof Builder);
250
251 6
        if (!is_string($alias)) {
252
            return $alias;
253
        }
254 2
255
        if (!str_contains($alias, '.')) {
256 2
            return $alias;
257
        }
258
259
        $elements = explode('.', $alias);
260
261
        if (
262
            !$query->isTable($elements[0])
263
            && !$query->isReference($elements[0])
264
        ) {
265
            return $alias;
266 389
        }
267
268
        array_shift($elements);
269
270
        return implode($elements);
271 389
    }
272
273
274 389
    /**
275
     * @param IlluminateQueryBuilder $query
276
     * @param array<string> $returnAttributes
277
     * @param array<string>  $returnDocs
278 389
     * @return string
279 389
     */
280 389
    protected function determineReturnValues(IlluminateQueryBuilder $query, $returnAttributes = [], $returnDocs = []): string
281
    {
282 3
        assert($query instanceof Builder);
283
284
        // If nothing was specifically requested, we return everything.
285 389
        $returnDocs = $this->processEmptyReturnValues($returnAttributes, $returnDocs, $query);
286 165
287
        // Aggregate functions only return the aggregate, so we can clear out everything else.
288
        list($returnDocs, $returnAttributes) = $this->processAggregateReturnValues($query, $returnDocs, $returnAttributes);
289 389
290
        // Return a single value for certain subqueries
291 389
        if (
292
            $query->returnSingleValue === true
293
            && count($returnAttributes) === 1
294
            && empty($returnDocs)
295
        ) {
296
            return reset($returnAttributes);
297
        }
298 389
299
        if (!empty($returnAttributes)) {
300 389
            $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

300
            /** @scrutinizer ignore-call */ 
301
            $returnDocs[] = $this->generateAqlObject($returnAttributes);
Loading history...
301
        }
302 389
303 27
        $values = $this->mergeReturnDocs($returnDocs);
304
305
        return $values;
306 385
    }
307
308
    /**
309
     * @param array<string> $returnDocs
310
     * @return string
311
     */
312
    protected function mergeReturnDocs($returnDocs)
313
    {
314 6
        $returnDocs = array_filter($returnDocs);
315
316
        if (sizeOf($returnDocs) > 1) {
317
            return 'MERGE(' . implode(', ', $returnDocs) . ')';
318 6
        }
319
320
        if (empty($returnDocs)) {
321
            return '';
322 6
        }
323 6
324
        return reset($returnDocs);
325 6
    }
326
327
    /**
328 6
     * @param IlluminateQueryBuilder $query
329
     * @param array<string> $returnDocs
330
     * @return array<string>
331 6
     */
332
    protected function mergeJoinResults(IlluminateQueryBuilder $query, $returnDocs = []): array
333
    {
334
        assert($query instanceof Builder);
335
336
        if (!is_array($query->joins)) {
337
            return $returnDocs;
338
        }
339
340
        foreach ($query->joins as $join) {
341
            $tableAlias = $query->getTableAlias($join->table);
342
343
            if (!isset($tableAlias)) {
344
                $tableAlias = $query->generateTableAlias($join->table);
345
            }
346
            $returnDocs[] = (string) $tableAlias;
347
        }
348
349
        return $returnDocs;
350
    }
351
}
352