Completed
Push — master ( 3845e2...5afe3b )
by Jean-Christophe
02:10
created

DbGenerator   A

Complexity

Total Complexity 38

Size/Duplication

Total Lines 203
Duplicated Lines 0 %

Coupling/Cohesion

Components 3
Dependencies 3

Importance

Changes 1
Bugs 0 Features 1
Metric Value
wmc 38
lcom 3
cbo 3
dl 0
loc 203
rs 9.36
c 1
b 0
f 1

20 Methods

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