|
1
|
|
|
<?php |
|
2
|
|
|
|
|
3
|
|
|
namespace Ubiquity\db\reverse; |
|
4
|
|
|
|
|
5
|
|
|
use Ubiquity\orm\reverse\TableReversor; |
|
6
|
|
|
use Ubiquity\orm\OrmUtils; |
|
7
|
|
|
use Ubiquity\cache\ClassUtils; |
|
8
|
|
|
use Ubiquity\db\utils\DbTypes; |
|
9
|
|
|
|
|
10
|
|
|
class DbGenerator { |
|
11
|
|
|
protected $nameProtection; |
|
12
|
|
|
protected $createDatabaseMask; |
|
13
|
|
|
protected $createTableMask; |
|
14
|
|
|
protected $fieldMask; |
|
15
|
|
|
protected $foreignKeyMask; |
|
16
|
|
|
protected $alterTableMask; |
|
17
|
|
|
protected $alterTableAddKey; |
|
18
|
|
|
protected $autoIncMask; |
|
19
|
|
|
protected $selectDbMask; |
|
20
|
|
|
protected $constraintNames=[]; |
|
21
|
|
|
protected $sqlScript=[]; |
|
22
|
|
|
protected $fieldTypes; |
|
23
|
|
|
protected $defaultType; |
|
24
|
|
|
protected $manyToManys=[]; |
|
25
|
|
|
|
|
26
|
|
|
|
|
27
|
1 |
|
public function isInt($fieldType){ |
|
28
|
1 |
|
return DbTypes::isInt($fieldType); |
|
29
|
|
|
} |
|
30
|
1 |
|
public function __construct(){ |
|
31
|
1 |
|
$this->nameProtection="`"; |
|
32
|
1 |
|
$this->createDatabaseMask="CREATE DATABASE %name%"; |
|
33
|
1 |
|
$this->selectDbMask="USE %name%"; |
|
34
|
1 |
|
$this->createTableMask="CREATE TABLE %name% (%fields%) %attributes%"; |
|
35
|
1 |
|
$this->fieldMask="%name% %type% %extra%"; |
|
36
|
1 |
|
$this->alterTableMask="ALTER TABLE %tableName% %alter%"; |
|
37
|
1 |
|
$this->foreignKeyMask="ALTER TABLE %tableName% ADD CONSTRAINT %fkName% FOREIGN KEY (%fkFieldName%) REFERENCES %referencesTableName% (%referencesFieldName%) ON DELETE %onDelete% ON UPDATE %onUpdate%"; |
|
38
|
1 |
|
$this->alterTableAddKey="ALTER TABLE %tableName% ADD %type% KEY (%pkFields%)"; |
|
39
|
1 |
|
$this->autoIncMask="ALTER TABLE %tableName% MODIFY %field% AUTO_INCREMENT, AUTO_INCREMENT=%value%"; |
|
40
|
1 |
|
$this->fieldTypes=DbTypes::TYPES; |
|
41
|
1 |
|
$this->defaultType=DbTypes::DEFAULT_TYPE; |
|
42
|
1 |
|
} |
|
43
|
|
|
|
|
44
|
1 |
|
public function createDatabase($name){ |
|
45
|
1 |
|
$script= $this->replaceMask("name", $name, $this->createDatabaseMask); |
|
46
|
1 |
|
return $this->addScript("head", $script); |
|
47
|
|
|
} |
|
48
|
|
|
|
|
49
|
1 |
|
public function selectDatabase($name){ |
|
50
|
1 |
|
$script= $this->replaceMask("name", $name, $this->selectDbMask); |
|
51
|
1 |
|
return $this->addScript("head", $script); |
|
52
|
|
|
} |
|
53
|
|
|
|
|
54
|
1 |
|
public function createTable($name,$fieldsAttributes,$attributes=["ENGINE=InnoDB","DEFAULT CHARSET=utf8"]){ |
|
55
|
1 |
|
$fields=$this->generateFields($fieldsAttributes); |
|
56
|
1 |
|
$attributes=\implode(" ", $attributes); |
|
57
|
1 |
|
$script=$this->replaceArrayMask(["name"=>$name,"fields"=>$fields,"attributes"=>$attributes], $this->createTableMask); |
|
58
|
1 |
|
return $this->addScript("body", $script); |
|
59
|
|
|
} |
|
60
|
|
|
|
|
61
|
1 |
|
public function addKey($tableName,$fieldNames,$type="PRIMARY"){ |
|
62
|
1 |
|
$pks=[]; |
|
63
|
1 |
|
foreach ($fieldNames as $fieldName){ |
|
64
|
1 |
|
$pks[]=$this->nameProtection.$fieldName.$this->nameProtection; |
|
65
|
|
|
} |
|
66
|
1 |
|
$script= $this->replaceArrayMask(["tableName"=>$tableName,"pkFields"=>\implode(",", $pks),"type"=>$type], $this->alterTableAddKey); |
|
67
|
1 |
|
return $this->addScript("before-constraints", $script); |
|
68
|
|
|
} |
|
69
|
|
|
|
|
70
|
1 |
|
public function addForeignKey($tableName,$fkFieldName,$referencesTableName,$referencesFieldName,$fkName=null,$onDelete="CASCADE",$onUpdate="NO ACTION"){ |
|
71
|
1 |
|
if(!isset($fkName)){ |
|
72
|
1 |
|
$fkName=$this->checkConstraintName("fk_".$tableName."_".$referencesTableName); |
|
73
|
|
|
} |
|
74
|
1 |
|
$script= $this->replaceArrayMask(["tableName"=>$tableName,"fkName"=>$fkName,"fkFieldName"=>$fkFieldName,"referencesTableName"=>$referencesTableName,"referencesFieldName"=>$referencesFieldName,"onDelete"=>$onDelete,"onUpdate"=>$onUpdate], $this->foreignKeyMask); |
|
75
|
1 |
|
return $this->addScript("constraints", $script); |
|
76
|
|
|
} |
|
77
|
|
|
|
|
78
|
1 |
|
public function addAutoInc($tableName,$fieldName,$value=1){ |
|
79
|
1 |
|
$script= $this->replaceArrayMask(["tableName"=>$tableName,"field"=>$fieldName,"value"=>$value], $this->autoIncMask); |
|
80
|
1 |
|
return $this->addScript("before-constraints", $script); |
|
81
|
|
|
} |
|
82
|
|
|
|
|
83
|
1 |
|
protected function addScript($key,$script){ |
|
84
|
1 |
|
if(!isset($this->sqlScript[$key])){ |
|
85
|
1 |
|
$this->sqlScript[$key]=[]; |
|
86
|
|
|
} |
|
87
|
1 |
|
$this->sqlScript[$key][]=$script; |
|
88
|
1 |
|
return $script; |
|
89
|
|
|
} |
|
90
|
|
|
|
|
91
|
1 |
|
protected function checkConstraintName($name){ |
|
92
|
1 |
|
if(\array_search($name, $this->constraintNames)){ |
|
93
|
|
|
$matches=[]; |
|
94
|
|
|
if (\preg_match('@([\s\S]*?)((?:\d)+)$@', $name,$matches)) { |
|
95
|
|
|
if(isset($matches[2])){ |
|
96
|
|
|
$nb=\intval($matches[2])+1; |
|
97
|
|
|
$name= $matches[1].$nb; |
|
98
|
|
|
} |
|
99
|
|
|
}else{ |
|
100
|
|
|
$name= $name."1"; |
|
101
|
|
|
} |
|
102
|
|
|
} |
|
103
|
1 |
|
$this->constraintNames[]=$name; |
|
104
|
1 |
|
return $name; |
|
105
|
|
|
} |
|
106
|
|
|
|
|
107
|
1 |
|
public function generateField($fieldAttributes){ |
|
108
|
1 |
|
$fieldAttributes=$this->checkFieldAttributes($fieldAttributes); |
|
109
|
1 |
|
return $this->replaceArrayMask($fieldAttributes,$this->fieldMask); |
|
110
|
|
|
} |
|
111
|
|
|
|
|
112
|
1 |
|
protected function checkFieldAttributes($fieldAttributes){ |
|
113
|
1 |
|
$result=$fieldAttributes; |
|
114
|
1 |
|
$type=$fieldAttributes["type"]; |
|
115
|
1 |
|
$existingType=false; |
|
116
|
1 |
|
$strType=DbTypes::getType($type); |
|
117
|
1 |
|
if(isset($strType)){ |
|
118
|
1 |
|
if(isset($this->fieldTypes[$strType])){ |
|
119
|
1 |
|
if(!isset($fieldAttributes["extra"]) || $fieldAttributes["extra"]=="") { |
|
120
|
1 |
|
$result["extra"]="DEFAULT ".$this->fieldTypes[$strType]; |
|
121
|
|
|
} |
|
122
|
1 |
|
$existingType=true; |
|
123
|
|
|
} |
|
124
|
|
|
} |
|
125
|
1 |
|
if(!$existingType){ |
|
126
|
|
|
$result["type"]=$this->defaultType; |
|
127
|
|
|
} |
|
128
|
1 |
|
return $result; |
|
129
|
|
|
} |
|
130
|
|
|
|
|
131
|
1 |
|
protected function generateFields($fieldsAttributes){ |
|
132
|
1 |
|
$result=[]; |
|
133
|
1 |
|
foreach ($fieldsAttributes as $fieldAttribute){ |
|
134
|
1 |
|
$result[]=$this->generateField($fieldAttribute); |
|
135
|
|
|
} |
|
136
|
1 |
|
return \implode(",", $result); |
|
137
|
|
|
} |
|
138
|
|
|
|
|
139
|
1 |
|
protected function replaceMask($key,$value,$mask){ |
|
140
|
1 |
|
if(\strstr(\strtolower($key),"name")) |
|
141
|
1 |
|
$value=$this->nameProtection.$value.$this->nameProtection; |
|
142
|
1 |
|
return \str_replace("%".$key."%", $value, $mask); |
|
143
|
|
|
} |
|
144
|
|
|
|
|
145
|
1 |
|
protected function replaceArrayMask($keyValues,$mask){ |
|
146
|
1 |
|
foreach ($keyValues as $key=>$value){ |
|
147
|
1 |
|
$mask=$this->replaceMask($key, $value, $mask); |
|
148
|
|
|
} |
|
149
|
1 |
|
return $mask; |
|
150
|
|
|
} |
|
151
|
|
|
|
|
152
|
|
|
public function getSqlScript() { |
|
153
|
|
|
return $this->sqlScript; |
|
154
|
|
|
} |
|
155
|
|
|
|
|
156
|
1 |
|
public function addManyToMany($jointable,$targetEntity){ |
|
157
|
1 |
|
if(!isset($this->manyToManys[$jointable])){ |
|
158
|
1 |
|
$this->manyToManys[$jointable]=[]; |
|
159
|
|
|
} |
|
160
|
1 |
|
$this->manyToManys[$jointable][]=$targetEntity; |
|
161
|
1 |
|
} |
|
162
|
|
|
|
|
163
|
1 |
|
public function generateManyToManys(){ |
|
164
|
1 |
|
foreach ($this->manyToManys as $joinTable=>$targetEntities){ |
|
165
|
1 |
|
$this->generateManyToMany($joinTable, $targetEntities); |
|
166
|
|
|
} |
|
167
|
1 |
|
} |
|
168
|
|
|
|
|
169
|
1 |
|
protected function generateManyToMany($joinTable,$targetEntities){ |
|
170
|
1 |
|
$fields=[]; |
|
171
|
1 |
|
$fieldTypes=[]; |
|
172
|
1 |
|
$manyToOnes=[]; |
|
173
|
1 |
|
$invertedJoinColumns=[]; |
|
174
|
1 |
|
foreach ($targetEntities as $targetEntity){ |
|
175
|
1 |
|
$pk=OrmUtils::getFirstKey($targetEntity); |
|
176
|
1 |
|
$shortClassName=ClassUtils::getClassSimpleName($targetEntity); |
|
177
|
1 |
|
$fieldName=$pk.\ucfirst($shortClassName); |
|
178
|
1 |
|
$fields[]=$fieldName; |
|
179
|
1 |
|
$type=OrmUtils::getFieldType($targetEntity, $pk); |
|
180
|
1 |
|
$fieldTypes[$fieldName]=$type; |
|
181
|
1 |
|
$memberName=\lcfirst($shortClassName); |
|
182
|
1 |
|
$manyToOnes[]=$memberName; |
|
183
|
1 |
|
$invertedJoinColumns[$fieldName]=["member"=>$memberName,"className"=>$targetEntity]; |
|
184
|
|
|
} |
|
185
|
1 |
|
$metas=["#tableName"=>$joinTable,"#primaryKeys"=>$fields,"#nullable"=>[], |
|
186
|
1 |
|
"#notSerializable"=>[],"#fieldTypes"=>$fieldTypes,"#manyToOne"=>$manyToOnes, |
|
187
|
1 |
|
"#invertedJoinColumn"=>$invertedJoinColumns,"#oneToMany"=>[],"#joinTable"=>[], |
|
188
|
1 |
|
"#manyToMany"=>[],"#fieldNames"=>$fields |
|
189
|
|
|
]; |
|
190
|
1 |
|
$tableGenerator=new TableReversor(); |
|
191
|
1 |
|
$tableGenerator->init($metas); |
|
192
|
1 |
|
$tableGenerator->generateSQL($this); |
|
193
|
1 |
|
} |
|
194
|
|
|
|
|
195
|
1 |
|
public function __toString(){ |
|
196
|
1 |
|
$scripts=\array_merge($this->sqlScript["head"],$this->sqlScript["body"]); |
|
197
|
1 |
|
$scripts=\array_merge($scripts,$this->sqlScript["before-constraints"]); |
|
198
|
1 |
|
$scripts=\array_merge($scripts,$this->sqlScript["constraints"]); |
|
199
|
1 |
|
return \implode(";\n", $scripts); |
|
200
|
|
|
} |
|
201
|
|
|
} |
|
202
|
|
|
|