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
|
|
|
|