Completed
Push — master ( 4f05e7...d82db7 )
by Ivan
02:15
created

Table::getRelation()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 4
ccs 2
cts 2
cp 1
rs 10
cc 1
eloc 2
nc 1
nop 1
crap 1
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 1
    public function __construct(string $name)
23
    {
24 1
        $this->data = [
25 1
            'name'    => $name,
26
            'columns' => [],
27
            'primary' => [],
28 1
            'comment' => ''
29
        ];
30 1
    }
31
    /**
32
     * Get the table comment
33
     * @return string  the table comment
34
     */
35
    public function getComment()
36
    {
37
        return $this->data['comment'];
38
    }
39
    /**
40
     * Set the table comment
41
     * @param  string    $comment     the table comment
42
     * @return $this
43
     */
44 1
    public function setComment(string $comment)
45
    {
46 1
        $this->data['comment'] = $comment;
47 1
        return $this;
48
    }
49
    /**
50
     * Add a column to the definition
51
     * @param  string    $column     the column name
52
     * @param  array     $definition optional array of data associated with the column
53
     * @return  self
54
     */
55 1
    public function addColumn(string $column, array $definition = []) : Table
56
    {
57 1
        $this->data['columns'][$column] = TableColumn::fromArray($column, $definition);
58 1
        return $this;
59
    }
60
    /**
61
     * Add columns to the definition
62
     * @param  array      $columns key - value pairs, where each key is a column name and each value - array of info
63
     * @return  self
64
     */
65 1
    public function addColumns(array $columns) : Table
66
    {
67 1
        foreach ($columns as $column => $definition) {
68 1
            if (is_numeric($column) && is_string($definition)) {
69
                $this->addColumn($definition, []);
70
            } else {
71 1
                $this->addColumn($column, $definition);
72
            }
73
        }
74 1
        return $this;
75
    }
76
    /**
77
     * Set the primary key
78
     * @param  array|string        $column either a single column name or an array of column names
79
     * @return  self
80
     */
81 1
    public function setPrimaryKey($column) : Table
82
    {
83 1
        if (!is_array($column)) {
84
            $column = [ $column ];
85
        }
86 1
        $this->data['primary'] = $column;
87 1
        return $this;
88
    }
89
    /**
90
     * Get the table name
91
     * @return string  the table name
92
     */
93 13
    public function getName()
94
    {
95 13
        return $this->data['name'];
96
    }
97
    /**
98
     * Get a column definition
99
     * @param  string    $column the column name to search for
100
     * @return array|null the column details or `null` if the column does not exist
101
     */
102 13
    public function getColumn($column)
103
    {
104 13
        return $this->data['columns'][$column] ?? null;
105
    }
106
    /**
107
     * Get all column names
108
     * @return array     array of strings, where each element is a column name
109
     */
110 13
    public function getColumns()
111
    {
112 13
        return array_keys($this->data['columns']);
113
    }
114
    /**
115
     * Get all column definitions
116
     * @return array         key - value pairs, where each key is a column name and each value - the column data
117
     */
118 2
    public function getFullColumns()
119
    {
120 2
        return $this->data['columns'];
121
    }
122
    /**
123
     * Get the primary key columns
124
     * @return array        array of column names
125
     */
126 13
    public function getPrimaryKey()
127
    {
128 13
        return $this->data['primary'];
129
    }
130
    /**
131
     * Create a relation where each record has zero or one related rows in another table
132
     * @param  Table             $toTable       the related table definition
133
     * @param  string|null       $name          the name of the relation (defaults to the related table name)
134
     * @param  string|array|null $toTableColumn the remote columns pointing to the PK in the current table
135
     * @param  string|null       $sql           additional where clauses to use, default to null
136
     * @param  array|null        $par           parameters for the above statement, defaults to null
137
     * @return $this
138
     */
139
    public function hasOne(
140
        Table $toTable,
141
        string $name = null,
142
        $toTableColumn = null,
143
        string $sql = null,
144
        array $par = []
145
    ) : Table {
146
        $columns = $toTable->getColumns();
147
148
        $keymap = [];
149
        if (!isset($toTableColumn)) {
150
            $toTableColumn = [];
151
        }
152
        if (!is_array($toTableColumn)) {
153
            $toTableColumn = [$toTableColumn];
154
        }
155
        foreach ($this->getPrimaryKey() as $k => $pkField) {
156
            if (isset($toTableColumn[$pkField])) {
157
                $key = $toTableColumn[$pkField];
158
            } elseif (isset($toTableColumn[$k])) {
159
                $key = $toTableColumn[$k];
160
            } else {
161
                $key = $this->getName().'_'.$pkField;
162
            }
163
            if (!in_array($key, $columns)) {
164
                throw new DBException('Missing foreign key mapping');
165
            }
166
            $keymap[$pkField] = $key;
167
        }
168
169
        if (!isset($name)) {
170
            $name = $toTable->getName() . '_' . implode('_', array_keys($keymap));
171
        }
172
        $this->addRelation(new TableRelation(
173
            $name,
174
            $toTable,
175
            $keymap,
176
            false,
177
            null,
178
            null,
179
            $sql,
180
            $par
181
        ));
182
        return $this;
183
    }
