Failed Conditions
Push — refactor/improve-static-analys... ( 8da3ef...a7b39f )
by Bas
09:53
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 IlluminateBuilder;
9
use Illuminate\Support\Arr;
10
use LaravelFreelancerNL\Aranguent\Query\Builder;
11
use LaravelFreelancerNL\FluentAQL\Expressions\FunctionExpression;
12
use LaravelFreelancerNL\FluentAQL\QueryBuilder;
13
14
trait CompilesColumns
15
{
16
    /**
17 145
     * Compile the "select *" portion of the query.
18
     *
19 145
     * @SuppressWarnings(PHPMD.CyclomaticComplexity)
20 145
     *
21 145
     * @param IlluminateBuilder $query
22
     * @param array<mixed> $columns
23
     * @return string|null
24 145
     * @throws Exception
25
     */
26 145
    protected function compileColumns(IlluminateBuilder $query, $columns)
27 12
    {
28 12
        $returnDocs = [];
29
        $returnAttributes = [];
30 12
31
        // Prepare columns
32
        foreach ($columns as $key => $column) {
33
            // Extract rows
34 145
            if (is_string($column) && substr($column, strlen($column) - 2)  === '.*') {
35 5
                $table = substr($column, 0, strlen($column) - 2);
36
                $returnDocs[] = $this->getTableAlias($table);
0 ignored issues
show
Bug introduced by
It seems like getTableAlias() 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

36
                /** @scrutinizer ignore-call */ 
37
                $returnDocs[] = $this->getTableAlias($table);
Loading history...
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] = $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
59 145
                $returnAttributes[$alias] = $this->normalizeColumn($query, $column);
60 24
            }
61
        }
62
63 145
        $values = $this->determineReturnValues($query, $returnAttributes, $returnDocs);
64 118
65 118
        $aql = 'RETURN';
66 4
        if ((bool) $query->distinct) {
67
            $aql .= ' DISTINCT';
68
        }
69
70 145
        if (! is_string($values)) {
71
            $values = json_encode($values);
72
        }
73 145
74
        $aql .= ' ' . $values;
75 145
76 145
        return $aql;
77 39
    }
78
79
    /**
80
     * @throws Exception
81
     */
82 145
    protected function normalizeColumn(Builder $query, mixed $column, string $table = null): mixed
83 17
    {
84
        if ($column instanceof QueryBuilder || $column instanceof FunctionExpression) {
85
            return $column;
86 145
        }
87
88
        $column = $this->convertColumnId($column);
0 ignored issues
show
Bug introduced by
It seems like convertColumnId() 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
        /** @scrutinizer ignore-call */ 
89
        $column = $this->convertColumnId($column);
Loading history...
89 145
90
        if (
91 145
            is_array($query->groups)
92 12
            && in_array($column, $query->groups)
93
            && debug_backtrace(DEBUG_BACKTRACE_PROVIDE_OBJECT)[1]['function'] !== "compileGroups"
94
        ) {
95 145
            return $column;
96 12
        }
97
98 145
        if (is_array($column)) {
99
            foreach ($column as $key => $value) {
100
                $column[$key] = $this->normalizeColumn($query, $value, $table);
101
            }
102 4
            return $column;
103
        }
104 4
105 4
        if (key_exists($column, $query->variables)) {
106 4
            return $column;
107
        }
108 4
109 4
        //We check for an existing alias to determine of the first reference is a table.
110
        // In which case we replace it with the alias.
111 4
        return $this->normalizeColumnReferences($query, $column, $table);
112
    }
113
114
    /**
115
     * @param array<mixed> $column
116
     * @return array<mixed>
117
     * @throws Exception
118
     */
119
    protected function normalizeStringColumn(Builder $query, int|string $key, mixed $column): array
