Completed
Push — master ( 01a07f...707e60 )
by Jean-Christophe
01:23
created

DAO   C

Complexity

Total Complexity 72

Size/Duplication

Total Lines 379
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 6

Importance

Changes 3
Bugs 0 Features 1
Metric Value
wmc 72
c 3
b 0
f 1
lcom 1
cbo 6
dl 0
loc 379
rs 5.5667

18 Methods

Rating   Name   Duplication   Size   Complexity  
B getCondition() 0 17 5
A getOneManyToOne() 0 15 3
B getOneToMany() 0 19 5
A getOneToManyFromArray() 0 11 4
A setToMember() 0 9 2
A getSQLForJoinTable() 0 6 1
C getManyToManyFromArray() 0 24 8
A getManyToMany() 0 19 4
C getAll() 0 23 7
B loadObjectFromRow() 0 19 7
A count() 0 6 2
B getOne() 0 14 5
A remove() 0 11 2
A insert() 0 21 4
A insertOrUpdateAllManyToMany() 0 9 3
B insertOrUpdateManyToMany() 0 31 5
A update() 0 17 4
A connect() 0 4 1

How to fix   Complexity   

Complex Class

Complex classes like DAO often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use DAO, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace micro\orm;
4
5
use micro\db\SqlUtils;
6
use micro\db\Database;
7
use micro\log\Logger;
8
use micro\orm\parser\ManyToManyParser;
9
use micro\orm\parser\Reflexion;
10
use micro\utils\JArray;
11
12
/**
13
 * Classe passerelle entre base de données et modèle objet
14
 * @author jc
15
 * @version 1.0.0.5
16
 * @package orm
17
 */
