Failed Conditions
Push — refactor/improve-static-analys... ( 8da3ef...a7b39f )
by Bas
09:53
created

Blueprint   A

Complexity

Total Complexity 27

Size/Duplication

Total Lines 261
Duplicated Lines 0 %

Test Coverage

Coverage 65.63%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
wmc 27
eloc 73
c 1
b 0
f 0
dl 0
loc 261
ccs 42
cts 64
cp 0.6563
rs 10

12 Methods

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