Blueprint::addCommand()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 2
dl 0
loc 5
ccs 3
cts 3
cp 1
crap 1
rs 10
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace LaravelFreelancerNL\Aranguent\Schema;
6
7
use ArangoClient\Schema\SchemaManager;
8
use Closure;
9
use Illuminate\Support\Fluent;
10
use Illuminate\Support\Traits\Macroable;
11
use LaravelFreelancerNL\Aranguent\Connection;
12
use LaravelFreelancerNL\Aranguent\Schema\Concerns\ColumnCommands;
13
use LaravelFreelancerNL\Aranguent\Schema\Concerns\IndexCommands;
14
use LaravelFreelancerNL\Aranguent\Schema\Concerns\TableCommands;
15
16
/**
17
 * Class Blueprint.
18
 *
19
 * The Schema blueprint works differently from the standard Illuminate version:
20
 * 1) ArangoDB is schemaless: we don't need to create columns
21
 * 2) ArangoDB doesn't allow DB schema actions within AQL nor within a transaction.
22
 *
23
 * This means that:
24
 * 1) We catch column related methods silently for backwards compatibility and ease of migrating between DB types
25
 * 2) We don't need to compile AQL for transactions within the accompanying schema grammar. (for now)
26
 * 3) We can just execute each command on order. We will gather them first for possible future optimisations.
27
 */