18
class DAO {
19
	public static $db;
20
21
	private static function getCondition($keyValues,$classname=NULL) {
22
		$retArray=array ();
23
		if (is_array($keyValues)) {
24
			if(!JArray::isAssociative($keyValues)){
25
				if(isset($classname)){
26
					$keys=OrmUtils::getKeyFields($classname);
27
					$keyValues=\array_combine($keys, $keyValues);
28
				}
29
			}
30
			foreach ( $keyValues as $key => $value ) {
31
				$retArray[]="`" . $key . "` = '" . $value . "'";
32
			}
33
			$condition=implode(" AND ", $retArray);
34
		} else
35
			$condition=$keyValues;
36
		return $condition;
37
	}
38
39
	/**
40
	 * Charge les membres associés à $instance par une relation de type ManyToOne
41
	 * @param object $instance
42
	 * @param mixed $value
43
	 * @param array $annotationArray
44
	 * @param boolean $useCache
45
	 */
46
	private static function getOneManyToOne($instance, $value, $annotationArray, $useCache=NULL) {
47
		$class=get_class($instance);
48
		$member=$annotationArray["member"];
49
		$key=OrmUtils::getFirstKey($annotationArray["className"]);
50
		$kv=array ($key => $value );
51
		$obj=self::getOne($annotationArray["className"], $kv, false, false, $useCache);
52
		if ($obj !== null) {
53
			Logger::log("getOneManyToOne", "Chargement de " . $member . " pour l'objet " . $class);
54
			$accesseur="set" . ucfirst($member);
55
			if (method_exists($instance, $accesseur)) {
56
				$instance->$accesseur($obj);
57
				return;
58
			}
59
		}
60
	}
61
62
	/**
63
	 * Affecte/charge les enregistrements fils dans le membre $member de $instance.
64
	 * Si $array est null, les fils sont chargés depuis la base de données
65
	 * @param object $instance
66
	 * @param string $member Membre sur lequel doit être présent une annotation OneToMany
67
	 * @param array $array paramètre facultatif contenant la liste des fils possibles
68
	 * @param boolean $useCache
69
	 * @param array $annot used internally
70
	 */
71
	public static function getOneToMany($instance, $member, $array=null, $useCache=NULL, $annot=null) {
72
		$ret=array ();
73
		$class=get_class($instance);
74
		if (!isset($annot))
75
			$annot=OrmUtils::getAnnotationInfoMember($class, "#oneToMany", $member);
76
		if ($annot !== false) {
77
			$fkAnnot=OrmUtils::getAnnotationInfoMember($annot["className"], "#joinColumn", $annot["mappedBy"]);
78
			if ($fkAnnot !== false) {
79
				$fkv=OrmUtils::getFirstKeyValue($instance);
80
				if (is_null($array)) {
81
					$ret=self::getAll($annot["className"], $fkAnnot["name"] . "='" . $fkv . "'", true, false, $useCache);
82
				} else {
83
					self::getOneToManyFromArray($ret, $array, $fkv, $annot);
84
				}
85
				self::setToMember($member, $instance, $ret, $class, "getOneToMany");
86
			}
87
		}
88
		return $ret;
89
	}
90
91
	private static function getOneToManyFromArray(&$ret, $array, $fkv, $annot) {
92
		$elementAccessor="get" . ucfirst($annot["mappedBy"]);
93
		foreach ( $array as $element ) {
94
			$elementRef=$element->$elementAccessor();
95
			if (!is_null($elementRef)) {
96
				$idElementRef=OrmUtils::getFirstKeyValue($elementRef);
97
				if ($idElementRef == $fkv)
98
					$ret[]=$element;
99
			}
100
		}
101
	}
102
103
	private static function setToMember($member, $instance, $value, $class, $part) {
104
		$accessor="set" . ucfirst($member);
105
		if (method_exists($instance, $accessor)) {
106
			Logger::log($part, "Affectation de " . $member . " pour l'objet " . $class);
107
			$instance->$accessor($value);
108
		} else {
109
			Logger::warn($part, "L'accesseur " . $accessor . " est manquant pour " . $class);
110
		}
111
	}
112
113
	/**
114
	 *
115
	 * @param object $instance
116
	 * @param ManyToManyParser $parser
117
	 * @return PDOStatement
118
	 */
119
	private static function getSQLForJoinTable($instance, ManyToManyParser $parser) {
120
		$accessor="get" . ucfirst($parser->getPk());
121
		$sql="SELECT * FROM `" . $parser->getJoinTable() . "` WHERE `" . $parser->getMyFkField() . "`='" . $instance->$accessor() . "'";
122
		Logger::log("ManyToMany", "Exécution de " . $sql);
123
		return self::$db->query($sql);
124
	}
125
126
	/**
127
	 * Affecte/charge les enregistrements fils dans le membre $member de $instance.
128
	 * Si $array est null, les fils sont chargés depuis la base de données
129
	 * @param object $instance
130
	 * @param string $member Membre sur lequel doit être présent une annotation ManyToMany
131
	 * @param array $array paramètre facultatif contenant la liste des fils possibles
132
	 * @param boolean $useCache
133
	 */
134
	public static function getManyToMany($instance, $member, $array=null, $useCache=NULL) {
135
		$ret=array ();
136
		$class=get_class($instance);
137
		$parser=new ManyToManyParser($instance, $member);
138
		if ($parser->init()) {
139
			if (is_null($array)) {
140
				$joinTableCursor=self::getSQLForJoinTable($instance, $parser);
141
				foreach ( $joinTableCursor as $row ) {
142
					$fkv=$row[$parser->getFkField()];
143
					$tmp=self::getOne($parser->getTargetEntity(), "`" . $parser->getPk() . "`='" . $fkv . "'", false, false, $useCache);
144
					array_push($ret, $tmp);
145
				}
146
			} else {
147
				self::getManyToManyFromArray($ret, $instance, $array, $class, $parser);
148
			}
149
			self::setToMember($member, $instance, $ret, $class, "getManyToMany");
150
		}
151
		return $ret;
152
	}
153
154
	private static function getManyToManyFromArray(&$ret, $instance, $array, $class, $parser) {
155
		$continue=true;
156
		$accessorToMember="get" . ucfirst($parser->getInversedBy());
157
		$myPkAccessor="get" . ucfirst($parser->getMyPk());
158
159
		if (!method_exists($instance, $myPkAccessor)) {
160
			Logger::warn("ManyToMany", "L'accesseur au membre clé primaire " . $myPkAccessor . " est manquant pour " . $class);
161
		}
162
		if (count($array) > 0)
163
			$continue=method_exists($array[0], $accessorToMember);
164
		if ($continue) {
165
			foreach ( $array as $targetEntityInstance ) {
166
				$instances=$targetEntityInstance->$accessorToMember();
167
				if (is_array($instances)) {
168
					foreach ( $instances as $inst ) {
169
						if ($inst->$myPkAccessor() == $instance->$myPkAccessor())
170
							array_push($ret, $targetEntityInstance);
171
					}
172
				}
173
			}
174
		} else {
175
			Logger::warn("ManyToMany", "L'accesseur au membre " . $parser->getInversedBy() . " est manquant pour " . $parser->getTargetEntity());
176
		}
177
	}
178
179
	/**
180
	 * Retourne un tableau d'objets de $className depuis la base de données
181
	 * @param string $className nom de la classe du model à charger
182
	 * @param string $condition Partie suivant le WHERE d'une instruction SQL
183
	 * @param boolean $loadManyToOne
184
	 * @param boolean $loadOneToMany
185
	 * @param boolean $useCache
186
	 * @return array
187
	 */
188
	public static function getAll($className, $condition='', $loadManyToOne=true, $loadOneToMany=false, $useCache=NULL) {
189
		$objects=array ();
190
		$invertedJoinColumns=null;
191
		$oneToManyFields=null;
192
		$tableName=OrmUtils::getTableName($className);
193
		$metaDatas=OrmUtils::getModelMetadata($className);
194
		if ($loadManyToOne && isset($metaDatas["#invertedJoinColumn"]))
195
			$invertedJoinColumns=$metaDatas["#invertedJoinColumn"];
196
		if ($loadOneToMany && isset($metaDatas["#oneToMany"])) {
197
			$oneToManyFields=$metaDatas["#oneToMany"];
198
		}
199
		if ($condition != '')
200
			$condition=" WHERE " . $condition;
201
		$query=self::$db->prepareAndExecute($tableName, $condition, $useCache);
202
		Logger::log("getAll", "SELECT * FROM " . $tableName . $condition);
203
204
		$members=OrmUtils::getAnnotationInfo($className, "#fieldNames");
205
		foreach ( $query as $row ) {
206
			$o=self::loadObjectFromRow($row, $className, $invertedJoinColumns, $oneToManyFields,$members, $useCache);
207
			$objects[]=$o;
208
		}
209
		return $objects;
210
	}
211
212
	private static function loadObjectFromRow($row, $className, $invertedJoinColumns, $oneToManyFields, $members,$useCache=NULL) {
213
		$o=new $className();
214
		foreach ( $row as $k => $v ) {
215
			$field=\array_search($k, $members);
216
			$accesseur="set" . ucfirst($field);
217
			if (method_exists($o, $accesseur)) {
218
				$o->$accesseur($v);
219
			}
220
			if (isset($invertedJoinColumns) && isset($invertedJoinColumns[$k])) {
221
				self::getOneManyToOne($o, $v, $invertedJoinColumns[$k], $useCache);
222
			}
223
		}
224
		if (isset($oneToManyFields)) {
225
			foreach ( $oneToManyFields as $k => $annot ) {
226
				self::getOneToMany($o, $k, null, $useCache, $annot);
227
			}
228
		}
229
		return $o;
230
	}
231
232
	/**
233
	 * Retourne le nombre d'objets de $className depuis la base de données respectant la condition éventuellement passée en paramètre
234
	 * @param string $className nom de la classe du model à charger
235
	 * @param string $condition Partie suivant le WHERE d'une instruction SQL
236
	 */
237
	public static function count($className, $condition='') {
238
		$tableName=OrmUtils::getTableName($className);
239
		if ($condition != '')
240
			$condition=" WHERE " . $condition;
241
		return self::$db->query("SELECT COUNT(*) FROM " . $tableName . $condition)->fetchColumn();
242
	}
243
244
	/**
245
	 * Retourne une instance de $className depuis la base de données, à  partir des valeurs $keyValues de la clé primaire
246
	 * @param String $className nom de la classe du model à charger
247
	 * @param Array|string $keyValues valeurs des clés primaires ou condition
248
	 * @param boolean $useCache
249
	 */
250
	public static function getOne($className, $keyValues, $loadManyToOne=true, $loadOneToMany=false, $useCache=NULL) {
251
		if (!is_array($keyValues)) {
252
			if (strrpos($keyValues, "=") === false) {
253
				$keyValues="`" . OrmUtils::getFirstKey($className) . "`='" . $keyValues . "'";
254
			} elseif ($keyValues == "")
255
				$keyValues="";
256
		}
257
		$condition=self::getCondition($keyValues,$className);
258
		$retour=self::getAll($className, $condition, $loadManyToOne, $loadOneToMany, $useCache);
259
		if (sizeof($retour) < 1)
260
			return null;
261
		else
262
			return $retour[0];
263
	}
264
265
	/**
266
	 * Supprime $instance dans la base de données
267
	 * @param Classe $instance instance à supprimer
268
	 */
269
	public static function remove($instance) {
270
		$tableName=OrmUtils::getTableName(get_class($instance));
271
		$keyAndValues=OrmUtils::getKeyFieldsAndValues($instance);
272
		$sql="DELETE FROM " . $tableName . " WHERE " . SqlUtils::getWhere($keyAndValues);
273
		Logger::log("delete", $sql);
274
		$statement=self::$db->prepareStatement($sql);
275
		foreach ( $keyAndValues as $key => $value ) {
276
			self::$db->bindValueFromStatement($statement, $key, $value);
277
		}
278
		return $statement->execute();
279
	}
280
281
	/**
282
	 * Insère $instance dans la base de données
283
	 * @param object $instance instance à insérer
284
	 * @param $insertMany si vrai, sauvegarde des instances reliées à $instance par un ManyToMany
285
	 */
286
	public static function insert($instance, $insertMany=false) {
287
		$tableName=OrmUtils::getTableName(get_class($instance));
288
		$keyAndValues=Reflexion::getPropertiesAndValues($instance);
289
		$keyAndValues=array_merge($keyAndValues, OrmUtils::getManyToOneMembersAndValues($instance));
290
		$sql="INSERT INTO " . $tableName . "(" . SqlUtils::getInsertFields($keyAndValues) . ") VALUES(" . SqlUtils::getInsertFieldsValues($keyAndValues) . ")";
291
		Logger::log("insert", $sql);
292
		Logger::log("Key and values", json_encode($keyAndValues));
293
		$statement=self::$db->prepareStatement($sql);
294
		foreach ( $keyAndValues as $key => $value ) {
295
			self::$db->bindValueFromStatement($statement, $key, $value);
296
		}
297
		$result=$statement->execute();
298
		if ($result) {
299
			$accesseurId="set" . ucfirst(OrmUtils::getFirstKey(get_class($instance)));
300
			$instance->$accesseurId(self::$db->lastInserId());
301
			if ($insertMany) {
302
				self::insertOrUpdateAllManyToMany($instance);
303
			}
304
		}
305
		return $result;
306
	}
307
308
	/**
309
	 * Met à jour les membres de $instance annotés par un ManyToMany
310
	 * @param object $instance
311
	 */
312
	public static function insertOrUpdateAllManyToMany($instance) {
313
		$members=OrmUtils::getAnnotationInfo(get_class($instance), "#manyToMany");
314
		if ($members !== false) {
315
			$members=\array_keys($members);
316
			foreach ( $members as $member ) {
317
				self::insertOrUpdateManyToMany($instance, $member);
318
			}
319
		}
320
	}
321
322
	/**
323
	 * Met à jour le membre $member de $instance annoté par un ManyToMany
324
	 * @param Object $instance
325
	 * @param String $member
326
	 */
327
	public static function insertOrUpdateManyToMany($instance, $member) {
328
		$parser=new ManyToManyParser($instance, $member);
329
		if ($parser->init()) {
330
			$myField=$parser->getMyFkField();
331
			$field=$parser->getFkField();
332
			$sql="INSERT INTO `" . $parser->getJoinTable() . "`(`" . $myField . "`,`" . $field . "`) VALUES (:" . $myField . ",:" . $field . ");";
333
			$memberAccessor="get" . ucfirst($member);
334
			$memberValues=$instance->$memberAccessor();
335
			$myKey=$parser->getMyPk();
336
			$myAccessorId="get" . ucfirst($myKey);
337
			$accessorId="get" . ucfirst($parser->getPk());
338
			$id=$instance->$myAccessorId();
339
			if (!is_null($memberValues)) {
340
				self::$db->execute("DELETE FROM `" . $parser->getJoinTable() . "` WHERE `" . $myField . "`='" . $id . "'");
341
				$statement=self::$db->prepareStatement($sql);
342
				foreach ( $memberValues as $targetInstance ) {
343
					$foreignId=$targetInstance->$accessorId();
344
					$foreignInstances=self::getAll($parser->getTargetEntity(), "`" . $parser->getPk() . "`" . "='" . $foreignId . "'");
345
					if (!OrmUtils::exists($targetInstance, $parser->getPk(), $foreignInstances)) {
346
						self::insert($targetInstance, false);
347
						$foreignId=$targetInstance->$accessorId();
348
						Logger::log("InsertMany", "Insertion d'une instance de " . get_class($instance));
349
					}
350
					self::$db->bindValueFromStatement($statement, $myField, $id);
351
					self::$db->bindValueFromStatement($statement, $field, $foreignId);
352
					$statement->execute();
353
					Logger::log("InsertMany", "Insertion des valeurs dans la table association '" . $parser->getJoinTable() . "'");
354
				}
355
			}
356
		}
357
	}
358
359
	/**
360
	 * Met à jour $instance dans la base de données.
361
	 * Attention de ne pas modifier la clé primaire
362
	 * @param Classe $instance instance à modifier
363
	 * @param $updateMany Ajoute ou met à jour les membres ManyToMany
364
	 */
365
	public static function update($instance, $updateMany=false) {
366
		$tableName=OrmUtils::getTableName(get_class($instance));
367
		$ColumnskeyAndValues=Reflexion::getPropertiesAndValues($instance);
368
		$ColumnskeyAndValues=array_merge($ColumnskeyAndValues, OrmUtils::getManyToOneMembersAndValues($instance));
369
		$keyFieldsAndValues=OrmUtils::getKeyFieldsAndValues($instance);
370
		$sql="UPDATE " . $tableName . " SET " . SqlUtils::getUpdateFieldsKeyAndValues($ColumnskeyAndValues) . " WHERE " . SqlUtils::getWhere($keyFieldsAndValues);
371
		Logger::log("update", $sql);
372
		Logger::log("Key and values", json_encode($ColumnskeyAndValues));
373
		$statement=self::$db->prepareStatement($sql);
374
		foreach ( $ColumnskeyAndValues as $key => $value ) {
375
			self::$db->bindValueFromStatement($statement, $key, $value);
376
		}
377
		$result=$statement->execute();
378
		if ($result && $updateMany)
379
			self::insertOrUpdateAllManyToMany($instance);
380
		return $result;
381
	}
382
383
	/**
384
	 * Réalise la connexion à la base de données en utilisant les paramètres passés
385
	 * @param string $dbType
386
	 * @param string $dbName
387
	 * @param string $serverName
388
	 * @param string $port
389
	 * @param string $user
390
	 * @param string $password
391
	 */
392
	public static function connect($dbType,$dbName, $serverName="127.0.0.1", $port="3306", $user="root", $password="", $cache=false) {
393
		self::$db=new Database($dbType,$dbName, $serverName, $port, $user, $password, $cache);
394
		self::$db->connect();
395
	}
396
}
397