Failed Conditions
Push — refactor/improve-static-analys... ( 740b03...f2a08f )
by Bas
06:58 queued 12s
created

CompilesColumns::mergeReturnAttributes()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 14
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 20

Importance

Changes 2
Bugs 0 Features 0
Metric Value
eloc 6
c 2
b 0
f 0
dl 0
loc 14
ccs 0
cts 0
cp 0
rs 10
cc 4
nc 4
nop 2
crap 20
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
     */
21 145
    protected function compileColumns(IlluminateQueryBuilder $query, $columns)
22
    {
23
        assert($query instanceof Builder);
24 145
25
        $returnDocs = [];
26 145
        $returnAttributes = [];
27 12
28 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

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

87
            return $this->/** @scrutinizer ignore-call */ wrap($query->convertIdToKey($column));
Loading history...
88
        }
89 145
90
        if (is_array($column)) {
91 145
            $column = $this->convertJsonFields($column);
92 12
93
            foreach ($column as $key => $value) {
94
                $column[$key] = $this->normalizeColumn($query, $value, $table);
95 145
            }
96 12
            return $column;
97
        }
98 145
99
        if (key_exists($column, $query->variables)) {
100
            return $this->wrap($column);
101
        }
102 4
103
104 4
        $column = $this->convertJsonFields($column);
105 4
        $column = $query->convertIdToKey($column);
106 4
107
        //We check for an existing alias to determine of the first reference is a table.
108 4
        // In which case we replace it with the alias.
109 4
        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

109
        return $this->wrap($this->normalizeColumnReferences($query, /** @scrutinizer ignore-type */ $column, $table));
Loading history...
110
    }
111 4
112
    /**
113
     * @param string $column
114
     * @return array<mixed>
115
     * @throws Exception
116
     */
117
    protected function normalizeStringColumn(Builder $query, int|string $key, string $column, string $table = null): array
118
    {
119
        [$column, $alias] = $query->extractAlias($column, $key);
120
121
        $column = $query->convertIdToKey($column);
122
123
        $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

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

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