HandlesAliases   A
last analyzed

Complexity

Total Complexity 40

Size/Duplication

Total Lines 208
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 70
dl 0
loc 208
rs 9.2
c 1
b 0
f 0
wmc 40

14 Methods

Rating   Name   Duplication   Size   Complexity  
A extractAlias() 0 19 6
A generateTableAlias() 0 6 2
A convertColumnId() 0 8 2
A getTableAlias() 0 15 4
A getTableAliases() 0 3 1
A exchangeTableAliases() 0 6 1
A registerColumnAlias() 0 13 4
A getColumnAlias() 0 7 2
A isTableAlias() 0 3 1
B registerTableAlias() 0 29 10
A importTableAliases() 0 8 2
A replaceTableForAlias() 0 12 2
A isTable() 0 3 1
A prefixAlias() 0 10 2

How to fix   Complexity   

Complex Class

Complex classes like HandlesAliases often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use HandlesAliases, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
declare(strict_types=1);
4
5
namespace LaravelFreelancerNL\Aranguent\Query\Concerns;
6
7
use Exception;
8
use Illuminate\Database\Eloquent\Builder as IlluminateEloquentBuilder;
9
use Illuminate\Database\Eloquent\Relations\Relation;
10
use Illuminate\Database\Query\Expression;
11
use Illuminate\Support\Str;
12
use Illuminate\Database\Query\Builder as IlluminateQueryBuilder;
13
use LaravelFreelancerNL\Aranguent\Query\Builder;
14
15
trait HandlesAliases
16
{
17
    /**
18
     * @var array<string, Expression|string>
19
     */
20
    public array $tableAliases = [];
21
22
    /**
23
     * @var array<string, Expression|string>
24
     */
25
    public array $columnAliases = [];
26
27
    /**
28
     * @param  array<mixed>|string  $column
29
     * @return array<mixed>|string
30
     */
31
    public function convertColumnId(array|string|Expression $column): array|string|Expression
32
    {
33
34
        if ($column instanceof Expression) {
0 ignored issues
show
introduced by
$column is never a sub-type of Illuminate\Database\Query\Expression.
Loading history...
35
            return $column;
36
        }
37
38
        return $this->convertIdToKey($column);
0 ignored issues
show
Bug introduced by
It seems like convertIdToKey() 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

38
        return $this->/** @scrutinizer ignore-call */ convertIdToKey($column);
Loading history...
39
    }
40
41
    /**
42
     * Extract table and alias from sql alias notation (entity AS `alias`)
43
     *
44
     * @return array<int|string, Expression|string>
45
     *
46
     * @throws Exception
47
     */
48
    public function extractAlias(string $entity, int|null|string $key = null): array
49
    {
50
        $results = preg_split("/\sas\s/i", $entity);
51
52
        if ($results === false) {
53
            throw new Exception('Column splitting failed');
54
        }
55
56
        if (isset($results[1])) {
57
            $results[1] = trim($results[1], '`');
58
        }
59
        if (!isset($results[1]) && is_string($key)) {
60
            $results[1] = $key;
61
        }
62
        if (!isset($results[1])) {
63
            $results[1] = $results[0];
64
        }
65
66
        return $results;
67
    }
68
69
    public function generateTableAlias(string|Expression $table, string $postfix = 'Doc'): string
70
    {
71
        if ($table instanceof Expression) {
72
            return 'Expression' . spl_object_id($table);
73
        }
74
        return Str::camel(Str::singular($table)) . $postfix;
75
    }
76
77
    public function getTableAlias(string|Expression $table): float|int|null|string
78
    {
79
        if ($table instanceof Expression) {
80
            $table = 'Expression' . spl_object_id($table);
81
        }
82
83
        if ($this->isTableAlias($table)) {
84
            return $table;
85
        }
86
87
        if (!isset($this->tableAliases[$table])) {
88
            return null;
89
        }
90
91
        return $this->grammar->getValue($this->tableAliases[$table]);
92
    }
93
94
    public function getColumnAlias(string $column): Expression|null|string
95
    {
96
        if (isset($this->columnAliases[$column])) {
97
            return $this->columnAliases[$column];
98
        }
99
100
        return null;
101
    }
102
103
    /**
104
     * @return array<string, Expression|string>
105
     */
106
    public function getTableAliases(): array
107
    {
108
        return $this->tableAliases;
109
    }
110
111
    /**
112
     * @param array<string, Expression|string>|IlluminateQueryBuilder $aliases
113
     * @return void
114
     */
115
    public function importTableAliases(array|IlluminateQueryBuilder $aliases): void
116
    {
117
        if ($aliases instanceof IlluminateQueryBuilder) {
0 ignored issues
show
introduced by
$aliases is never a sub-type of Illuminate\Database\Query\Builder.
Loading history...
118
            assert($aliases instanceof Builder);
119
            $aliases = $aliases->getTableAliases();
120
        }
121
122
        $this->tableAliases = array_merge($this->tableAliases, $aliases);
123
    }
124
125
    /**
126
     * @param IlluminateEloquentBuilder|IlluminateQueryBuilder|Relation $query
127
     * @return void
128
     */
129
    public function exchangeTableAliases($query): void
130
    {
131
        assert($query instanceof Builder);
132
133
        $this->importTableAliases($query);
134
        $query->importTableAliases($this);
0 ignored issues
show
Bug introduced by
$this of type LaravelFreelancerNL\Aran...Concerns\HandlesAliases is incompatible with the type Illuminate\Database\Query\Builder|array expected by parameter $aliases of LaravelFreelancerNL\Aran...r::importTableAliases(). ( Ignorable by Annotation )

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

134
        $query->importTableAliases(/** @scrutinizer ignore-type */ $this);
Loading history...
135
    }
136
137
    public function isTableAlias(string $value): bool
138
    {
139
        return in_array($value, $this->tableAliases);
140
    }
141
142
    public function isTable(string $value): bool
143
    {
144
        return array_key_exists($value, $this->tableAliases);
145
    }
146
147
    public function prefixAlias(string $target, string $value): string
148
    {
149
        /** @phpstan-ignore-next-line */
150
        $alias =  $this->grammar->getValue($this->getTableAlias($target));
151
152
        if (Str::startsWith($value, $alias . '.')) {
153
            return $value;
154
        }
155
156
        return $alias . '.' . $value;
157
    }
158
159
    /**
160
     * @throws Exception
161
     */
162
    public function registerColumnAlias(string $column, ?string $alias = null): bool
163
    {
164
        if (preg_match("/\sas\s/i", $column)) {
165
            [$column, $alias] = $this->extractAlias($column);
166
        }
167
168
        if (isset($alias) && !$column instanceof Expression) {
169
            $this->columnAliases[$column] = $alias;
170
171
            return true;
172
        }
173
174
        return false;
175
    }
176
177
    /**
178
     * @SuppressWarnings("PHPMD.CyclomaticComplexity")
179
     */
180
    public function registerTableAlias(string|Expression $table, ?string $alias = null): string
181
    {
182
        if ($table instanceof Expression  && $alias !== null) {
183
            $table = 'Expression' . spl_object_id($table);
184
        }
185
186
        if ($table instanceof Expression && $alias === null) {
187
            $table = 'Expression' . spl_object_id($table);
188
            $alias = $table;
189
        }
190
191
        /** @phpstan-ignore-next-line  */
192
        if ($alias == null && is_string($table) && stripos($table, ' as ') !== false) {
0 ignored issues
show
Bug introduced by
It seems like you are loosely comparing $alias of type null|string against null; this is ambiguous if the string can be empty. Consider using a strict comparison === instead.
Loading history...
193
            $tableParts = [];
194
195
            if (preg_match("/(^.*) as (.*?)$/", $table, $tableParts)) {
196
                $table = $tableParts[1];
197
                $alias = $tableParts[2];
198
            }
199
        }
200
201
        if ($alias == null) {
202
            $alias = $this->generateTableAlias($table);
203
        }
204
205
        /** @phpstan-ignore-next-line  */
206
        $this->tableAliases[$table] = $alias;
207
208
        return $alias;
209
    }
210
211
    public function replaceTableForAlias(string $reference): string
212
    {
213
        $referenceParts = explode('.', $reference);
214
        $first = array_shift($referenceParts);
215
        /** @phpstan-ignore-next-line */
216
        $alias = $this->grammar->getValue($this->getTableAlias($first));
217
        if ($alias == null) {
218
            $alias = $first;
219
        }
220
        array_unshift($referenceParts, $alias);
221
222
        return implode('.', $referenceParts);
223
    }
224
}
225