Failed Conditions
Push — next ( a0f087...d84956 )
by Bas
03:42 queued 15s
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 371
    public function processEmptyReturnValues(array $returnAttributes, array $returnDocs, Builder $query): array
21
    {
22 371
        if (empty($returnAttributes) && empty($returnDocs)) {
23 286
            $returnDocs[] = (string) $query->getTableAlias($query->from);
24
25 286
            if ($query->joins !== null) {
26 6
                $returnDocs = $this->mergeJoinResults($query, $returnDocs);
27
            }
28
        }
29 371
        return $returnDocs;
30
    }
31
32
    /**
33
     * @param Builder $query
34
     * @param mixed[] $returnDocs
35
     * @param mixed[] $returnAttributes
36
     * @return mixed[]
37
     */
38 371
    public function processAggregateReturnValues(Builder $query, array $returnDocs, array $returnAttributes): array
39
    {
40 371
        if ($query->aggregate !== null && $query->unions === null) {
41 77
            $returnDocs = [];
42 77
            $returnAttributes = ['aggregate' => 'aggregateResult'];
43
        }
44 371
        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 371
    protected function compileColumns(IlluminateQueryBuilder $query, $columns)
56
    {
57
        assert($query instanceof Builder);
58
59 371
        $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

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

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

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

286
            /** @scrutinizer ignore-call */ 
287
            $returnDocs[] = $this->generateAqlObject($returnAttributes);
Loading history...
287
        }
288
289 371
        $values = $this->mergeReturnDocs($returnDocs);
290
291 371
        return $values;
292
    }
293
294
    /**
295
     * @param array<string> $returnDocs
296
     * @return string
297
     */
298 371
    protected function mergeReturnDocs($returnDocs)
299
    {
300 371
        if (sizeOf($returnDocs) > 1) {
301 27
            return 'MERGE(' . implode(', ', $returnDocs) . ')';
302
        }
303
304 367
        return $returnDocs[0];
305
    }
306
307
    /**
308
     * @param IlluminateQueryBuilder $query
309
     * @param array<string> $returnDocs
310
     * @return array<string>
311
     */
312 6
    protected function mergeJoinResults(IlluminateQueryBuilder $query, $returnDocs = []): array
313
    {
314
        assert($query instanceof Builder);
315
316 6
        if (!is_array($query->joins)) {
317
            return $returnDocs;
318
        }
319
320 6
        foreach ($query->joins as $join) {
321 6
            $tableAlias = $query->getTableAlias($join->table);
322
323 6
            if (!isset($tableAlias)) {
324
                $tableAlias = $query->generateTableAlias($join->table);
325
            }
326 6
            $returnDocs[] = (string) $tableAlias;
327
        }
328
329 6
        return $returnDocs;
330
    }
331
}
332