Completed
Branch feature/pre-split (f8e7b8)
by Anton
04:02
created

AbstractTable::intiSchema()   A

Complexity

Conditions 4
Paths 8

Size

Total Lines 16
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 7
nc 8
nop 1
dl 0
loc 16
rs 9.2
c 0
b 0
f 0
1
<?php
2
/**
3
 * components
4
 *
5
 * @author    Wolfy-J
6
 */
7
namespace Spiral\Database\Schemas\Prototypes;
8
9
use Interop\Container\ContainerInterface;
10
use Psr\Log\LoggerAwareInterface;
11
use Spiral\Core\Component;
12
use Spiral\Database\Entities\Driver;
13
use Spiral\Database\Exceptions\SchemaException;
14
use Spiral\Database\Schemas\ColumnInterface;
15
use Spiral\Database\Schemas\IndexInterface;
16
use Spiral\Database\Schemas\ReferenceInterface;
17
use Spiral\Database\Schemas\TableInterface;
18
use Spiral\Database\Schemas\TableState;
19
use Spiral\Debug\Traits\LoggerTrait;
20
21
/**
22
 * AbstractTable class used to describe and manage state of specified table. It provides ability to
23
 * get table introspection, update table schema and automatically generate set of diff operations.
24
 *
25
 * Most of table operation like column, index or foreign key creation/altering will be applied when
26
 * save() method will be called.
27
 *
28
 * Column configuration shortcuts:
29
 *
30
 * @method AbstractColumn primary($column)
31
 * @method AbstractColumn bigPrimary($column)
32
 * @method AbstractColumn enum($column, array $values)
33
 * @method AbstractColumn string($column, $length = 255)
34
 * @method AbstractColumn decimal($column, $precision, $scale)
35
 * @method AbstractColumn boolean($column)
36
 * @method AbstractColumn integer($column)
37
 * @method AbstractColumn tinyInteger($column)
38
 * @method AbstractColumn bigInteger($column)
39
 * @method AbstractColumn text($column)
40
 * @method AbstractColumn tinyText($column)
41
 * @method AbstractColumn longText($column)
42
 * @method AbstractColumn double($column)
43
 * @method AbstractColumn float($column)
44
 * @method AbstractColumn datetime($column)
45
 * @method AbstractColumn date($column)
46
 * @method AbstractColumn time($column)
47
 * @method AbstractColumn timestamp($column)
48
 * @method AbstractColumn binary($column)
49
 * @method AbstractColumn tinyBinary($column)
50
 * @method AbstractColumn longBinary($column)
51
 */
