1
|
|
|
<?php |
2
|
|
|
namespace Rentgen\Schema\Info; |
3
|
|
|
|
4
|
|
|
use Rentgen\Schema\Command; |
5
|
|
|
use Rentgen\Schema\ColumnTypeMapper; |
6
|
|
|
use Rentgen\Database\Table; |
7
|
|
|
use Rentgen\Database\Column\ColumnCreator; |
8
|
|
|
use Rentgen\Database\Constraint\ForeignKey; |
9
|
|
|
use Rentgen\Database\Constraint\PrimaryKey; |
10
|
|
|
use Rentgen\Database\Constraint\Unique; |
11
|
|
|
use Rentgen\Exception\TableNotExistsException; |
12
|
|
|
|
13
|
|
|
/** |
14
|
|
|
* @author Arek Jaskólski <[email protected]> |
15
|
|
|
*/ |
16
|
|
|
class GetTableCommand extends Command |
17
|
|
|
{ |
18
|
|
|
private $tableName; |
19
|
|
|
|
20
|
|
|
/** |
21
|
|
|
* Sets a table name. |
22
|
|
|
* |
23
|
|
|
* @param string $tableName A table name. |
24
|
|
|
* |
25
|
|
|
* @return GetTableCommand |
26
|
|
|
*/ |
27
|
|
|
public function setTableName($tableName) |
28
|
|
|
{ |
29
|
|
|
$this->tableName = $tableName; |
30
|
|
|
|
31
|
|
|
return $this; |
32
|
|
|
} |
33
|
|
|
|
34
|
|
|
/** |
35
|
|
|
* {@inheritdoc} |
36
|
|
|
*/ |
37
|
|
|
public function getSql() |
38
|
|
|
{ |
39
|
|
|
$sql = sprintf("SELECT table_schema, column_name, data_type, is_identity, is_nullable, |
40
|
|
|
column_default, character_maximum_length, numeric_precision, numeric_scale |
41
|
|
|
FROM information_schema.columns |
42
|
|
|
WHERE table_name ='%s';", $this->tableName); |
43
|
|
|
|
44
|
|
|
return $sql; |
45
|
|
|
} |
46
|
|
|
|
47
|
|
|
/** |
48
|
|
|
* {@inheritdoc} |
49
|
|
|
*/ |
50
|
|
|
public function execute() |
51
|
|
|
{ |
52
|
|
|
$columnTypeMapper = new ColumnTypeMapper(); |
53
|
|
|
|
54
|
|
|
$this->preExecute(); |
55
|
|
|
$columns = $this->connection->query($this->getSql()); |
56
|
|
|
if (empty($columns)) { |
57
|
|
|
throw new TableNotExistsException($this->tableName); |
58
|
|
|
} |
59
|
|
|
$table = new Table($this->tableName); |
60
|
|
|
if (null === $table->getSchema()) { |
61
|
|
|
$table->setSchema($columns[0]['table_schema']); |
62
|
|
|
} |
63
|
|
|
$columnCreator = new ColumnCreator(); |
64
|
|
|
foreach ($columns as $column) { |
65
|
|
|
$columnType = $columnTypeMapper->getCommon($column['data_type']); |
66
|
|
|
$options = array(); |
67
|
|
|
$options['not_null'] = $column['is_nullable'] === 'NO'; |
68
|
|
|
$options['default'] = $column['column_default']; |
69
|
|
|
if ($columnType === 'string') { |
70
|
|
|
preg_match("/'(.*)'::character varying/", $column['column_default'], $matches); |
71
|
|
|
$options['default'] = isset($matches[1]) ? $matches[1] : ''; |
72
|
|
|
$options['limit'] = $column['character_maximum_length']; |
73
|
|
|
} |
74
|
|
|
$column = $columnCreator->create($column['column_name'], $columnType, $options); |
75
|
|
|
$table->addColumn($column); |
76
|
|
|
} |
77
|
|
|
$this->loadConstraints($table); |
78
|
|
|
$this->postExecute(); |
79
|
|
|
|
80
|
|
|
return $table; |
|
|
|
|
81
|
|
|
} |
82
|
|
|
|
83
|
|
|
/** |
84
|
|
|
* Load contsraints to table. |
85
|
|
|
* |
86
|
|
|
* @param Table $table A table. |
87
|
|
|
* |
88
|
|
|
* @return void |
89
|
|
|
*/ |
90
|
|
|
private function loadConstraints(Table $table) |
91
|
|
|
{ |
92
|
|
|
|
93
|
|
|
foreach ($this->getConstraints() as $constraint) { |
94
|
|
|
switch ($constraint['constraint_type']) { |
95
|
|
|
case 'FOREIGN KEY': |
96
|
|
|
// TODO Find a better way to define foreign key |
97
|
|
|
$foreignKey = new ForeignKey(new Table($constraint['table_name']), new Table($constraint['column_name'])); |
98
|
|
|
$foreignKey->setColumns($constraint['references_table']); |
99
|
|
|
$foreignKey->setReferencedColumns($constraint['references_field']); |
100
|
|
|
|
101
|
|
|
$table->addConstraint($foreignKey); |
102
|
|
|
break; |
103
|
|
|
case 'PRIMARY KEY': |
104
|
|
|
$table->addConstraint(new PrimaryKey($constraint['column_name'], $table)); |
105
|
|
|
break; |
106
|
|
|
case 'UNIQUE': |
107
|
|
|
$table->addConstraint(new Unique($constraint['column_name'], new Table($constraint['table_name']))); |
108
|
|
|
break; |
109
|
|
|
} |
110
|
|
|
} |
111
|
|
|
} |
112
|
|
|
|
113
|
|
|
/** |
114
|
|
|
* Gets constraints from database. |
115
|
|
|
* |
116
|
|
|
* @return array Array of contraints. |
117
|
|
|
*/ |
118
|
|
|
private function getConstraints() |
119
|
|
|
{ |
120
|
|
|
$sql = sprintf("SELECT tc.constraint_name, |
121
|
|
|
tc.constraint_type, |
122
|
|
|
tc.table_name, |
123
|
|
|
kcu.column_name, |
124
|
|
|
tc.is_deferrable, |
125
|
|
|
tc.initially_deferred, |
126
|
|
|
rc.match_option AS match_type, |
127
|
|
|
rc.update_rule AS on_update, |
128
|
|
|
rc.delete_rule AS on_delete, |
129
|
|
|
ccu.table_name AS references_table, |
130
|
|
|
ccu.column_name AS references_field |
131
|
|
|
FROM information_schema.table_constraints tc |
132
|
|
|
LEFT JOIN information_schema.key_column_usage kcu |
133
|
|
|
ON tc.constraint_catalog = kcu.constraint_catalog |
134
|
|
|
AND tc.constraint_schema = kcu.constraint_schema |
135
|
|
|
AND tc.constraint_name = kcu.constraint_name |
136
|
|
|
LEFT JOIN information_schema.referential_constraints rc |
137
|
|
|
ON tc.constraint_catalog = rc.constraint_catalog |
138
|
|
|
AND tc.constraint_schema = rc.constraint_schema |
139
|
|
|
AND tc.constraint_name = rc.constraint_name |
140
|
|
|
LEFT JOIN information_schema.constraint_column_usage ccu |
141
|
|
|
ON rc.unique_constraint_catalog = ccu.constraint_catalog |
142
|
|
|
AND rc.unique_constraint_schema = ccu.constraint_schema |
143
|
|
|
AND rc.unique_constraint_name = ccu.constraint_name |
144
|
|
|
WHERE tc.table_name = '%s' AND tc.constraint_type IN ('PRIMARY KEY', 'FOREIGN KEY', 'UNIQUE')", $this->tableName); |
145
|
|
|
|
146
|
|
|
return $this->connection->query($sql); |
147
|
|
|
} |
148
|
|
|
} |
149
|
|
|
|
If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.
Let’s take a look at an example:
Our function
my_function
expects aPost
object, and outputs the author of the post. The base classPost
returns a simple string and outputting a simple string will work just fine. However, the child classBlogPost
which is a sub-type ofPost
instead decided to return anobject
, and is therefore violating the SOLID principles. If aBlogPost
were passed tomy_function
, PHP would not complain, but ultimately fail when executing thestrtoupper
call in its body.