Completed
Push — master ( 10806f...3a0750 )
by Jean-Christophe
01:35
created

DbGenerator::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

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