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

Blueprint::compileAqlCommand()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 0
Metric Value
eloc 4
c 0
b 0
f 0
dl 0
loc 8
ccs 0
cts 5
cp 0
rs 10
cc 2
nc 2
nop 1
crap 6
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\Columns;
13
use LaravelFreelancerNL\Aranguent\Schema\Concerns\Indexes;
14
use LaravelFreelancerNL\Aranguent\Schema\Concerns\Tables;
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 (and can't) 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 Tables;
0 ignored issues
show
introduced by
The trait LaravelFreelancerNL\Aran...\Schema\Concerns\Tables requires some properties which are not provided by LaravelFreelancerNL\Aranguent\Schema\Blueprint: $name, $options, $explanation
Loading history...
32
    use Columns;
33
    use Indexes;
0 ignored issues
show
introduced by
The trait LaravelFreelancerNL\Aran...Schema\Concerns\Indexes 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
     * Whether to make the table temporary.
84
     *
85
     * @var bool
86
     */
87
    public $temporary = false;
88
89
    /**
90
     * Detect if _id should autoincrement.
91
     *
92
     * @var bool
93
     */
94
    protected $autoIncrement = false;
95
96
    /**
97
     * Create a new schema blueprint.
98
     *
99
     * Blueprint constructor.
100
     *
101
     * @param  string  $table
102
     * @param  SchemaManager  $schemaManager
103
     * @param  string  $prefix
104
     */
105 25
    public function __construct($table, $schemaManager, Closure $callback = null, $prefix = '')
106
    {
107 25
        $this->table = $table;
108
109 25
        $this->schemaManager = $schemaManager;
110
111 25
        $this->prefix = $prefix;
112
113 25
        if (!is_null($callback)) {
114 23
            $callback($this);
115
        }
116
    }
117
118
    /**
119
     * Execute the blueprint against the database.
120
     *
121
     *
122
     * @return void
123
     */
124 25
    public function build(Connection $connection, Grammar $grammar = null)
125
    {
126 25
        $this->connection = $connection;
127
128 25
        if (!isset($grammar)) {
129
            $this->grammar = $connection->getSchemaGrammar();
130
        }
131
132 25
        foreach ($this->commands as $command) {
133 23
            if ($command->handler == 'aql') {
134
                $command = $this->compileAqlCommand($command);
135
            }
136
137 23
            $this->executeCommand($command);
138
        }
139
    }
140
141
    /**
142
     * Generate the compilation method name and call it if method exists in the Grammar object.
143
     */
144
    public function compileAqlCommand(Fluent $command): Fluent
145
    {
146
        $compileMethod = 'compile' . ucfirst($command->name);
147
        if (method_exists($this->grammar, $compileMethod)) {
148
            return $this->grammar->$compileMethod($this->table, $command);
149
        }
150
151
        return $command;
152
    }
153
154
    /**
155
     * Generate the execution method name and call it if the method exists.
156
     */
157 23
    public function executeCommand(Fluent $command): void
158
    {
159 23
        $executeNamedMethod = 'execute' . ucfirst($command->name) . 'Command';
160 23
        if (method_exists($this, $executeNamedMethod)) {
161 23
            $this->$executeNamedMethod($command);
162
163 23
            return;
164
        }
165
        $this->executeCommandByHandler($command);
166
    }
167
168
    protected function executeCommandByHandler(Fluent $command): void
169
    {
170
        if (!isset($command->handler)) {
171
            return;
172
        }
173
        $executeHandlerMethod = 'execute' . ucfirst($command->handler) . 'Command';
174
        if (method_exists($this, $executeHandlerMethod)) {
175
            $this->$executeHandlerMethod($command);
176
        }
177
    }
178
179
    /**
180
     * Execute an AQL statement.
181
     */
182
    public function executeAqlCommand(Fluent $command)
183
    {
184
        $this->connection->statement($command->aqb->query, $command->aqb->binds);
185
    }
186
187
    public function executeCollectionCommand(Fluent $command): void
188
    {
189
        if ($this->connection->pretending()) {
190
            $this->connection->logQuery('/* ' . $command->explanation . " */\n", []);
191
192
            return;
193
        }
194
195
        if (method_exists($this->schemaManager, $command->method)) {
196
            $this->schemaManager->{$command->method}($command->parameters);
197
        }
198
    }
199
200
    /**
201
     * Solely provides feedback to the developer in pretend mode.
202
     */
203 16
    public function executeIgnoreCommand(Fluent $command): void
204
    {
205 16
        if ($this->connection->pretending()) {
206
            $this->connection->logQuery('/* ' . $command->explanation . " */\n", []);
207
        }
208
    }
209
210
    /**
211
     * Add a new command to the blueprint.
212
     *
213
     * @param  string  $name
214
     * @return Fluent
215
     */
216 23
    protected function addCommand($name, array $parameters = [])
217
    {
218 23
        $this->commands[] = $command = $this->createCommand($name, $parameters);
219
220 23
        return $command;
221
    }
222
223
    /**
224
     * Create a new Fluent command.
225
     *
226
     * @param  string  $name
227
     * @return Fluent
228
     */
229 23
    protected function createCommand($name, array $parameters = [])
230
    {
231 23
        return new Fluent(array_merge(compact('name'), $parameters));
232
    }
233
234
    /**
235
     * Get the commands on the blueprint.
236
     *
237
     * @return Fluent[]
238
     */
239 3
    public function getCommands()
240
    {
241 3
        return $this->commands;
242
    }
243
244
    /**
245
     * Silently catch unsupported schema methods. Store columns for backwards compatible fluent index creation.
246
     *
247
     * @param  array<mixed>  $args
248
     * @return Blueprint
249
     */
250 16
    public function __call($method, $args = [])
251
    {
252 16
        $columnMethods = [
253 16
            'bigIncrements', 'bigInteger', 'binary', 'boolean', 'char', 'date', 'dateTime', 'dateTimeTz', 'decimal',
254 16
            'double', 'enum', 'engine', 'float', 'foreignId', 'geometry', 'geometryCollection', 'increments', 'integer',
255 16
            'ipAddress', 'json', 'jsonb', 'lineString', 'longText', 'macAddress', 'mediumIncrements', 'mediumInteger',
256 16
            'mediumText', 'morphs', 'uuidMorphs', 'multiLineString', 'multiPoint', 'multiPolygon',
257 16
            'nullableMorphs', 'nullableUuidMorphs', 'nullableTimestamps', 'point', 'polygon', 'rememberToken',
258 16
            'set', 'smallIncrements', 'smallInteger', 'softDeletes', 'softDeletesTz', 'string',
259 16
            'text', 'time', 'timeTz', 'timestamp', 'timestampTz', 'timestamps', 'tinyIncrements', 'tinyInteger',
260 16
            'unsignedBigInteger', 'unsignedDecimal', 'unsignedInteger', 'unsignedMediumInteger', 'unsignedSmallInteger',
261 16
            'unsignedTinyInteger', 'uuid', 'year',
262 16
        ];
263
264 16
        if (in_array($method, $columnMethods)) {
265 16
            if (isset($args[0]) && is_string($args[0])) {
266 16
                $this->columns[] = $args[0];
267
            }
268
        }
269
270 16
        $autoIncrementMethods = ['increments', 'autoIncrement'];
271 16
        if (in_array($method, $autoIncrementMethods)) {
272
            $this->autoIncrement = true;
273
        }
274
275 16
        $info = [];
276 16
        $info['method'] = $method;
277 16
        $info['explanation'] = "'$method' is ignored; Aranguent Schema Blueprint doesn't support it.";
278 16
        $this->addCommand('ignore', $info);
279
280 16
        return $this;
281
    }
282
283 21
    public function renameIdField(mixed $fields)
284
    {
285 21
        return array_map(function ($value) {
286 21
            return $value === 'id' ? '_key' : $value;
287 21
        }, $fields);
288
    }
289
}
290