28
class Blueprint
29
{
30
    use Macroable;
0 ignored issues
show
Bug introduced by
The trait Illuminate\Support\Traits\Macroable requires the property $name which is not provided by LaravelFreelancerNL\Aranguent\Schema\Blueprint.
Loading history...
31
    use TableCommands;
0 ignored issues
show
introduced by
The trait LaravelFreelancerNL\Aran...\Concerns\TableCommands requires some properties which are not provided by LaravelFreelancerNL\Aranguent\Schema\Blueprint: $options, $explanation
Loading history...
32
    use ColumnCommands;
33
    use IndexCommands;
0 ignored issues
show
introduced by
The trait LaravelFreelancerNL\Aran...\Concerns\IndexCommands requires some properties which are not provided by LaravelFreelancerNL\Aranguent\Schema\Blueprint: $indexOptions, $id, $explanation, $type, $unique, $index
Loading history...
34
35
    /**
36
     * The connection that is used by the blueprint.
37
     *
38
     * @var Connection
39
     */
40
    protected $connection;
41
42
    /**
43
     * The grammar that is used by the blueprint.
44
     *
45
     * @var Grammar
46
     */
47
    protected $grammar;
48
49
    /**
50
     * The table the blueprint describes.
51
     *
52
     * @var string
53
     */
54
    protected $table;
55
56
    /**
57
     * The handler for table manipulation.
58
     */
59
    protected SchemaManager $schemaManager;
60
61
    /**
62
     * The prefix of the table.
63
     *
64
     * @var string
65
     */
66
    protected $prefix;
67
68
    /**
69
     * The commands that should be run for the table.
70
     *
71
     * @var Fluent[]
72
     */
73
    protected $commands = [];
74
75
    /**
76
     * Catching columns to be able to add fluent indexes.
77
     *
78
     * @var array
79
     */
80
    protected $columns = [];
81
82
83
    /**
84
     * the key generator to use for this table.
85
     *
86
     * @var bool
87
     */
88
    protected $keyGenerator;
89
90
    protected int $incrementOffset = 0;
91
92
    /**
93
     * Create a new schema blueprint.
94
     *
95
     * Blueprint constructor.
96
     *
97
     * @param string $table
98
     * @param Grammar $grammar
99
     * @param SchemaManager $schemaManager
100
     * @param Closure|null $callback
101
     * @param string $prefix
102
     */
103 90
    public function __construct($table, $grammar, $schemaManager, Closure $callback = null, $prefix = '')
104
    {
105 90
        $this->table = $table;
106
107 90
        $this->grammar = $grammar;
108
109 90
        $this->schemaManager = $schemaManager;
110
111 90
        $this->keyGenerator = config('arangodb.schema.keyOptions.type');
112
113 90
        $this->prefix = $prefix;
114
115 90
        if (!is_null($callback)) {
116 82
            $callback($this);
117
        }
118
    }
119
120
    /**
121
     * Execute the blueprint against the database.
122
     *
123
     *
124
     * @return void
125
     */
126 90
    public function build(Connection $connection, Grammar $grammar = null)
127
    {
128 90
        $this->connection = $connection;
129
130 90
        if (!isset($grammar)) {
131
            $this->grammar = $connection->getSchemaGrammar();
132
        }
133
134 90
        foreach ($this->commands as $command) {
135 88
            if ($command->handler === 'aql') {
136 3
                $command = $this->compileAqlCommand($command);
137
            }
138
139 88
            $this->executeCommand($command);
140
        }
141
    }
142
143
    /**
144
     * Generate the compilation method name and call it if method exists in the Grammar object.
145
     */
146 3
    public function compileAqlCommand(Fluent $command): Fluent
147
    {
148 3
        $compileMethod = 'compile' . ucfirst($command->name);
149 3
        if (method_exists($this->grammar, $compileMethod)) {
150 3
            return $this->grammar->$compileMethod($this->table, $command);
151
        }
152
153
        return $command;
154
    }
155
156
    /**
157
     * Generate the execution method name and call it if the method exists.
158
     */
159 88
    public function executeCommand(Fluent $command): void
160
    {
161 88
        $executeNamedMethod = 'execute' . ucfirst($command->name) . 'Command';
162
163 88
        if (method_exists($this, $executeNamedMethod)) {
164 88
            $this->$executeNamedMethod($command);
165
166 88
            return;
167
        }
168
169 3
        $this->executeCommandByHandler($command);
170
    }
171
172 3
    protected function executeCommandByHandler(Fluent $command): void
173
    {
174 3
        if (!isset($command->handler)) {
175
            return;
176
        }
177 3
        $executeHandlerMethod = 'execute' . ucfirst($command->handler) . 'Command';
178 3
        if (method_exists($this, $executeHandlerMethod)) {
179 3
            $this->$executeHandlerMethod($command);
180
        }
181
    }
182
183
    /**
184
     * Execute an AQL statement.
185
     */
186 3
    public function executeAqlCommand(Fluent $command)
187
    {
188 3
        $this->connection->statement($command->aqb->query, $command->aqb->binds);
189
    }
190
191
    public function executeCollectionCommand(Fluent $command): void
192
    {
193
        if ($this->connection->pretending()) {
194
            $this->connection->logQuery('/* ' . $command->explanation . " */\n", []);
195
196
            return;
197
        }
198
199
        if (method_exists($this->schemaManager, $command->method)) {
200
            $this->schemaManager->{$command->method}($command);
201
        }
202
    }
203
204
    /**
205
     * Solely provides feedback to the developer in pretend mode.
206
     */
207 68
    public function executeIgnoreCommand(Fluent $command): void
208
    {
209 68
        if ($this->connection->pretending()) {
210
            $this->connection->logQuery('/* ' . $command->explanation . " */\n", []);
211
        }
212
    }
213
214
    /**
215
     * Add a new command to the blueprint.
216
     *
217
     * @param  string  $name
218
     * @return Fluent
219
     */
220 88
    protected function addCommand($name, array $parameters = [])
221
    {
222 88
        $this->commands[] = $command = $this->createCommand($name, $parameters);
223
224 88
        return $command;
225
    }
226
227
    /**
228
     * Create a new Fluent command.
229
     *
230
     * @param  string  $name
231
     * @return Fluent
232
     */
233 88
    protected function createCommand($name, array $parameters = [])
234
    {
235 88
        return new Fluent(array_merge(compact('name'), $parameters));
236
    }
237
238
    /**
239
     * Get the commands on the blueprint.
240
     *
241
     * @return Fluent[]
242
     */
243 3
    public function getCommands()
244
    {
245 3
        return $this->commands;
246
    }
247
248 1
    public function from(int $startingValue)
249
    {
250 1
        $this->incrementOffset = $startingValue;
251
252 1
        $info = [];
253 1
        $info['method'] = 'from';
254 1
        $info['explanation'] = "The autoincrement offset has been set to {$startingValue}.";
255
256 1
        return $this->addCommand('ignore', $info);
257
    }
258
259
    /**
260
     * Silently catch unsupported schema methods. Store columns for backwards compatible fluent index creation.
261
     *
262
     * @param  array<mixed>  $args
263
     * @return Blueprint
264
     */
265 68
    public function __call($method, $args = [])
266
    {
267 68
        $columnMethods = [
268 68
            'bigIncrements', 'bigInteger', 'binary', 'boolean', 'char', 'date', 'dateTime', 'dateTimeTz', 'decimal',
269 68
            'double', 'enum', 'engine', 'float', 'foreignId', 'geometry', 'geometryCollection', 'increments', 'integer',
270 68
            'ipAddress', 'json', 'jsonb', 'lineString', 'longText', 'macAddress', 'mediumIncrements', 'mediumInteger',
271 68
            'mediumText', 'morphs', 'uuidMorphs', 'multiLineString', 'multiPoint', 'multiPolygon',
272 68
            'nullableMorphs', 'nullableUuidMorphs', 'nullableTimestamps', 'point', 'polygon', 'rememberToken',
273 68
            'set', 'smallIncrements', 'smallInteger', 'softDeletes', 'softDeletesTz', 'string',
274 68
            'text', 'time', 'timeTz', 'timestamp', 'timestampTz', 'timestamps', 'tinyIncrements', 'tinyInteger',
275 68
            'unsignedBigInteger', 'unsignedDecimal', 'unsignedInteger', 'unsignedMediumInteger', 'unsignedSmallInteger',
276 68
            'unsignedTinyInteger', 'uuid', 'year',
277 68
        ];
278
279 68
        if (in_array($method, $columnMethods)) {
280 66
            if (isset($args[0]) && is_string($args[0])) {
281 66
                $this->columns[] = $args[0];
282
            }
283
        }
284
285 68
        $keyMethods = ['autoIncrement', 'bigIncrements', 'increments', 'mediumIncrements', 'tinyIncrements', 'uuid'];
286 68
        if (in_array($method, $keyMethods)) {
287 49
            $this->handleKeyCommands($method, $args);
288
        }
289
290 68
        $this->ignoreMethod($method);
291
292 68
        return $this;
293
    }
294
295 68
    protected function ignoreMethod(string $method)
296
    {
297 68
        $info = [];
298 68
        $info['method'] = $method;
299 68
        $info['explanation'] = "'$method' is ignored; Aranguent Schema Blueprint doesn't support it.";
300 68
        $this->addCommand('ignore', $info);
301
    }
302
303 79
    public function renameIdField(mixed $fields)
304
    {
305 79
        return array_map(function ($value) {
306 79
            return $value === 'id' ? '_key' : $value;
307 79
        }, $fields);
308
    }
309
310
    /**
311
     * @param mixed[] $options
312
     * @return mixed[]
313
     */
314 67
    protected function setKeyOptions($tableOptions)
315
    {
316 67
        $configuredKeyOptions = config('arangodb.schema.keyOptions');
317
318 67
        $columnOptions = [];
319 67
        $columnOptions['type'] = $this->keyGenerator;
320
321 67
        $mergedKeyOptions = (config('arangodb.schema.key_handling.prioritize_configured_key_type'))
322
            ? array_merge($columnOptions, $configuredKeyOptions, $tableOptions)
323 67
            : array_merge($configuredKeyOptions, $columnOptions, $tableOptions);
324
325 67
        if ($mergedKeyOptions['type'] === 'autoincrement' && $this->incrementOffset !== 0) {
326 1
            $mergedKeyOptions['offset'] = $this->incrementOffset;
327
        }
328
329 67
        return $mergedKeyOptions;
330
    }
331
}
332