Passed
Push — refactor/improve-static-analys... ( efcf20...37f12c )
by Bas
03:04
created

Blueprint::executeCommandByHandler()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 8
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 12

Importance

Changes 0
Metric Value
cc 3
eloc 5
c 0
b 0
f 0
nc 3
nop 1
dl 0
loc 8
ccs 0
cts 6
cp 0
crap 12
rs 10
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 14
    public function __construct($table, $schemaManager, Closure $callback = null, $prefix = '')
106
    {
107 14
        $this->table = $table;
108
109 14
        $this->schemaManager = $schemaManager;
110
111 14
        $this->prefix = $prefix;
112
113 14
        if (!is_null($callback)) {
114 10
            $callback($this);
115
        }
116
    }
117
118
    /**
119
     * Execute the blueprint against the database.
120
     *
121
     *
122
     * @return void
123
     */
124 14
    public function build(Connection $connection, Grammar $grammar = null)
125
    {
126 14
        $this->connection = $connection;
127
128 14
        if (!isset($grammar)) {
129
            $this->grammar = $connection->getSchemaGrammar();
130
        }
131
132 14
        foreach ($this->commands as $command) {
133 12
            if ($command->handler == 'aql') {
134
                $command = $this->compileAqlCommand($command);
135
            }
136
137 12
            $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 12
    public function executeCommand(Fluent $command): void
158
    {
159 12
        $executeNamedMethod = 'execute' . ucfirst($command->name) . 'Command';
160 12
        if (method_exists($this, $executeNamedMethod)) {
161 12
            $this->$executeNamedMethod($command);
162
163 12
            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 6
    public function executeIgnoreCommand(Fluent $command): void
204
    {
205 6
        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 12
    protected function addCommand($name, array $parameters = [])
217
    {
218 12
        $this->commands[] = $command = $this->createCommand($name, $parameters);
219
220 12
        return $command;
221
    }
222
223
    /**
224
     * Create a new Fluent command.
225
     *
226
     * @param  string  $name
227
     * @return Fluent
228
     */
229 12
    protected function createCommand($name, array $parameters = [])
230
    {
231 12
        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 6
    public function __call(string $method, array $args = [])
251
    {
252 6
        $columnMethods = [
253 6
            'bigIncrements', 'bigInteger', 'binary', 'boolean', 'char', 'date', 'dateTime', 'dateTimeTz', 'decimal',
254 6
            'double', 'enum', 'engine', 'float', 'foreignId', 'geometry', 'geometryCollection', 'increments', 'integer',
255 6
            'ipAddress', 'json', 'jsonb', 'lineString', 'longText', 'macAddress', 'mediumIncrements', 'mediumInteger',
256 6
            'mediumText', 'morphs', 'uuidMorphs', 'multiLineString', 'multiPoint', 'multiPolygon',
257 6
            'nullableMorphs', 'nullableUuidMorphs', 'nullableTimestamps', 'point', 'polygon', 'rememberToken',
258 6
            'set', 'smallIncrements', 'smallInteger', 'softDeletes', 'softDeletesTz', 'string',
259 6
            'text', 'time', 'timeTz', 'timestamp', 'timestampTz', 'timestamps', 'tinyIncrements', 'tinyInteger',
260 6
            'unsignedBigInteger', 'unsignedDecimal', 'unsignedInteger', 'unsignedMediumInteger', 'unsignedSmallInteger',
261 6
            'unsignedTinyInteger', 'uuid', 'year',
262 6
        ];
263
264 6
        if (in_array($method, $columnMethods)) {
265 6
            if (isset($args[0]) && is_string($args[0])) {
266 6
                $this->columns[] = $args[0];
267
            }
268
        }
269
270 6
        $autoIncrementMethods = ['increments', 'autoIncrement'];
271 6
        if (in_array($method, $autoIncrementMethods)) {
272
            $this->autoIncrement = true;
273
        }
274
275 6
        $info = [];
276 6
        $info['method'] = $method;
277 6
        $info['explanation'] = "'$method' is ignored; Aranguent Schema Blueprint doesn't support it.";
278 6
        $this->addCommand('ignore', $info);
279
280 6
        return $this;
281
    }
282
}
283