Failed Conditions
Push — refactor/improve-static-analys... ( bdf823...46faab )
by Bas
10:08
created

CompilesColumns::normalizeColumn()   B

Complexity

Conditions 9
Paths 6

Size

Total Lines 34
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 17
CRAP Score 9

Importance

Changes 0
Metric Value
eloc 17
c 0
b 0
f 0
dl 0
loc 34
ccs 17
cts 17
cp 1
rs 8.0555
cc 9
nc 6
nop 3
crap 9
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
use LaravelFreelancerNL\FluentAQL\QueryBuilder;
12
13
trait CompilesColumns
14
{
15
    /**
16
     * Compile the "select *" portion of the query.
17 145
     *
18
     * @param IlluminateQueryBuilder $query
19 145
     * @param  array  $columns
20 145
     * @return string|null
21 145
     */
22
    protected function compileColumns(IlluminateQueryBuilder $query, $columns)
23
    {
24 145
        $returnDocs = [];
25
        $returnAttributes = [];
26 145
27 12
        $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

27
        /** @scrutinizer ignore-call */ 
28
        $columns = $this->convertJsonFields($columns);
Loading history...
28 12
29
        // Prepare columns
30 12
        foreach ($columns as $key => $column) {
31
            // Extract complete documents
32
            if (is_string($column) && substr($column, strlen($column) - 2)  === '.*') {
33
                $table = substr($column, 0, strlen($column) - 2);
34 145
                $returnDocs[] = $query->getTableAlias($table);
35 5
36
                continue;
37 5
            }
38
39
            // Extract groups
40 140
            if (is_array($query->groups) && in_array($column, $query->groups)) {
41 34
                $returnAttributes[$key] = $this->normalizeColumn($query, $column);
42
43 34
                continue;
44
            }
45
46 145
            if (is_string($column) && $column != null && $column != '*') {
47
                [$column, $alias] = $this->normalizeStringColumn($query, $key, $column);
48 145
49
                if (isset($returnAttributes[$alias]) && is_array($column)) {
50 145
                    $returnAttributes[$alias] = array_merge_recursive(
51
                        $returnAttributes[$alias],
52
                        $this->normalizeColumn($query, $column)
53 145
                    );
54
                    continue;
55 145
                }
56
                $returnAttributes[$alias] = $column;
57 145
            }
58
        }
59 145
60 24
        $values = $this->determineReturnValues($query, $returnAttributes, $returnDocs);
61
62
        $return = 'RETURN ';
63 145
        if ($query->distinct) {
64 118
            $return .= 'DISTINCT ';
65 118
        }
66 4
67
        return $return . $values;
68
    }
69
70 145
    /**
71
     * @throws Exception
72
     */
73 145
    protected function normalizeColumn(IlluminateQueryBuilder $query, mixed $column, string $table = null): mixed
74
    {
75 145
        if ($column instanceof QueryBuilder || $column instanceof Expression) {
76 145
            return $column;
77 39
        }
78
79
        if (
80
            is_array($query->groups)
81
            && in_array($column, $query->groups)
82 145
            && debug_backtrace(DEBUG_BACKTRACE_PROVIDE_OBJECT)[1]['function'] !== "compileGroups"
83 17
        ) {
84
            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

84
            return $this->/** @scrutinizer ignore-call */ wrap($query->convertIdToKey($column));
Loading history...
85
        }
86 145
87
        if (is_array($column)) {
88
            $column = $this->convertJsonFields($column);
89 145
90
            foreach ($column as $key => $value) {
91 145
                $column[$key] = $this->normalizeColumn($query, $value, $table);
92 12
            }
93
            return $column;
94
        }
95 145
96 12
        if (key_exists($column, $query->variables)) {
97
            return $this->wrap($column);
98 145
        }
99
100
101
        $column = $this->convertJsonFields($column);
102 4
        $column = $query->convertIdToKey($column);
103
104 4
        //We check for an existing alias to determine of the first reference is a table.
105 4
        // In which case we replace it with the alias.
106 4
        return $this->wrap($this->normalizeColumnReferences($query, $column, $table));
107
    }
108 4
109 4
    /**
110
     * @param string $column
111 4
     * @return array<mixed>
112
     * @throws Exception
113
     */
114
    protected function normalizeStringColumn(Builder $query, int|string $key, string $column, string $table = null): array
115
    {
116
        [$column, $alias] = $query->extractAlias($column, $key);
117
118
        $column = $query->convertIdToKey($column);
119
120
        $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

120
        $column = $this->wrap($this->normalizeColumnReferences($query, /** @scrutinizer ignore-type */ $column, $table));
Loading history...
121
122
        return [$column, $alias];
123
    }
124
125
126
    /**
127
     * @param Builder $builder
128
     * @param string $column
129
     * @param string|null $table
130
     * @return string
131
     */
132
    protected function normalizeColumnReferences(IlluminateQueryBuilder $query, string $column, string $table = null): string
133
    {
134
        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...
135
            $table = $query->from;
136
        }
137
138
        $references = explode('.', $column);
139
140
        $tableAlias = $query->getTableAlias($references[0]);
141
        if (isset($tableAlias)) {
142
            $references[0] = $tableAlias;
143
        }
144
145
        if ($tableAlias === null && $table != null && !$query->isTableAlias($references[0])) {
146
            $tableAlias = $query->generateTableAlias($table);
147
            array_unshift($references, $tableAlias);
148
        }
149
150
        return implode('.', $references);
151
    }