52
abstract class AbstractTable extends Component implements TableInterface, LoggerAwareInterface
53
{
54
    use LoggerTrait;
55
56
    /**
57
     * Indication that table is exists and current schema is fetched from database.
58
     *
59
     * @var bool
60
     */
61
    private $exists = false;
62
63
    /**
64
     * Database specific tablePrefix. Required for table renames.
65
     *
66
     * @var string
67
     */
68
    private $prefix = '';
69
70
    /**
71
     * @invisible
72
     *
73
     * @var Driver
74
     */
75
    protected $driver = null;
76
77
    /**
78
     * Initial table state.
79
     *
80
     * @invisible
81
     * @var TableState
82
     */
83
    protected $initialState = null;
84
85
    /**
86
     * Currently defined table state.
87
     *
88
     * @invisible
89
     * @var TableState
90
     */
91
    protected $currentState = null;
92
93
    /**
94
     * @param Driver $driver Parent driver.
95
     * @param string $name   Table name, must include table prefix.
96
     * @param string $prefix Database specific table prefix.
97
     */
98
    public function __construct(Driver $driver, string $name, string $prefix)
99
    {
100
        $this->driver = $driver;
101
        $this->prefix = $prefix;
102
103
        //Initializing states
104
        $this->initialState = new TableState($this->prefix . $name);
105
        $this->exists = $this->driver->hasTable($this->initialState->getName());
106
107
        if ($this->exists) {
108
            //Initiating table schema
109
            $this->intiSchema($this->initialState);
110
        }
111
112
        $this->setState($this->initialState);
113
    }
114
115
    /**
116
     * Get instance of associated driver.
117
     *
118
     * @return Driver
119
     */
120
    public function getDriver(): Driver
121
    {
122
        return $this->driver;
123
    }
124
125
    /**
126
     * {@inheritdoc}
127
     */
128
    public function exists(): bool
129
    {
130
        return $this->exists;
131
    }
132
133
    /**
134
     * Return database specific table prefix.
135
     *
136
     * @return string
137
     */
138
    public function getPrefix(): string
139
    {
140
        return $this->prefix;
141
    }
142
143
    /**
144
     * Sets table name. Use this function in combination with save to rename table.
145
     *
146
     * @param string $name
147
     *
148
     * @return string Prefixed table name.
149
     */
150
    public function setName(string $name): string
151
    {
152
        $this->currentState->setName($this->prefix . $name);
153
154
        return $this->getName();
155
    }
156
157
    /**
158
     * {@inheritdoc}
159
     */
160
    public function getName(): string
161
    {
162
        return $this->currentState->getName();
163
    }
164
165
    /**
166
     * Set table primary keys. Operation can only be applied for newly created tables. Now every
167
     * database might support compound indexes.
168
     *
169
     * @param array $columns
170
     *
171
     * @return self
172
     *
173
     * @throws SchemaException
174
     */
175
    public function setPrimaryKeys(array $columns): AbstractTable
176
    {
177
        if ($this->exists() && $this->initialState->getPrimaryKeys() != $columns) {
178
            throw new SchemaException('Unable to change primary keys for already exists table');
0 ignored issues
show
Unused Code introduced by
The call to SchemaException::__construct() has too many arguments starting with 'Unable to change primar...r already exists table'.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
179
        }
180
181
        //Updating primary keys in current state
182
        $this->currentState->setPrimaryKeys($columns);
183
184
        return $this;
185
    }
186
187
    /**
188
     * {@inheritdoc}
189
     */
190
    public function getPrimaryKeys(): array
191
    {
192
        return $this->currentState->getPrimaryKeys();
193
    }
194
195
    /**
196
     * {@inheritdoc}
197
     */
198
    public function hasColumn(string $name): bool
199
    {
200
        return $this->currentState->hasColumn($name);
201
    }
202
203
    /**
204
     * {@inheritdoc}
205
     */
206
    public function getColumns(): array
207
    {
208
        return $this->currentState->getColumns();
209
    }
210
211
    /**
212
     * {@inheritdoc}
213
     */
214
    public function hasIndex(array $columns = []): bool
215
    {
216
        return $this->currentState->hasIndex($columns);
217
    }
218
219
    /**
220
     * {@inheritdoc}
221
     */
222
    public function getIndexes(): array
223
    {
224
        return $this->currentState->getIndexes();
225
    }
226
227
    /**
228
     * {@inheritdoc}
229
     */
230
    public function hasForeign(string $column): bool
231
    {
232
        return $this->currentState->hasForeign($column);
233
    }
234
235
    /**
236
     * {@inheritdoc}
237
     */
238
    public function getForeigns(): array
239
    {
240
        return $this->currentState->getForeigns();
241
    }
242
243
    /**
244
     * {@inheritdoc}
245
     */
246
    public function getDependencies(): array
247
    {
248
        $tables = [];
249
        foreach ($this->currentState->getForeigns() as $foreign) {
250
            $tables[] = $foreign->getForeignTable();
251
        }
252
253
        return $tables;
254
    }
255
256
    //------
257
    //Altering operations
258
    //------
259
260
    /**
261
     * Reset table state to new form.
262
     *
263
     * @param TableState $state Use null to flush table schema.
264
     *
265
     * @return self|$this
266
     */
267
    public function setState(TableState $state = null): AbstractTable
268
    {
269
        $this->currentState = new TableState($this->getName());
270
271
        if (!empty($state)) {
272
            $this->currentState->setName($state->getName());
273
            $this->currentState->syncState($state);
0 ignored issues
show
Documentation introduced by
$state is of type object<Spiral\Database\Schemas\TableState>, but the function expects a object<self>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
274
        }
275
276
        return $this;
277
    }
278
279
    /**
280
     * Reset table state to it initial form.
281
     *
282
     * @return self|$this
283
     */
284
    public function resetState(): AbstractTable
285
    {
286
        $this->setState($this->initialState);
287
288
        return $this;
289
    }
290
291
    /**
292
     * Populate table schema with values from database.
293
     *
294
     * @param TableState $state
295
     */
296
    protected function intiSchema(TableState $state)
297
    {
298
        foreach ($this->fetchColumns() as $column) {
299
            $state->registerColumn($column);
300
        }
301
302
        foreach ($this->fetchIndexes() as $index) {
303
            $state->registerIndex($index);
304
        }
305
306
        foreach ($this->fetchReferences() as $foreign) {
307
            $state->registerIndex($foreign);
0 ignored issues
show
Documentation introduced by
$foreign is of type object<Spiral\Database\S...mas\ReferenceInterface>, but the function expects a object<Spiral\Database\Schemas\IndexInterface>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
308
        }
309
310
        //DBMS specific initialization can be placed here
311
    }
312
313
    /**
314
     * Fetch index declarations from database.
315
     *
316
     * @return ColumnInterface[]
317
     */
318
    abstract protected function fetchColumns(): array;
319
320
    /**
321
     * Fetch index declarations from database.
322
     *
323
     * @return IndexInterface[]
324
     */
325
    abstract protected function fetchIndexes(): array;
326
327
    /**
328
     * Fetch references declaration from database.
329
     *
330
     * @return ReferenceInterface[]
331
     */
332
    abstract protected function fetchReferences(): array;
333
334
    /**
335
     * @return AbstractColumn|string
336
     */
337
    public function __toString(): string
338
    {
339
        return $this->getName();
340
    }
341
342
    /**
343
     * @return ContainerInterface
344
     */
345
    protected function iocContainer()
346
    {
347
        //Falling back to driver specific container
348
        return $this->driver->iocContainer();
0 ignored issues
show
Bug introduced by
The method iocContainer() cannot be called from this context as it is declared protected in class Spiral\Core\Component.

This check looks for access to methods that are not accessible from the current context.

If you need to make a method accessible to another context you can raise its visibility level in the defining class.

Loading history...
349
    }
350
}