Passed
Push — feature/streamline-schema-inde... ( 97bf6c )
by Bas
11:35 queued 06:44
created

Indexes::indexCommand()   A

Complexity

Conditions 5
Paths 8

Size

Total Lines 23
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 9
CRAP Score 5.025

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 5
eloc 9
nc 8
nop 4
dl 0
loc 23
ccs 9
cts 10
cp 0.9
crap 5.025
rs 9.6111
c 1
b 0
f 0
1
<?php
2
3
namespace LaravelFreelancerNL\Aranguent\Schema\Concerns;
4
5
use ArangoClient\Exceptions\ArangoException;
6
use Illuminate\Support\Fluent;
7
8
trait Indexes
9
{
10
    /**
11
     * Add a new index command to the blueprint.
12
     *
13
     * @param  string|array<string>|null  $columns
14
     */
15 21
    protected function indexCommand(
16
        string $type = '',
17
        array|string $columns = null,
18
        string $name = null,
19
        array $indexOptions = []
20
    ): Fluent {
21 21
        if ($type == '') {
22
            $type = $this->mapIndexAlgorithm('persistent');
23
        }
24
25 21
        if ($columns === null) {
26 15
            $columns = end($this->columns);
27
        }
28
29 21
        if (is_string($columns)) {
30 15
            $columns = [$columns];
31
        }
32
33 21
        $columns = $this->renameIdField($columns);
0 ignored issues
show
Bug introduced by
It seems like renameIdField() 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

33
        /** @scrutinizer ignore-call */ 
34
        $columns = $this->renameIdField($columns);
Loading history...
34
35 21
        $indexOptions['name'] = $name ?: $this->createIndexName($type, $columns, $indexOptions);
36
37 21
        return $this->addCommand('index', compact('type', 'columns', 'indexOptions'));
0 ignored issues
show
Bug introduced by
It seems like addCommand() 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

37
        return $this->/** @scrutinizer ignore-call */ addCommand('index', compact('type', 'columns', 'indexOptions'));
Loading history...
38
    }
39
40
    /**
41
     * Specify an index for the table.
42
     *
43
     * @param  string|array  $columns
44
     * @param  string  $name
45
     * @param  string|null  $algorithm
46
     * @return Fluent
47
     */
48 6
    public function index($columns = null, $name = null, $algorithm = null)
49
    {
50 6
        $type = $this->mapIndexAlgorithm($algorithm);
51
52 6
        return $this->indexCommand($type, $columns, $name);
53
    }
54
55
    /**
56
     * Create a hash index for fast exact matching.
57
     *
58
     * @param  null  $columns
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $columns is correct as it would always require null to be passed?
Loading history...
59
     * @param  array  $indexOptions
60
     * @return Fluent
61
     */
62
    public function hashIndex($columns = null, $indexOptions = [])
63
    {
64
        return $this->indexCommand('hash', $columns, $indexOptions);
0 ignored issues
show
Bug introduced by
$indexOptions of type array is incompatible with the type null|string expected by parameter $name of LaravelFreelancerNL\Aran...Indexes::indexCommand(). ( Ignorable by Annotation )

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

64
        return $this->indexCommand('hash', $columns, /** @scrutinizer ignore-type */ $indexOptions);
Loading history...
65
    }
66
67
    /**
68
     * @param  null|string  $column
69
     * @param  string  $name
70
     * @param  array  $indexOptions
71
     * @return Fluent
72
     *
73
     * @deprecated
74
     */
75
    public function fulltextIndex($column = null, $name = null, $indexOptions = [])
76
    {
77
        return $this->indexCommand('fulltext', $column, $name, $indexOptions);
78
    }
79
80
    /**
81
     *  Specify a spatial index for the table.
82
     *
83
     * @param  array<mixed>|null  $columns
84
     * @param  null  $name
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $name is correct as it would always require null to be passed?
Loading history...
85
     * @param  array  $indexOptions
86
     * @return Fluent
87
     */
88
    public function geoIndex(array $columns = null, $name = null, $indexOptions = [])
89
    {
90
        return $this->indexCommand('geo', $columns, $name, $indexOptions);
91
    }
92
93
    /**
94
     *  Specify a inverted index for the table.
95
     *
96
     * @param  array<mixed>|null  $columns
97
     * @param  null  $name
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $name is correct as it would always require null to be passed?
Loading history...
98
     * @param  array  $indexOptions
99
     * @return Fluent
100
     */
101 13
    public function invertedIndex(array $columns = null, $name = null, $indexOptions = [])
102
    {
103 13
        return $this->indexCommand('inverted', $columns, $name, $indexOptions);
104
    }
105
106
    /**
107
     * Specify a spatial index for the table.
108
     *
109
     * @param  string|array  $columns
110
     * @param  string  $name
111
     * @return Fluent
112
     */
113
    public function spatialIndex($columns, $name = null)
114
    {
115
        return $this->geoIndex($columns, $name);
0 ignored issues
show
Bug introduced by
It seems like $name can also be of type string; however, parameter $name of LaravelFreelancerNL\Aran...rns\Indexes::geoIndex() does only seem to accept null, 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

115
        return $this->geoIndex($columns, /** @scrutinizer ignore-type */ $name);
Loading history...
Bug introduced by
It seems like $columns can also be of type string; however, parameter $columns of LaravelFreelancerNL\Aran...rns\Indexes::geoIndex() does only seem to accept array|null, 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

115
        return $this->geoIndex(/** @scrutinizer ignore-type */ $columns, $name);
Loading history...
116
    }
117
118
    /**
119
     * @param array<mixed>|null$columns
120
     * @param  string|null  $name
121
     * @param  array  $indexOptions
122
     * @return Fluent
123
     */
124
    public function skiplistIndex(array $columns = null, $name = null, $indexOptions = [])
125
    {
126
        return $this->indexCommand('skiplist', $columns, $name, $indexOptions);
127
    }
128
129
    public function persistentIndex(array $columns = null, string $name = null, array $indexOptions = []): Fluent
130
    {
131
        return $this->indexCommand('persistent', $columns, $name, $indexOptions);
132
    }
133
134 14
    public function primary(array $columns = null, string $name = null, array $indexOptions = []): Fluent
135
    {
136 14
        $indexOptions['unique'] = true;
137
138 14
        return $this->indexCommand('persistent', $columns, $name, $indexOptions);
139
    }
140
141
    /**
142
     * Create a TTL index for the table.
143
     *
144
     * @param  array<string>|null  $columns
145
     * @param  null  $name
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $name is correct as it would always require null to be passed?
Loading history...
146
     * @param  array  $indexOptions
147
     */
148
    public function ttlIndex(array $columns = null, $name = null, $indexOptions = []): Fluent
149
    {
150
        return $this->indexCommand('ttl', $columns, $name, $indexOptions);
151
    }
152
153
    /**
154
     * Specify a unique index for the table.
155
     *
156
     * @param  string|array  $columns
157
     * @param  string  $name
158
     * @param  string|null  $algorithm
159
     */
160 13
    public function unique($columns = null, $name = null, $algorithm = null): Fluent
161
    {
162 13
        $type = $this->mapIndexAlgorithm($algorithm);
163
164 13
        $indexOptions = [];
165 13
        $indexOptions['unique'] = true;
166
167 13
        return $this->indexCommand($type, $columns, $name, $indexOptions);
168
    }
169
170
    /**
171
     * @throws ArangoException
172
     */
173 21
    public function executeIndexCommand(Fluent $command)
174
    {
175 21
        if ($this->connection->pretending()) {
176
            $this->connection->logQuery('/* ' . $command->explanation . " */\n", []);
177
178
            return;
179
        }
180
181
        // Skip persistent indexes set solely on id - ArangoDB already sets a unique index on _key.
182
        if (
183 21
            $command->type === 'persistent'
0 ignored issues
show
introduced by
The condition $command->type === 'persistent' is always false.
Loading history...
184 21
            && count($command->columns) === 1
185 21
            && $command->columns[0] === '_key'
186
        ) {
187
            return;
188
        }
189
190 21
        $options = [
191 21
            'type' => $command->type,
192 21
            'fields' => $command->columns,
193 21
            'unique' => $command->unique,
194 21
            'options' => $command->indexOptions,
195 21
        ];
196
197 21
        if (isset($command->indexOptions) && is_array($command->indexOptions)) {
0 ignored issues
show
introduced by
The condition is_array($command->indexOptions) is always false.
Loading history...
198 21
            $options = array_merge($options, $command->indexOptions);
199
        }
200
201 21
        $this->schemaManager->createIndex($this->table, $options);
202
    }
203
204
    /**
205
     * Indicate that the given index should be dropped.
206
     */
207 4
    public function dropIndex(string $name): Fluent
208
    {
209 4
        $parameters = [];
210 4
        $parameters['name'] = 'dropIndex';
211 4
        $parameters['index'] = $name;
212 4
        $parameters['explanation'] = "Drop the '" . $name . "' index on the {$this->table} table.";
213 4
        $parameters['handler'] = 'collection';
214
215 4
        return $this->addCommand('dropIndex', $parameters);
216
    }
217
218
    /**
219
     * Indicate that the given index should be dropped.
220
     */
221 1
    public function dropPrimary(string $name): Fluent
222
    {
223 1
        return $this->dropIndex($name);
224
    }
225
226
227
    /**
228
     * Drop the index by first getting all the indexes on the table; then selecting the matching one
229
     * by name.
230
     */
231 4
    public function executeDropIndexCommand(Fluent $command)
232
    {
233 4
        if ($this->connection->pretending()) {
234
            $this->connection->logQuery('/* ' . $command->explanation . " */\n", []); // @phpstan-ignore-line
235
236
            return;
237
        }
238 4
        $indexes = $this->schemaManager->getIndexes($this->table);
239 4
        $arrayIndex = array_search($command->index, array_column($indexes, 'name'), true);
240 4
        $indexId = $indexes[$arrayIndex]->id;
241
242 4
        $this->schemaManager->deleteIndex($indexId);
243
    }
244
245
    /**
246
     * @param  string|null  $algorithm
247
     * @return mixed|string
248
     */
249 19
    protected function mapIndexAlgorithm($algorithm): mixed
250
    {
251 19
        $algorithmConversion = [
252 19
            'HASH' => 'hash',
253 19
            'BTREE' => 'persistent',
254 19
            'RTREE' => 'geo',
255 19
            'TTL' => 'ttl',
256 19
        ];
257 19
        $algorithm = strtoupper($algorithm);
0 ignored issues
show
Bug introduced by
It seems like $algorithm can also be of type null; however, parameter $string of strtoupper() 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

257
        $algorithm = strtoupper(/** @scrutinizer ignore-type */ $algorithm);
Loading history...
258
259 19
        return (isset($algorithmConversion[$algorithm])) ? $algorithmConversion[$algorithm] : 'persistent';
260
    }
261
262
    /**
263
     * Create a default index name for the table.
264
     *
265
     * @param  string  $type
266
     */
267 22
    public function createIndexName($type, array $columns, array $options = []): string
268
    {
269 22
        $nameParts = [];
270 22
        $nameParts[] = $this->prefix . $this->table;
271 22
        $nameParts = array_merge($nameParts, $columns);
272 22
        $nameParts[] = $type;
273 22
        $nameParts = array_merge($nameParts, array_keys($options));
274 22
        array_filter($nameParts);
275
276 22
        $index = strtolower(implode('_', $nameParts));
277 22
        $index = preg_replace("/\[\*+\]+/", '_array', $index);
278
279 22
        return preg_replace('/[^A-Za-z0-9]+/', '_', $index);
280
    }
281
}
282