184
    /**
185
     * Create a relation where each record has zero, one or more related rows in another table
186
     * @param  Table   $toTable       the related table definition
187
     * @param  string|null       $name          the name of the relation (defaults to the related table name)
188
     * @param  string|array|null $toTableColumn the remote columns pointing to the PK in the current table
189
     * @param  string|null       $sql           additional where clauses to use, default to null
190
     * @param  array|null        $par           parameters for the above statement, defaults to null
191
     * @return $this
192
     */
193
    public function hasMany(
194
        Table $toTable,
195
        string $name = null,
196
        $toTableColumn = null,
197
        $sql = null,
198
        array $par = []
199
    ) : Table {
200
        $columns = $toTable->getColumns();
201
202
        $keymap = [];
203
        if (!isset($toTableColumn)) {
204
            $toTableColumn = [];
205
        }
206
        if (!is_array($toTableColumn)) {
207
            $toTableColumn = [$toTableColumn];
208
        }
209
        foreach ($this->getPrimaryKey() as $k => $pkField) {
210
            if (isset($toTableColumn[$pkField])) {
211
                $key = $toTableColumn[$pkField];
212
            } elseif (isset($toTableColumn[$k])) {
213
                $key = $toTableColumn[$k];
214
            } else {
215
                $key = $this->getName().'_'.$pkField;
216
            }
217
            if (!in_array($key, $columns)) {
218
                throw new DBException('Missing foreign key mapping');
219
            }
220
            $keymap[$pkField] = $key;
221
        }
222
223
        if (!isset($name)) {
224
            $name = $toTable->getName().'_'.implode('_', array_keys($keymap));
225
        }
226
        $this->addRelation(new TableRelation(
227
            $name,
228
            $toTable,
229
            $keymap,
230
            true,
231
            null,
232
            null,
233
            $sql,
234
            $par
235
        ));
236
        return $this;
237
    }
238
    /**
239
     * Create a relation where each record belongs to another row in another table
240
     * @param  Table   $toTable       the related table definition
241
     * @param  string|null       $name          the name of the relation (defaults to the related table name)
242
     * @param  string|array|null $localColumn   the local columns pointing to the PK of the related table
243
     * @param  string|null       $sql           additional where clauses to use, default to null
244
     * @param  array|null        $par           parameters for the above statement, defaults to null
245
     * @return $this
246
     */
247
    public function belongsTo(
248
        Table $toTable,
249
        string $name = null,
250
        $localColumn = null,
251
        $sql = null,
252
        array $par = []
253
    ) : Table {
254
        $columns = $this->getColumns();
255
256
        $keymap = [];
257
        if (!isset($localColumn)) {
258
            $localColumn = [];
259
        }
260
        if (!is_array($localColumn)) {
261
            $localColumn = [$localColumn];
262
        }
263
        foreach ($toTable->getPrimaryKey() as $k => $pkField) {
264
            $key = null;
0 ignored issues
show
Unused Code introduced by
$key is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
265
            if (isset($localColumn[$pkField])) {
266
                $key = $localColumn[$pkField];
267
            } elseif (isset($localColumn[$k])) {
268
                $key = $localColumn[$k];
269
            } else {
270
                $key = $toTable->getName().'_'.$pkField;
271
            }
272
            if (!in_array($key, $columns)) {
273
                throw new DBException('Missing foreign key mapping');
274
            }
275
            $keymap[$key] = $pkField;
276
        }
277
278
        if (!isset($name)) {
279
            $name = $toTable->getName().'_'.implode('_', array_keys($keymap));
280
        }
281
        $this->addRelation(new TableRelation(
282
            $name,
283
            $toTable,
284
            $keymap,
285
            false,
286
            null,
287
            null,
288
            $sql,
289
            $par
290
        ));
291
        return $this;
292
    }
293
    /**
294
     * Create a relation where each record has many linked records in another table but using a liking table
295
     * @param  Table   $toTable       the related table definition
296
     * @param  Table   $pivot         the pivot table definition
297
     * @param  string|null       $name          the name of the relation (defaults to the related table name)
298
     * @param  string|array|null $toTableColumn the local columns pointing to the pivot table
299
     * @param  string|array|null $localColumn   the pivot columns pointing to the related table PK
300
     * @return $this
301
     */