120
    {
121
        [$column, $alias] = $this->extractAlias($column, $key);
0 ignored issues
show
Bug introduced by
It seems like extractAlias() 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

121
        /** @scrutinizer ignore-call */ 
122
        [$column, $alias] = $this->extractAlias($column, $key);
Loading history...
122
123
        if (! isDotString($alias)) {
124
            $column = $this->normalizeColumn($query, $column);
125
126
            return [$column, $alias];
127
        }
128
129
        $column = Arr::undot([$column => $column]);
130
        $alias = array_key_first($column);
131
        $column = $column[$alias];
132
        return [$column, $alias];
133
    }
134
135
136
    /**
137
     * @param Builder $query
138
     * @param string $column
139
     * @param string|null $table
140
     * @return string
141
     */
142
    protected function normalizeColumnReferences(Builder $query, string $column, string $table = null): string
143
    {
144
        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...
145
            $table = $query->from;
146
        }
147
148
        // Replace SQL JSON arrow for AQL dot
149
        $column = str_replace('->', '.', $column);
150
151
        $references = explode('.', $column);
152
153
154
        $tableAlias = $this->getTableAlias($references[0]);
155
        if (isset($tableAlias)) {
156
            $references[0] = $tableAlias;
157
        }
158
159
        if ($tableAlias === null && $table != null && ! $this->isTableAlias($references[0])) {
0 ignored issues
show
Bug introduced by
It seems like isTableAlias() 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

159
        if ($tableAlias === null && $table != null && ! $this->/** @scrutinizer ignore-call */ isTableAlias($references[0])) {
Loading history...
160
            $tableAlias = $this->generateTableAlias($table);
0 ignored issues
show
Bug introduced by
It seems like generateTableAlias() 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

160
            /** @scrutinizer ignore-call */ 
161
            $tableAlias = $this->generateTableAlias($table);
Loading history...
161
            array_unshift($references, $tableAlias);
162
        }
163
        return implode('.', $references);
164
    }
165
166
167
    protected function determineReturnValues($query, $returnAttributes = [], $returnDocs = [])
168
    {
169
        $values = $this->mergeReturnAttributes($returnAttributes, $returnDocs);
170
171
        $values = $this->mergeReturnDocs($values, $query, $returnAttributes, $returnDocs);
172
173
        if ($query->aggregate !== null) {
174
            $values = ['aggregate' => 'aggregateResult'];
175
        }
176
177
        if (empty($values)) {
178
            $values = $this->getTableAlias($query->from);
179
            if (is_array($query->joins) && !empty($query->joins)) {
180
                $values = $this->mergeJoinResults($query, $values);
181
            }
182
        }
183
184
        return $values;
185
    }
186
187
    protected function mergeReturnAttributes($returnAttributes, $returnDocs)
188
    {
189
        $values = [];
190
        if (! empty($returnAttributes)) {
191
            $values = $returnAttributes;
192
        }
193
194
        // If there is just one attribute/column given we assume that you want a list of values
195
        //  instead of a list of objects
196
        if (count($returnAttributes) == 1 && empty($returnDocs)) {
197
            $values = reset($returnAttributes);
198
        }
199
200
        return $values;
201
    }
202
203
    protected function mergeReturnDocs($values, $query, $returnAttributes, $returnDocs)
204
    {
205
        if (! empty($returnAttributes) && ! empty($returnDocs)) {
206
            $returnDocs[] = $returnAttributes;
207
        }
208
209
        if (! empty($returnDocs)) {
210
            $values = $query->aqb->merge(...$returnDocs);
211
        }
212
        return $values;
213
    }
214
215
216
    protected function mergeJoinResults($query, $baseTable)
217
    {
218
        $tablesToJoin = [];
219
        foreach ($query->joins as $key => $join) {
220
            $tableAlias = $this->getTableAlias($join->table);
221
222
            if (! isset($tableAlias)) {
223
                $tableAlias = $this->generateTableAlias($join->table);
224
            }
225
            $tablesToJoin[$key] = $tableAlias;
226
        }
227
228
        $tablesToJoin = array_reverse($tablesToJoin);
229
        $tablesToJoin[] = $baseTable;
230
231
        return $query->aqb->merge(...$tablesToJoin);
232
    }
233
}
234