Passed
Pull Request — next (#84)
by Bas
15:17 queued 11:12
created

HandlesAliases::generateTableAlias()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 2.0625

Importance

Changes 0
Metric Value
cc 2
eloc 3
c 0
b 0
f 0
nc 2
nop 2
dl 0
loc 6
ccs 3
cts 4
cp 0.75
crap 2.0625
rs 10
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 72
    public function extractAlias(string $entity, int|string $key = null): array
49
    {
50 72
        $results = preg_split("/\sas\s/i", $entity);
51
52 72
        if ($results === false) {
53
            throw new Exception('Column splitting failed');
54
        }
55
56 72
        if (isset($results[1])) {
57 19
            $results[1] = trim($results[1], '`');
58
        }
59 72
        if (!isset($results[1]) && is_string($key)) {
60 2
            $results[1] = $key;
61
        }
62 72
        if (!isset($results[1])) {
63 58
            $results[1] = $results[0];
64
        }
65
66 72
        return $results;
67
    }
68
69 255
    public function generateTableAlias(string|Expression $table, string $postfix = 'Doc'): string
70
    {
71 255
        if ($table instanceof Expression) {
72
            return 'Expression' . spl_object_id($table);
73
        }
74 255
        return Str::camel(Str::singular($table)) . $postfix;
75
    }
76
77 252
    public function getTableAlias(string|Expression $table): float|int|null|string
78
    {
79 252
        if ($table instanceof Expression) {
80 1
            $table = 'Expression' . spl_object_id($table);
81
        }
82
83 252
        if (!isset($this->tableAliases[$table])) {
84 187
            return null;
85
        }
86
87 210
        return $this->grammar->getValue($this->tableAliases[$table]);
88
    }
89
90
    public function getColumnAlias(string $column): Expression|null|string
91
    {
92
        if (isset($this->columnAliases[$column])) {
93
            return $this->columnAliases[$column];
94
        }
95
96
        return null;
97
    }
98
99
    /**
100
     * @return array<string, Expression|string>
101
     */
102 36
    public function getTableAliases(): array
103
    {
104 36
        return $this->tableAliases;
105
    }
106
107
    /**
108
     * @param array<string, Expression|string>|IlluminateQueryBuilder $aliases
109
     * @return void
110
     */
111 36
    public function importTableAliases(array|IlluminateQueryBuilder $aliases): void
112
    {
113 36
        if ($aliases instanceof IlluminateQueryBuilder) {
0 ignored issues
show
introduced by
$aliases is never a sub-type of Illuminate\Database\Query\Builder.
Loading history...
114
            assert($aliases instanceof Builder);
115 36
            $aliases = $aliases->getTableAliases();
116
        }
117
118 36
        $this->tableAliases = array_merge($this->tableAliases, $aliases);
119
    }
120
121
    /**
122
     * @param IlluminateEloquentBuilder|IlluminateQueryBuilder|Relation $query
123
     * @return void
124
     */
125 18
    public function exchangeTableAliases($query): void
126
    {
127
        assert($query instanceof Builder);
128
129 18
        $this->importTableAliases($query);
130 18
        $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

130
        $query->importTableAliases(/** @scrutinizer ignore-type */ $this);
Loading history...
131
    }
132
133
    public function isTableAlias(string $value): bool
134
    {
135
        return in_array($value, $this->tableAliases);
136
    }
137
138 8
    public function isTable(string $value): bool
139
    {
140 8
        return array_key_exists($value, $this->tableAliases);
141
    }
142
143
    public function prefixAlias(string $target, string $value): string
144
    {
145
        /** @phpstan-ignore-next-line */
146
        $alias =  $this->grammar->getValue($this->getTableAlias($target));
147
148
        if (Str::startsWith($value, $alias . '.')) {
149
            return $value;
150
        }
151
152
        return $alias . '.' . $value;
153
    }
154
155
    /**
156
     * @throws Exception
157
     */
158
    public function registerColumnAlias(string $column, string $alias = null): bool
159
    {
160
        if (preg_match("/\sas\s/i", $column)) {
161
            [$column, $alias] = $this->extractAlias($column);
162
        }
163
164
        if (isset($alias) && !$column instanceof Expression) {
165
            $this->columnAliases[$column] = $alias;
166
167
            return true;
168
        }
169
170
        return false;
171
    }
172
173 255
    public function registerTableAlias(string|Expression $table, string $alias = null): string
174
    {
175 255
        if ($table instanceof Expression  && $alias !== null) {
176 3
            $table = 'Expression' . spl_object_id($table);
177
        }
178
179 255
        if ($table instanceof Expression && $alias === null) {
180 3
            $table = 'Expression' . spl_object_id($table);
181 3
            $alias = $table;
182
        }
183
184
        /** @phpstan-ignore-next-line  */
185 255
        if ($alias == null && 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...
Bug introduced by
It seems like $table can also be of type Illuminate\Database\Query\Expression; however, parameter $haystack of stripos() 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

185
        if ($alias == null && stripos(/** @scrutinizer ignore-type */ $table, ' as ') !== false) {
Loading history...
186
            $tableParts = [];
187
            /** @phpstan-ignore-next-line  */
188
            preg_match("/(^.*) as (.*?)$/", $table, $tableParts);
0 ignored issues
show
Bug introduced by
It seems like $table can also be of type Illuminate\Database\Query\Expression; however, parameter $subject of preg_match() 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

188
            preg_match("/(^.*) as (.*?)$/", /** @scrutinizer ignore-type */ $table, $tableParts);
Loading history...
189
            $table = $tableParts[1];
190
            $alias = $tableParts[2];
191
        }
192
193 255
        if ($alias == null) {
194 255
            $alias = $this->generateTableAlias($table);
195
        }
196
197
        /** @phpstan-ignore-next-line  */
198 255
        $this->tableAliases[$table] = $alias;
199
200 255
        return $alias;
201
    }
202
203
    public function replaceTableForAlias(string $reference): string
204
    {
205
        $referenceParts = explode('.', $reference);
206
        $first = array_shift($referenceParts);
207
        /** @phpstan-ignore-next-line */
208
        $alias = $this->grammar->getValue($this->getTableAlias($first));
209
        if ($alias == null) {
210
            $alias = $first;
211
        }
212
        array_unshift($referenceParts, $alias);
213
214
        return implode('.', $referenceParts);
215
    }
216
}
217