Failed Conditions
Push — next ( 045758...d6d4d6 )
by
unknown
30:18 queued 20:12
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
        $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 389
        [$returnAttributes, $returnDocs] = $this->prepareColumns($query, $columns);
62
63 389
        $returnValues = $this->determineReturnValues($query, $returnAttributes, $returnDocs);
64
65 389
        $return = 'RETURN ';
66 389
        if ($query->distinct) {
67 4
            $return .= 'DISTINCT ';
68
        }
69
70 389
        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 389
    protected function prepareColumns(IlluminateQueryBuilder $query, array $columns)
82
    {
83
        assert($query instanceof Builder);
84
85 389
        $returnDocs = [];
86 389
        $returnAttributes = [];
87
88 389
        foreach ($columns as $key => $column) {
89
            // Extract complete documents
90 389
            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 389
            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 380
            if (is_string($column) && $column != null && $column != '*') {
105 151
                [$column, $alias] = $this->normalizeStringColumn($query, $key, $column);
106
107 151
                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 151
                $returnAttributes[$alias] = $column;
115
            }
116
        }
117
118 389
        return [
119 389
            $returnAttributes,
120 389
            $returnDocs,
121 389
        ];
122
    }
123
124
    /**
125
     * @throws Exception
126
     */
127 330
    protected function normalizeColumn(IlluminateQueryBuilder $query, mixed $column, ?string $table = null): mixed
128
    {
129
        assert($query instanceof Builder);
130
131 330
        if ($column instanceof Expression) {
132
            return $column;
133
        }
134
135
        if (
136 330
            is_array($query->groups)
137 330
            && in_array($column, $query->groups)
138 330
            && 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 330
        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 330
        if ($query->isVariable($column)) {
153 1
            return $this->wrap($column);
154
        }
155
156
157 330
        $column = $this->convertJsonFields($column);
158 330
        $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 330
        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 151
    protected function normalizeStringColumn(Builder $query, int|string $key, string $column, ?string $table = null): array
174
    {
175 151
        [$column, $alias] = $query->extractAlias($column, $key);
176
177 151
        $column = $query->convertIdToKey($column);
178
179 151
        $column = $this->wrap($this->normalizeColumnReferences($query, $column, $table));
180
181
        /** @phpstan-ignore-next-line */
182 151
        $alias = $this->cleanAlias($query, $alias);
183
184 151
        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 353
    protected function normalizeColumnReferences(IlluminateQueryBuilder $query, string $column, ?string $table = null): string
195
    {
196
        assert($query instanceof Builder);
197
198 353
        if ($query->isReference($column)) {
199 121
            return $column;
200
        }
201
202 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...
203 322
            $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 322
        $references = explode('.', $column);
207
208 322
        $tableAlias = $query->getTableAlias($references[0]);
209
210 322
        if (isset($tableAlias)) {
211 50
            $references[0] = $tableAlias;
212
        }
213
214 322
        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 322
        if ($tableAlias === null  && array_key_exists($table, $query->tableAliases)) {
221 310
            array_unshift($references, $query->tableAliases[$table]);
222
        }
223
224 322
        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 322
        return implode('.', $references);
231
    }
232
233 151
    protected function cleanAlias(IlluminateQueryBuilder $query, int|null|string $alias): int|string|null
234
    {
235
        assert($query instanceof Builder);
236
237 151
        if (!is_string($alias)) {
238
            return $alias;
239
        }
240
241 151
        if (!str_contains($alias, '.')) {
242 149
            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 389
    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 389
        $returnDocs = $this->processEmptyReturnValues($returnAttributes, $returnDocs, $query);
272
273
        // Aggregate functions only return the aggregate, so we can clear out everything else.
274 389
        list($returnDocs, $returnAttributes) = $this->processAggregateReturnValues($query, $returnDocs, $returnAttributes);
275
276
        // Return a single value for certain subqueries
277
        if (
278 389
            $query->returnSingleValue === true
279 389
            && count($returnAttributes) === 1
280 389
            && empty($returnDocs)
281
        ) {
282 3
            return reset($returnAttributes);
283
        }
284
285 389
        if (!empty($returnAttributes)) {
286 165
            $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 389
        $values = $this->mergeReturnDocs($returnDocs);
290
291 389
        return $values;
292
    }
293
294
    /**
295
     * @param array<string> $returnDocs
296
     * @return string
297
     */
298 389
    protected function mergeReturnDocs($returnDocs)
299
    {
300 389
        if (sizeOf($returnDocs) > 1) {
301 27
            return 'MERGE(' . implode(', ', $returnDocs) . ')';
302
        }
303
304 385
        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