Passed
Push — next ( f287d6...dbfc9f )
by Bas
12:00
created

CompilesColumns::mergeReturnAttributes()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 14
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 4

Importance

Changes 2
Bugs 0 Features 0
Metric Value
eloc 6
c 2
b 0
f 0
dl 0
loc 14
ccs 7
cts 7
cp 1
rs 10
cc 4
nc 4
nop 2
crap 4
1
<?php
2
3
declare(strict_types=1);
4
5
namespace LaravelFreelancerNL\Aranguent\Query\Concerns;
6
7
use Exception;
8
use Illuminate\Support\Arr;
9
use LaravelFreelancerNL\Aranguent\Query\Builder;
10
use LaravelFreelancerNL\FluentAQL\Expressions\FunctionExpression;
11
use LaravelFreelancerNL\FluentAQL\QueryBuilder;
12
13
trait CompilesColumns
14
{
15
    /**
16
     * Compile the "RETURN" portion of the query.
17
     *
18
     * @param Builder $builder
19
     * @param array $columns
20
     *
21
     * @return Builder
22
     * @throws Exception
23
     * @SuppressWarnings(PHPMD.CyclomaticComplexity)
24
     */
25 158
    protected function compileColumns(Builder $builder, array $columns): Builder
26
    {
27 158
        $returnDocs = [];
28 158
        $returnAttributes = [];
29
30
        // Prepare columns
31 158
        foreach ($columns as $key => $column) {
32
            // Extract rows
33 158
            if (is_string($column) && substr($column, strlen($column) - 2)  === '.*') {
34 14
                $table = substr($column, 0, strlen($column) - 2);
35 14
                $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

35
                /** @scrutinizer ignore-call */ 
36
                $returnDocs[] = $this->getTableAlias($table);
Loading history...
36
37 14
                continue;
38
            }
39
40
            // Extract groups
41 158
            if (is_array($builder->groups) && in_array($column, $builder->groups)) {
42 5
                $returnAttributes[$key] = $column;
43
44 5
                continue;
45
            }
46
47 153
            if (is_string($column) && $column != null && $column != '*') {
48 43
                [$column, $alias] = $this->normalizeStringColumn($builder, $key, $column);
49
50 43
                if (isset($returnAttributes[$alias]) && is_array($column)) {
51 4
                    $returnAttributes[$alias] = array_merge_recursive(
52 4
                        $returnAttributes[$alias],
53 4
                        $this->normalizeColumn($builder, $column)
54
                    );
55 4
                    continue;
56
                }
57
58 43
                $returnAttributes[$alias] = $this->normalizeColumn($builder, $column);
59
            }
60
        }
61
62 158
        $values = $this->determineReturnValues($builder, $returnAttributes, $returnDocs);
63
64 158
        $builder->aqb = $builder->aqb->return($values, (bool) $builder->distinct);
0 ignored issues
show
Bug introduced by
The method return() does not exist on LaravelFreelancerNL\Flue...ions\FunctionExpression. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

64
        /** @scrutinizer ignore-call */ 
65
        $builder->aqb = $builder->aqb->return($values, (bool) $builder->distinct);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
65
66 158
        return $builder;
67
    }
68
69
    /**
70
     * @throws Exception
71
     */
72 134
    protected function normalizeColumn(Builder $builder, mixed $column, string $table = null): mixed
73
    {
74 134
        if ($column instanceof QueryBuilder || $column instanceof FunctionExpression) {
75 3
            return $column;
76
        }
77
78 133
        $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

78
        /** @scrutinizer ignore-call */ 
79
        $column = $this->convertColumnId($column);
Loading history...
79
80 133
        if (is_array($builder->groups) && in_array($column, $builder->groups)) {
81 3
            return $column;
82
        }
83
84 130
        if (is_array($column)) {
85 5
            foreach ($column as $key => $value) {
86 5
                $column[$key] = $this->normalizeColumn($builder, $value, $table);
87
            }
88 5
            return $column;
89
        }
90
91 130
        if (key_exists($column, $builder->variables)) {
92 1
            return $column;
93
        }
94
95
        //We check for an existing alias to determine of the first reference is a table.
96
        // In which case we replace it with the alias.
97 130
        return $this->normalizeColumnReferences($builder, $column, $table);
98
    }
99
100
    /**
101
     * @param array<mixed> $column
102
     * @return array<mixed>
103
     * @throws Exception
104
     */
105 43
    protected function normalizeStringColumn(Builder $builder, int|string $key, mixed $column): array
106
    {
107 43
        [$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

107
        /** @scrutinizer ignore-call */ 
108
        [$column, $alias] = $this->extractAlias($column, $key);
Loading history...
108
109 43
        if (! isDotString($alias)) {
110 43
            $column = $this->normalizeColumn($builder, $column);
111
112 43
            return [$column, $alias];
113
        }
114
115 5
        $column = Arr::undot([$column => $column]);
116 5
        $alias = array_key_first($column);
117 5
        $column = $column[$alias];
118 5
        return [$column, $alias];
119
    }
120
121
122
    /**
123
     * @param Builder $builder
124
     * @param string $column
125
     * @param string|null $table
126
     * @return string
127
     */
128 130
    protected function normalizeColumnReferences(Builder $builder, string $column, string $table = null): string
129
    {
130 130
        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...
131 130
            $table = $builder->from;
132
        }
133
134
        // Replace SQL JSON arrow for AQL dot
135 130
        $column = str_replace('->', '.', $column);
136
137 130
        $references = explode('.', $column);
138
139
140 130
        $tableAlias = $this->getTableAlias($references[0]);
141 130
        if (isset($tableAlias)) {
142 58
            $references[0] = $tableAlias;
143
        }
144
145 130
        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

145
        if ($tableAlias === null && $table != null && ! $this->/** @scrutinizer ignore-call */ isTableAlias($references[0])) {
Loading history...
146 98
            $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

146
            /** @scrutinizer ignore-call */ 
147
            $tableAlias = $this->generateTableAlias($table);
Loading history...
147 98
            array_unshift($references, $tableAlias);
148
        }
149
150 130
        return implode('.', $references);
151
    }
152
153
154 158
    protected function determineReturnValues($builder, $returnAttributes = [], $returnDocs = [])
155
    {
156 158
        $values = $this->mergeReturnAttributes($returnAttributes, $returnDocs);
157
158 158
        $values = $this->mergeReturnDocs($values, $builder, $returnAttributes, $returnDocs);
159
160 158
        if ($builder->aggregate !== null) {
161 24
            $values = ['aggregate' => 'aggregateResult'];
162
        }
163
164 158
        if (empty($values)) {
165 121
            $values = $this->getTableAlias($builder->from);
166 121
            if (is_array($builder->joins) && !empty($builder->joins)) {
167 4
                $values = $this->mergeJoinResults($builder, $values);
168
            }
169
        }
170
171 158
        return $values;
172
    }
173
174 158
    protected function mergeReturnAttributes($returnAttributes, $returnDocs)
175
    {
176 158
        $values = [];
177 158
        if (! empty($returnAttributes)) {
178 48
            $values = $returnAttributes;
179
        }
180
181
        // If there is just one attribute/column given we assume that you want a list of values
182
        //  instead of a list of objects
183 158
        if (count($returnAttributes) == 1 && empty($returnDocs)) {
184 16
            $values = reset($returnAttributes);
185
        }
186
187 158
        return $values;
188
    }
189
190 158
    protected function mergeReturnDocs($values, $builder, $returnAttributes, $returnDocs)
191
    {
192 158
        if (! empty($returnAttributes) && ! empty($returnDocs)) {
193 14
            $returnDocs[] = $returnAttributes;
194
        }
195
196 158
        if (! empty($returnDocs)) {
197 14
            $values = $builder->aqb->merge(...$returnDocs);
198
        }
199 158
        return $values;
200
    }
201
202
203 4
    protected function mergeJoinResults($builder, $baseTable)
204
    {
205 4
        $tablesToJoin = [];
206 4
        foreach ($builder->joins as $key => $join) {
207 4
            $tableAlias = $this->getTableAlias($join->table);
208
209 4
            if (! isset($tableAlias)) {
210 1
                $tableAlias = $this->generateTableAlias($join->table);
211
            }
212 4
            $tablesToJoin[$key] = $tableAlias;
213
        }
214
215 4
        $tablesToJoin = array_reverse($tablesToJoin);
216 4
        $tablesToJoin[] = $baseTable;
217
218 4
        return $builder->aqb->merge(...$tablesToJoin);
219
    }
220
}
221