152
153
154
    protected function determineReturnValues($query, $returnAttributes = [], $returnDocs = []): string
155
    {
156
        $values = $this->mergeReturnAttributes($returnAttributes, $returnDocs);
157
158
        $values = $this->mergeReturnDocs($values, $returnAttributes, $returnDocs);
159
160
        if ($query->aggregate !== null) {
161
            $values = ['`aggregate`' => 'aggregateResult'];
162
        }
163
164
        if (empty($values)) {
165
            $values = $query->getTableAlias($query->from);
166
            if (is_array($query->joins) && !empty($query->joins)) {
167
                $values = $this->mergeJoinResults($query, $values);
168
            }
169
        }
170
171
        if (is_array($values)) {
172
            $values = $this->generateAqlObject($values);
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

172
            /** @scrutinizer ignore-call */ 
173
            $values = $this->generateAqlObject($values);
Loading history...
173
        }
174
175
        return $values;
176
    }
177
178
    protected function formatReturnData($values)
179
    {
180
        $object = "";
181
        foreach ($values as $key => $value) {
182
            if ($object !== "") {
183
                $object .= ", ";
184
            }
185
            if (is_array($value)) {
186
                $value = $this->formatReturnData($value);
187
            }
188
189
            if (array_is_list($values)) {
190
                $object .= $value;
191
                continue;
192
            }
193
194
            $object .= $key . ': ' . $value;
195
        }
196
197
        if (array_is_list($values)) {
198
            return '[' . $object . ']';
199
        }
200
        return '{' . $object . '}';
201
    }
202
203
    protected function mergeReturnAttributes($returnAttributes, $returnDocs)
204
    {
205
        $values = [];
206
        if (!empty($returnAttributes)) {
207
            $values = $returnAttributes;
208
        }
209
210
        // If there is just one attribute/column given we assume that you want a list of values
211
        //  instead of a list of objects
212
        if (count($returnAttributes) == 1 && empty($returnDocs)) {
213
            $values = reset($returnAttributes);
214
        }
215
216
        return $values;
217
    }
218
219
    protected function mergeReturnDocs($values, $returnAttributes, $returnDocs)
220
    {
221
        if (!empty($returnAttributes) && !empty($returnDocs)) {
222
            $returnDocs[] = $returnAttributes;
223
        }
224
225
        if (!empty($returnDocs)) {
226
            $returnDocString = implode(', ', $returnDocs);
227
            $values = `MERGE($returnDocString)`;
228
        }
229
        return $values;
230
    }
231
232
    protected function mergeJoinResults(IlluminateQueryBuilder $query, $baseTable): string
233
    {
234
        $tablesToJoin = [];
235
        foreach ($query->joins as $key => $join) {
236
            $tableAlias = $query->getTableAlias($join->table);
237
238
            if (!isset($tableAlias)) {
239
                $tableAlias = $query->generateTableAlias($join->table);
240
            }
241
            $tablesToJoin[$key] = $tableAlias;
242
        }
243
244
        $tablesToJoin = array_reverse($tablesToJoin);
245
        $tablesToJoin[] = $baseTable;
246
247
        return 'MERGE(' . implode(', ', $tablesToJoin) . ')';
248
    }
249
}
250