Completed
Push — master ( e5c5d6...6b5dbf )
by Ivan
15:12
created

Table::getFullName()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 4
ccs 2
cts 2
cp 1
rs 10
c 0
b 0
f 0
cc 2
nc 2
nop 0
crap 2
1
<?php
2
3
namespace vakata\database\schema;
4
5
use \vakata\database\DBException;
6
7
/**
8
 * A table definition
9
 */
10
class Table
11
{
12
    protected $data = [];
13
    /**
14
     * @var TableRelation[]
15
     */
16
    protected $relations = [];
17
18
    /**
19
     * Create a new instance
20
     * @param  string      $name the table name
21
     */
22 4
    public function __construct(string $name, string $schema = '')
23
    {
24 4
        $this->data = [
25 4
            'name'    => $name,
26
            'schema'  => $schema,
27
            'columns' => [],
28 4
            'primary' => [],
29
            'comment' => ''
30 4
        ];
31
    }
32
    /**
33
     * Get the table comment
34
     * @return string  the table comment
35
     */
36
    public function getComment()
37
    {
38
        return $this->data['comment'];
39
    }
40
    /**
41
     * Set the table comment
42
     * @param  string    $comment     the table comment
43
     * @return $this
44 4
     */
45
    public function setComment(string $comment)
46 4
    {
47 4
        $this->data['comment'] = $comment;
48
        return $this;
49
    }
50
    /**
51
     * Add a column to the definition
52
     * @param  string    $column     the column name
53
     * @param  array     $definition optional array of data associated with the column
54
     * @return  self
55 4
     */
56
    public function addColumn(string $column, array $definition = []) : Table
57 4
    {
58 4
        $this->data['columns'][$column] = TableColumn::fromArray($column, $definition);
59
        return $this;
60
    }
61
    /**
62
     * Add columns to the definition
63
     * @param  array      $columns key - value pairs, where each key is a column name and each value - array of info
64
     * @return  self
65 4
     */
66
    public function addColumns(array $columns) : Table
67 4
    {
68 4
        foreach ($columns as $column => $definition) {
69
            if (is_numeric($column) && is_string($definition)) {
70
                $this->addColumn($definition, []);
71 4
            } else {
72
                $this->addColumn($column, $definition);
73
            }
74 4
        }
75
        return $this;
76
    }
77
    /**
78
     * Set the primary key
79
     * @param  array|string        $column either a single column name or an array of column names
80
     * @return  self
81 4
     */
82
    public function setPrimaryKey($column) : Table
83 4
    {
84
        if (!is_array($column)) {
85
            $column = [ $column ];
86 4
        }
87 4
        $this->data['primary'] = $column;
88
        return $this;
89
    }
90
    /**
91
     * Get the table schema
92
     * @return string  the table name
93 156
     */
94
    public function getSchema()
95 156
    {
96
        return $this->data['schema'];
97
    }
98
    /**
99
     * Get the table name
100
     * @return string  the table name
101
     */
102 156
    public function getName()
103
    {
104 156
        return $this->data['name'];
105
    }
106
    /**
107
     * Get the table name with the schema if available
108
     * @return string  the table name
109
     */
110 156
    public function getFullName()
111
    {
112 156
        return ($this->data['schema'] ? $this->data['schema'] . '.' : '') . $this->data['name'];
113
    }
114
    /**
115
     * Get a column definition
116
     * @param  string    $column the column name to search for
117
     * @return array|null the column details or `null` if the column does not exist
118 28
     */
119
    public function getColumn($column)
120 28
    {
121
        return $this->data['columns'][$column] ?? null;
122
    }
123
    /**
124
     * Get all column names
125
     * @return array     array of strings, where each element is a column name
126 156
     */
127
    public function getColumns()
128 156
    {
129
        return array_keys($this->data['columns']);
130
    }
131
    /**
132
     * Get all column definitions
133
     * @return array         key - value pairs, where each key is a column name and each value - the column data
134
     */
135
    public function getFullColumns()
136
    {
137
        return $this->data['columns'];
138
    }
139
    /**
140
     * Get the primary key columns
141
     * @return array        array of column names
142
     */
143
    public function getPrimaryKey()
144
    {
145
        return $this->data['primary'];
146
    }
147
    /**
148
     * Create a relation where each record has zero or one related rows in another table
149
     * @param  Table             $toTable       the related table definition
150
     * @param  string|null       $name          the name of the relation (defaults to the related table name)
151
     * @param  string|array|null $toTableColumn the remote columns pointing to the PK in the current table
152
     * @param  string|null       $sql           additional where clauses to use, default to null
153
     * @param  array             $par           parameters for the above statement, defaults to []
154
     * @return $this
155
     */
156
    public function hasOne(
157
        Table $toTable,
158
        string $name = null,
159
        $toTableColumn = null,
160
        string $sql = null,
161
        array $par = []
162
    ) : Table {
163
        $columns = $toTable->getColumns();
164
165
        $keymap = [];
166
        if (!isset($toTableColumn)) {
167
            $toTableColumn = [];
168
        }
169
        if (!is_array($toTableColumn)) {
170
            $toTableColumn = [$toTableColumn];
171
        }
172
        foreach ($this->getPrimaryKey() as $k => $pkField) {
173
            if (isset($toTableColumn[$pkField])) {
174
                $key = $toTableColumn[$pkField];
175
            } elseif (isset($toTableColumn[$k])) {
176
                $key = $toTableColumn[$k];
177
            } else {
178
                $key = $this->getName().'_'.$pkField;
179
            }
180
            if (!in_array($key, $columns)) {
181
                throw new DBException('Missing foreign key mapping');
182
            }
183
            $keymap[$pkField] = $key;
184
        }
185
186
        if (!isset($name)) {
187
            $name = $toTable->getName() . '_' . implode('_', array_keys($keymap));
188
        }
189
        $this->addRelation(new TableRelation(
190
            $name,
191
            $toTable,
192
            $keymap,
193
            false,
194
            null,
195
            null,
196
            $sql,
197
            $par
198
        ));
199
        return $this;
200
    }
201
    /**
202
     * Create a relation where each record has zero, one or more related rows in another table
203
     * @param  Table   $toTable       the related table definition
204
     * @param  string|null       $name          the name of the relation (defaults to the related table name)
205
     * @param  string|array|null $toTableColumn the remote columns pointing to the PK in the current table
206
     * @param  string|null       $sql           additional where clauses to use, default to null
207
     * @param  array             $par           parameters for the above statement, defaults to []
208
     * @return $this
209
     */
210
    public function hasMany(
211
        Table $toTable,
212
        string $name = null,
213
        $toTableColumn = null,
214
        $sql = null,
215
        array $par = []
216
    ) : Table {
217
        $columns = $toTable->getColumns();
218
219
        $keymap = [];
220
        if (!isset($toTableColumn)) {
221
            $toTableColumn = [];
222
        }
223
        if (!is_array($toTableColumn)) {
224
            $toTableColumn = [$toTableColumn];
225
        }
226
        foreach ($this->getPrimaryKey() as $k => $pkField) {
227
            if (isset($toTableColumn[$pkField])) {
228
                $key = $toTableColumn[$pkField];
229
            } elseif (isset($toTableColumn[$k])) {
230
                $key = $toTableColumn[$k];
231
            } else {
232
                $key = $this->getName().'_'.$pkField;
233
            }
234
            if (!in_array($key, $columns)) {
235
                throw new DBException('Missing foreign key mapping');
236
            }
237
            $keymap[$pkField] = $key;
238
        }
239
240
        if (!isset($name)) {
241
            $name = $toTable->getName().'_'.implode('_', array_keys($keymap));
242
        }
243
        $this->addRelation(new TableRelation(
244
            $name,
245
            $toTable,
246
            $keymap,
247
            true,
248
            null,
249
            null,
250
            $sql,
251
            $par
252
        ));
253
        return $this;
254
    }
255
    /**
256
     * Create a relation where each record belongs to another row in another table
257
     * @param  Table   $toTable       the related table definition
258
     * @param  string|null       $name          the name of the relation (defaults to the related table name)
259
     * @param  string|array|null $localColumn   the local columns pointing to the PK of the related table
260
     * @param  string|null       $sql           additional where clauses to use, default to null
261
     * @param  array             $par           parameters for the above statement, defaults to []
262
     * @return $this
263
     */
264
    public function belongsTo(
265
        Table $toTable,
266
        string $name = null,
267
        $localColumn = null,
268
        $sql = null,
269
        array $par = []
270
    ) : Table {
271
        $columns = $this->getColumns();
272
273
        $keymap = [];
274
        if (!isset($localColumn)) {
275
            $localColumn = [];
276
        }
277
        if (!is_array($localColumn)) {
278
            $localColumn = [$localColumn];
279
        }
280
        foreach ($toTable->getPrimaryKey() as $k => $pkField) {
281
            if (isset($localColumn[$pkField])) {
282
                $key = $localColumn[$pkField];
283
            } elseif (isset($localColumn[$k])) {
284
                $key = $localColumn[$k];
285
            } else {
286
                $key = $toTable->getName().'_'.$pkField;
287
            }
288
            if (!in_array($key, $columns)) {
289
                throw new DBException('Missing foreign key mapping');
290
            }
291
            $keymap[$key] = $pkField;
292
        }
293
294
        if (!isset($name)) {
295
            $name = $toTable->getName().'_'.implode('_', array_keys($keymap));
296
        }
297
        $this->addRelation(new TableRelation(
298
            $name,
299
            $toTable,
300
            $keymap,
301
            false,
302
            null,
303
            null,
304
            $sql,
305
            $par
306
        ));
307
        return $this;
308
    }
309
    /**
310
     * Create a relation where each record has many linked records in another table but using a liking table
311
     * @param  Table   $toTable       the related table definition
312
     * @param  Table   $pivot         the pivot table definition
313
     * @param  string|null       $name          the name of the relation (defaults to the related table name)
314
     * @param  string|array|null $toTableColumn the local columns pointing to the pivot table
315
     * @param  string|array|null $localColumn   the pivot columns pointing to the related table PK
316
     * @return $this
317
     */
318
    public function manyToMany(
319
        Table $toTable,
320
        Table $pivot,
321
        $name = null,
322
        $toTableColumn = null,
323
        $localColumn = null
324
    ) : Table {
325
        $pivotColumns = $pivot->getColumns();
326
327
        $keymap = [];
328
        if (!isset($toTableColumn)) {
329
            $toTableColumn = [];
330
        }
331
        if (!is_array($toTableColumn)) {
332
            $toTableColumn = [$toTableColumn];
333
        }
334
        foreach ($this->getPrimaryKey() as $k => $pkField) {
335
            if (isset($toTableColumn[$pkField])) {
336
                $key = $toTableColumn[$pkField];
337
            } elseif (isset($toTableColumn[$k])) {
338
                $key = $toTableColumn[$k];
339
            } else {
340
                $key = $this->getName().'_'.$pkField;
341
            }
342
            if (!in_array($key, $pivotColumns)) {
343
                throw new DBException('Missing foreign key mapping');
344
            }
345
            $keymap[$pkField] = $key;
346
        }
347
348
        $pivotKeymap = [];
349
        if (!isset($localColumn)) {
350
            $localColumn = [];
351
        }
352
        if (!is_array($localColumn)) {
353
            $localColumn = [$localColumn];
354
        }
355
        foreach ($toTable->getPrimaryKey() as $k => $pkField) {
356
            if (isset($localColumn[$pkField])) {
357
                $key = $localColumn[$pkField];
358
            } elseif (isset($localColumn[$k])) {
359
                $key = $localColumn[$k];
360
            } else {
361
                $key = $toTable->getName().'_'.$pkField;
362
            }
363
            if (!in_array($key, $pivotColumns)) {
364
                throw new DBException('Missing foreign key mapping');
365
            }
366
            $pivotKeymap[$key] = $pkField;
367
        }
368
369
        if (!isset($name)) {
370
            $name = $toTable->getName().'_'.implode('_', array_keys($keymap));
371 4
        }
372
        $this->addRelation(new TableRelation(
373 4
            $name,
374 4
            $toTable,
375 4
            $keymap,
376 4
            true,
377
            $pivot,
378
            $pivotKeymap
379
        ));
380
        return $this;
381
    }
382
    /**
383
     * Create an advanced relation using the internal array format
384
     * @param  TableRelation     $relation      the relation definition
385
     * @param  string|null       $name          optional name of the relation (defaults to the related table name)
386
     * @return $this
387
     */
388
    public function addRelation(TableRelation $relation, string $name = null)
389
    {
390 152
        $name = $name ?? $relation->name;
391
        $relation->name = $name;
392 152
        $this->relations[$name] = $relation;
393
        return $this;
394
    }
395
    /**
396
     * Does the definition have related tables
397
     * @return boolean
398
     */
399 56
    public function hasRelations() : bool
400
    {
401 56
        return count($this->relations) > 0;
402
    }
403
    /**
404
     * Get all relation definitions
405
     * @return TableRelation[]       the relation definitions
406
     */
407
    public function getRelations() : array
408 48
    {
409
        return $this->relations;
410 48
    }
411
    /**
412
     * Check if a named relation exists
413
     * @param  string      $name the name to search for
414
     * @return boolean           does the relation exist
415
     */
416
    public function hasRelation(string $name) : bool
417
    {
418
        return isset($this->relations[$name]);
419
    }
420
    /**
421
     * Get a relation by name
422
     * @param  string      $name      the name to search for
423
     * @return TableRelation          the relation definition
424
     */
425
    public function getRelation(string $name)
426
    {
427
        return $this->relations[$name];
428
    }
429
    /**
430
     * Rename a relation
431
     * @param  string      $name the name to search for
432 4
     * @param  string      $new  the new name for the relation
433
     * @return mixed       the relation definition
434 4
     */
435 4
    public function renameRelation(string $name, string $new)
436 4
    {
437 4
        if (!isset($this->relations[$name])) {
438 4
            throw new DBException("Relation not found");
439
        }
440 4
        if (isset($this->relations[$new])) {
441 4
            throw new DBException("A relation with that name already exists");
442 4
        }
443 4
        $temp = $this->relations[$name];
444 4
        $temp->name = $new;
445 4
        $this->relations[$new] = $temp;
446 4
        unset($this->relations[$name]);
447
        return $this->relations[$new] ?? null;
448 4
    }
449 4
    public function toLowerCase()
450 4
    {
451 4
        $this->data['name'] = strtolower($this->data['name']);
452 4
        $temp = [];
453
        foreach ($this->data['columns'] as $k => $v) {
454 4
            $temp[strtolower($k)] = $v;
455
            $v->setName(strtolower($k));
456 4
        }
457 4
        $this->data['columns'] = $temp;
458
        $this->data['primary'] = array_map("strtolower", $this->data['primary']);
459 4
        $temp = [];
460 4
        foreach ($this->relations as $k => $v) {
461
            $t = [];
462
            foreach ($v->keymap as $kk => $vv) {
463
                $t[strtolower($kk)] = strtolower($vv);
464
            }
465
            $v->keymap = $t;
466
            if ($v->pivot_keymap !== null) {
467
                $t = [];
468
                foreach ($v->pivot_keymap as $kk => $vv) {
469
                    $t[strtolower($kk)] = strtolower($vv);
470
                }
471
                $v->pivot_keymap = $t;
472
            }
473
            $v->name = strtolower($v->name);
474
            $temp[strtolower($k)] = $v;
475
        }
476
        $this->relations = $temp;
477
        return $this;
478
    }
479
}
480