Failed Conditions
Push — refactor/improve-static-analys... ( 40018b...b52513 )
by Bas
06:54
created

CompilesColumns::formatReturnData()   A

Complexity

Conditions 6
Paths 18

Size

Total Lines 23
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 42

Importance

Changes 0
Metric Value
eloc 13
c 0
b 0
f 0
dl 0
loc 23
ccs 0
cts 0
cp 0
rs 9.2222
cc 6
nc 18
nop 1
crap 42
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
        assert($query instanceof Builder);
25
26 145
        $returnDocs = [];
27 12
        $returnAttributes = [];
28 12
29
        $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

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

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

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

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

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