302
    public function manyToMany(
303
        Table $toTable,
304
        Table $pivot,
305
        $name = null,
306
        $toTableColumn = null,
307
        $localColumn = null
308
    ) : Table {
309
        $pivotColumns = $pivot->getColumns();
310
311
        $keymap = [];
312
        if (!isset($toTableColumn)) {
313
            $toTableColumn = [];
314
        }
315
        if (!is_array($toTableColumn)) {
316
            $toTableColumn = [$toTableColumn];
317
        }
318
        foreach ($this->getPrimaryKey() as $k => $pkField) {
319
            if (isset($toTableColumn[$pkField])) {
320
                $key = $toTableColumn[$pkField];
321
            } elseif (isset($toTableColumn[$k])) {
322
                $key = $toTableColumn[$k];
323
            } else {
324
                $key = $this->getName().'_'.$pkField;
325
            }
326
            if (!in_array($key, $pivotColumns)) {
327
                throw new DBException('Missing foreign key mapping');
328
            }
329
            $keymap[$pkField] = $key;
330
        }
331
332
        $pivotKeymap = [];
333
        if (!isset($localColumn)) {
334
            $localColumn = [];
335
        }
336
        if (!is_array($localColumn)) {
337
            $localColumn = [$localColumn];
338
        }
339
        foreach ($toTable->getPrimaryKey() as $k => $pkField) {
340
            if (isset($localColumn[$pkField])) {
341
                $key = $localColumn[$pkField];
342
            } elseif (isset($localColumn[$k])) {
343
                $key = $localColumn[$k];
344
            } else {
345
                $key = $toTable->getName().'_'.$pkField;
346
            }
347
            if (!in_array($key, $pivotColumns)) {
348
                throw new DBException('Missing foreign key mapping');
349
            }
350
            $pivotKeymap[$key] = $pkField;
351
        }
352
353
        if (!isset($name)) {
354
            $name = $toTable->getName().'_'.implode('_', array_keys($keymap));
355
        }
356
        $this->addRelation(new TableRelation(
357
            $name,
358
            $toTable,
359
            $keymap,
360
            true,
361
            $pivot,
362
            $pivotKeymap
363
        ));
364
        return $this;
365
    }
366
    /**
367
     * Create an advanced relation using the internal array format
368
     * @param  TableRelation     $relation      the relation definition
369
     * @param  string|null       $name          optional name of the relation (defaults to the related table name)
370
     * @return $this
371
     */
372 1
    public function addRelation(TableRelation $relation, string $name = null)
373
    {
374 1
        $name = $name ?? $relation->name;
375 1
        $relation->name = $name;
376 1
        $this->relations[$name] = $relation;
377 1
        return $this;
378
    }
379
    /**
380
     * Does the definition have related tables
381
     * @return boolean
382
     */
383
    public function hasRelations() : bool
384
    {
385
        return count($this->relations) > 0;
386
    }
387
    /**
388
     * Get all relation definitions
389
     * @return TableRelation[]       the relation definitions
390
     */
391 13
    public function getRelations() : array
392
    {
393 13
        return $this->relations;
394
    }
395
    /**
396
     * Check if a named relation exists
397
     * @param  string      $name the name to search for
398
     * @return boolean           does the relation exist
399
     */
400 3
    public function hasRelation(string $name) : bool
401
    {
402 3
        return isset($this->relations[$name]);
403
    }
404
    /**
405
     * Get a relation by name
406
     * @param  string      $name      the name to search for
407
     * @return TableRelation|null     the relation definition
408
     */
409 5
    public function getRelation(string $name)
410
    {
411 5
        return $this->relations[$name] ?? null;
412
    }
413
    /**
414
     * Rename a relation
415
     * @param  string      $name the name to search for
416
     * @param  string      $new  the new name for the relation
417
     * @return TableRelation     the relation definition
418
     */
419
    public function renameRelation(string $name, string $new) : array
420
    {
421
        if (!isset($this->relations[$name])) {
422
            throw new DBException("Relation not found");
423
        }
424
        if (isset($this->relations[$new])) {
425
            throw new DBException("A relation with that name already exists");
426
        }
427
        $temp = $this->relations[$name];
428
        $temp->name = $new;
429
        $this->relations[$new] = $temp;
430
        unset($this->relations[$name]);
431
        return $this->relations[$new] ?? null;
432
    }
433 1
    public function toLowerCase()
434
    {
435 1
        $temp = [];
436 1
        foreach ($this->data['columns'] as $k => $v) {
437 1
            $temp[strtolower($k)] = $v;
438 1
            $v->setName(strtolower($k));
439
        }
440 1
        $this->data['columns'] = $temp;
441 1
        $this->data['primary'] = array_map("strtolower", $this->data['primary']);
442 1
        $temp = [];
443 1
        foreach ($this->relations as $k => $v) {
444 1
            $t = [];
445 1
            foreach ($v->keymap as $kk => $vv) {
446 1
                $t[strtolower($kk)] = strtolower($vv);
447
            }
448 1
            $v->keymap = $t;
449 1
            if ($v->pivot_keymap) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $v->pivot_keymap of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
450 1
                $t = [];
451 1
                foreach ($v->pivot_keymap as $kk => $vv) {
452 1
                    $t[strtolower($kk)] = strtolower($vv);
453
                }
454 1
                $v->pivot_keymap = $t;
455
            }
456 1
            $temp[strtolower($k)] = $v;
457
        }
458 1
        $this->relations = $temp;
459 1
        return $this;
460
    }
461
}
462