Completed
Push — master ( d44d5c...77e295 )
by Jean-Christophe
01:57
created

DAO::getOneToMany()   B

Complexity

Conditions 6
Paths 3

Size

Total Lines 25
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 25
rs 8.439
c 0
b 0
f 0
cc 6
eloc 19
nc 3
nop 3
1
<?php
2
namespace micro\orm;
3
use micro\annotations\ManyToManyParser;
4
use micro\db\SqlUtils;
5
use micro\db\Database;
6
use micro\log\Logger;
7
8
/**
9
 * Classe passerelle entre base de données et modèle objet
10
 * @author jc
11
 * @version 1.0.0.4
12
 * @package orm
13
 */
14
class DAO {
15
	public static $db;
16
	private static $objects;
17
18
	private static function getObjects(){
19
		if(is_null(self::$objects)){
20
			self::$objects=array();
21
			Logger::log("getObjects","Instanciation de Objects");
22
		}
23
		return self::$objects;
24
	}
25
26
	private static function getInstanceIdInObjects($instance){
27
		$condition=self::getCondition(OrmUtils::getKeyFieldsAndValues($instance));
28
		$condition=preg_replace('/\s|\'+/', '', $condition);
29
		return get_class($instance)."#".$condition;
30
	}
31
32
	private static function getCondition($keyValues){
33
		$retArray=array();
34
		if(is_array($keyValues)){
35
			foreach ($keyValues as $key=>$value){
36
				$retArray[]="`".$key."` = '".$value."'";
37
			}
38
			$condition=implode(" AND ", $retArray);
39
		}else
40
			$condition=$keyValues;
41
		return $condition;
42
	}
43
44
	private static function addInstanceInObjects($instance){
45
		self::getObjects();
46
		self::$objects[self::getInstanceIdInObjects($instance)]=$instance;
47
	}
48
49
	private static function getInstanceInObjects($className,$keyValue){
50
		$objects=self::getObjects();
51
		$condition=self::getCondition($keyValue);
52
		$condition=preg_replace('/\s|\'+/', '', $condition);
53
		$key=$className."#".$condition;
54
		if(array_key_exists($key,$objects)){
55
			Logger::log("getInstanceInObjects", "Récupération d'une instance de ".$className."(".$condition.") dans objects");
56
			return $objects[$key];
57
		}
58
		return null;
59
	}
60
61
	/**
62
	 * Charge les membres associés à $instance par une relation de type ManyToOne
63
	 * @param object $instance
64
	 * @param array $keyValues
65
	 * @param array $members
66
	 */
67
	private static function getOneManyToOne($instance,$keyValues,$members){
68
		$class=get_class($instance);
69
		foreach ($members as $member){
70
			$annot=Reflexion::getAnnotationMember($class, $member->getName(), "JoinColumn");
71
			if($annot!==false){
72
				reset($keyValues);
73
				if($annot->name==key($keyValues)){
74
					$key=OrmUtils::getFirstKey($annot->className);
75
					$kv=array($key=>$keyValues[$annot->name]);
76
77
					$obj=self::getOne($annot->className, $kv,false);
78
					if($obj!==null){
79
						Logger::log("getOneManyToOne", "Chargement de ".$member->getName()." pour l'objet ".$class);
80
						$accesseur="set".ucfirst($member->getName());
81
						if(method_exists($instance,$accesseur)){
82
							$instance->$accesseur($obj);
83
							return;
84
						}
85
					}
86
				}
87
			}
88
		}
89
	}
90
91
	/**
92
	 * Affecte/charge les enregistrements fils dans le membre $member de $instance.
93
	 * Si $array est null, les fils sont chargés depuis la base de données
94
	 * @param object $instance
95
	 * @param string $member Membre sur lequel doit être présent une annotation OneToMany
96
	 * @param array $array paramètre facultatif contenant la liste des fils possibles
97
	 */
98
	public static function getOneToMany($instance,$member,$array=null){
99
		$ret=array();
100
		$class=get_class($instance);
101
		$annot=Reflexion::getAnnotationMember($class, $member, "OneToMany");
102
		if($annot!==false){
103
			$fk=Reflexion::getAnnotationMember($annot->className, $annot->mappedBy, "JoinColumn");
104
			$fkv=OrmUtils::getFirstKeyValue($instance);
105
			if(is_null($array)){
106
				$ret=self::getAll($annot->className,$fk->name."='".$fkv."'");
107
			}
108
			else{
109
				$elementAccessor="get".ucfirst($annot->mappedBy);
110
				foreach ($array as $element){
111
					$elementRef=$element->$elementAccessor();
112
					if(!is_null($elementRef)){
113
						$idElementRef=OrmUtils::getFirstKeyValue($elementRef);
114
						if($idElementRef==$fkv)
115
							$ret[]=$element;
116
					}
117
				}
118
			}
119
			self::setToMember($member, $instance, $ret, $class, "getOneToMany");
120
		}
121
		return $ret;
122
	}
123
124
	private static function setToMember($member,$instance,$value,$class,$part){
125
		$accessor="set".ucfirst($member);
126
		if(method_exists($instance,$accessor)){
127
			Logger::log($part, "Affectation de ".$member." pour l'objet ".$class);
128
			$instance->$accessor($value);
129
		}else{
130
			Logger::warn($part, "L'accesseur ".$accessor." est manquant pour ".$class);
131
		}
132
	}
133
	/**
134
	 * @param object $instance
135
	 * @param ManyToManyParser $parser
136
	 * @return PDOStatement
137
	 */
138
	private static function getSQLForJoinTable($instance,ManyToManyParser $parser){
139
		$accessor="get".ucfirst($parser->getPk());
140
		$sql="SELECT * FROM `".$parser->getJoinTable()."` WHERE `".$parser->getMyFkField()."`='".$instance->$accessor()."'";
141
		Logger::log("ManyToMany", "Exécution de ".$sql);
142
		return self::$db->query($sql);
143
	}
144
	/**
145
	 * Affecte/charge les enregistrements fils dans le membre $member de $instance.
146
	 * Si $array est null, les fils sont chargés depuis la base de données
147
	 * @param object $instance
148
	 * @param string $member Membre sur lequel doit être présent une annotation OneToMany
149
	 * @param array $array paramètre facultatif contenant la liste des fils possibles
150
	 */
151
	public static function getManyToMany($instance,$member,$array=null){
152
		$ret=array();
153
		$class=get_class($instance);
154
		$parser=new ManyToManyParser($instance, $member);
155
		if($parser->init()){
156
			$joinTableCursor=self::getSQLForJoinTable($instance,$parser);
157
			if(is_null($array)){
158
				foreach($joinTableCursor as $row){
159
					$fkv=$row[$parser->getFkField()];
160
					$tmp=self::getOne($parser->getTargetEntity(),"`".$parser->getPk()."`='".$fkv."'");
161
					array_push($ret,$tmp);
162
				}
163
			}
164
			else{
165
				$continue=true;
166
				$accessorToMember="get".ucfirst($parser->getInversedBy());
167
				$myPkAccessor="get".ucfirst($parser->getMyPk());
168
169
				if(!method_exists($instance, $myPkAccessor)){
170
					Logger::warn("ManyToMany", "L'accesseur au membre clé primaire ".$myPkAccessor." est manquant pour ".$class);
171
				}
172
				if(count($array)>0)
173
					$continue=method_exists($array[0], $accessorToMember);
174
				if($continue){
175
					foreach($joinTableCursor as $row){
176
						$fkv=$row[$parser->getFkField()];
0 ignored issues
show
Unused Code introduced by
$fkv is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
177
						foreach($array as $targetEntityInstance){
178
							$instances=$targetEntityInstance->$accessorToMember();
179
							if(is_array($instances)){
180
								foreach ($instances as $inst){
181
									if($inst->$myPkAccessor==$instance->$myPkAccessor)
182
										array_push($array, $targetEntityInstance);
183
								}
184
							}
185
						}
186
					}
187
				}else{
188
					Logger::warn("ManyToMany", "L'accesseur au membre ".$parser->getInversedBy()." est manquant pour ".$parser->getTargetEntity());
189
				}
190
			}
191
			self::setToMember($member, $instance, $ret, $class, "getManyToMany");
192
		}
193
		return $ret;
194
	}
195
	/**
196
	 * Retourne un tableau d'objets de $className depuis la base de données
197
	 * @param string $className nom de la classe du model à charger
198
	 * @param string $condition Partie suivant le WHERE d'une instruction SQL
199
	 * @return array
200
	 */
201
	public static function getAll($className,$condition='',$loadManyToOne=true){
202
		$objects=array();
203
		$membersManyToOne=Reflexion::getMembersWithAnnotation($className, "ManyToOne");
204
		$tableName=OrmUtils::getTableName($className);
205
		if($condition!='')
206
			$condition=" WHERE ".$condition;
207
		$query=self::$db->query("SELECT * FROM ".$tableName.$condition);
208
		Logger::log("getAll","SELECT * FROM ".$tableName.$condition);
209
		foreach ($query as $row){
210
			//Pour chaque enregistrement : instanciation d'un objet
211
			$o=new $className();
212
			$objects[]=$o;
213
			foreach ($row as $k=>$v){
214
				//Modificateur et test de son existance
215
				if(!is_numeric($k)){
216
					$accesseur="set".ucfirst($k);
217
					if(method_exists($o,$accesseur)){
218
						$o->$accesseur($v);
219
					}
220
					if($loadManyToOne===true && OrmUtils::isMemberInManyToOne($className,$membersManyToOne, $k)) {
221
						self::getOneManyToOne($o, array($k=>$v), $membersManyToOne);
222
					}
223
				}
224
			}
225
			self::addInstanceInObjects($o);
226
		}
227
		return $objects;
228
	}
229
	/**
230
	 * Retourne le nombre d'objets de $className depuis la base de données respectant la condition éventuellement passée en paramètre
231
	 * @param string $className nom de la classe du model à charger
232
	 * @param string $condition Partie suivant le WHERE d'une instruction SQL
233
	 */
234
	public static function count($className,$condition=''){
235
		$tableName=OrmUtils::getTableName($className);
236
		if($condition!='')
237
			$condition=" WHERE ".$condition;
238
		return self::$db->query("SELECT COUNT(*) FROM ".$tableName.$condition)->fetchColumn();
239
	}
240
241
	/**
242
	 * Retourne une instance de $className depuis la base de données, à  partir des valeurs $keyValues de la clé primaire
243
	 * @param String $className nom de la classe du model à charger
244
	 * @param Array|string $keyValues valeurs des clés primaires ou condition
245
	 */
246
	public static function getOne($className,$keyValues,$loadManyToOne=true){
247
		if(!is_array($keyValues)){
248
			if(strrpos($keyValues,"=")===false){
249
				$keyValues="`".OrmUtils::getFirstKey($className)."`='".$keyValues."'";
250
			}elseif ($keyValues=="")
251
				$keyValues="";
252
		}
253
		$condition=self::getCondition($keyValues);
254
		$retour=self::getInstanceInObjects($className,$condition);
255
		if(!isset($retour)){
256
			$retour=self::getAll($className,$condition,$loadManyToOne);
257
			if(sizeof($retour)<1)
258
				return null;
259
			else
260
				return $retour[0];
261
		}
262
		return $retour;
263
264
	}
265
266
	/**
267
	 * Supprime $instance dans la base de données
268
	 * @param Classe $instance instance à supprimer
269
	 */
270
	public static function remove($instance){
271
		$tableName=OrmUtils::getTableName(get_class($instance));
272
		$keyAndValues=OrmUtils::getKeyFieldsAndValues($instance);
273
		$sql="DELETE FROM ".$tableName." WHERE ".SqlUtils::getWhere($keyAndValues);
274
		Logger::log("delete", $sql);
275
		$statement=self::$db->prepareStatement($sql);
276
		foreach ($keyAndValues as $key=>$value){
277
			self::$db->bindValueFromStatement($statement,$key,$value);
278
		}
279
		return $statement->execute();
280
	}
281
282
	/**
283
	 * Insère $instance dans la base de données
284
	 * @param object $instance instance à insérer
285
	 * @param $insertMany si vrai, sauvegarde des instances reliées à $instance par un ManyToMany
286
	 */
287
	public static function insert($instance,$insertMany=false){
288
		$tableName=OrmUtils::getTableName(get_class($instance));
289
		$keyAndValues=Reflexion::getPropertiesAndValues($instance);
290
		$keyAndValues=array_merge($keyAndValues, OrmUtils::getManyToOneMembersAndValues($instance));
291
		$sql="INSERT INTO ".$tableName."(".SqlUtils::getInsertFields($keyAndValues).") VALUES(".SqlUtils::getInsertFieldsValues($keyAndValues).")";
292
		Logger::log("insert", $sql);
293
		Logger::log("Key and values", json_encode($keyAndValues));
294
		$statement=self::$db->prepareStatement($sql);
295
		foreach ($keyAndValues as $key=>$value){
296
				self::$db->bindValueFromStatement($statement,$key,$value);
297
		}
298
		$result=$statement->execute();
299
		if($result){
300
			$accesseurId="set".ucfirst(OrmUtils::getFirstKey(get_class($instance)));
301
			$instance->$accesseurId(self::$db->lastInserId());
302
			if($insertMany){
303
				self::insertOrUpdateAllManyToMany($instance);
304
			}
305
		}
306
		return $result;
307
	}
308
309
	/**
310
	 * Met à jour les membres de $instance annotés par un ManyToMany
311
	 * @param object $instance
312
	 */
313
	public static function insertOrUpdateAllManyToMany($instance){
314
		$members=Reflexion::getMembersWithAnnotation(get_class($instance), "ManyToMany");
315
		foreach ($members as $member){
316
			self::insertOrUpdateManyToMany($instance, $member->name);
317
		}
318
	}
319
320
	/**
321
	 * Met à jour le membre $member de $instance annoté par un ManyToMany
322
	 * @param Object $instance
323
	 * @param String $member
324
	 */
325
	public static function insertOrUpdateManyToMany($instance,$member){
326
		$parser=new ManyToManyParser($instance, $member);
327
		if($parser->init()){
328
			$myField=$parser->getMyFkField();
329
			$field=$parser->getFkField();
330
			$sql="INSERT INTO `".$parser->getJoinTable()."`(`".$myField."`,`".$field."`) VALUES (:".$myField.",:".$field.");";
331
			$memberAccessor="get".ucfirst($member);
332
			$memberValues=$instance->$memberAccessor();
333
			$myKey=$parser->getMyPk();
334
			$myAccessorId="get".ucfirst($myKey);
335
			$accessorId="get".ucfirst($parser->getPk());
336
			$id=$instance->$myAccessorId();
337
			if(!is_null($memberValues)){
338
				self::$db->execute("DELETE FROM `".$parser->getJoinTable()."` WHERE `".$myField."`='".$id."'");
339
				$statement=self::$db->prepareStatement($sql);
340
				foreach ($memberValues as $k=>$targetInstance){
341
					$foreignId=$targetInstance->$accessorId();
342
					$foreignInstances=self::getAll($parser->getTargetEntity(), "`".$parser->getPk()."`"."='".$foreignId."'");
343
					if(!OrmUtils::exists($targetInstance, $parser->getPk(), $foreignInstances)){
344
						self::insert($targetInstance,false);
345
						$foreignId=$targetInstance->$accessorId();
346
						Logger::log("InsertMany", "Insertion d'une instance de ".get_class($instance));
347
					}
348
					self::$db->bindValueFromStatement($statement,$myField,$id);
349
					self::$db->bindValueFromStatement($statement,$field,$foreignId);
350
					$statement->execute();
351
					Logger::log("InsertMany", "Insertion des valeurs dans la table association '".$parser->getJoinTable()."'");
352
				}
353
			}
354
		}
355
	}
356
	/**
357
	 * Met à jour $instance dans la base de données.
358
	 * Attention de ne pas modifier la clé primaire
359
	 * @param Classe $instance instance à modifier
360
	 * @param $updateMany Ajoute ou met à jour les membres ManyToMany
361
	 */
362
	public static function update($instance,$updateMany=false){
363
		$tableName=OrmUtils::getTableName(get_class($instance));
364
		$ColumnskeyAndValues=Reflexion::getPropertiesAndValues($instance);
365
		$ColumnskeyAndValues=array_merge($ColumnskeyAndValues, OrmUtils::getManyToOneMembersAndValues($instance));
366
		$keyFieldsAndValues=OrmUtils::getKeyFieldsAndValues($instance);
367
		$sql="UPDATE ".$tableName." SET ".SqlUtils::getUpdateFieldsKeyAndValues($ColumnskeyAndValues)." WHERE ".SqlUtils::getWhere($keyFieldsAndValues);
368
		Logger::log("update", $sql);
369
		Logger::log("Key and values", json_encode($ColumnskeyAndValues));
370
		$statement=self::$db->prepareStatement($sql);
371
		foreach ($ColumnskeyAndValues as $key=>$value){
372
				self::$db->bindValueFromStatement($statement,$key,$value);
373
		}
374
		$result= $statement->execute();
375
		if($result && $updateMany)
376
			self::insertOrUpdateAllManyToMany($instance);
377
		return $result;
378
	}
379
380
	/**
381
	 * Réalise la connexion à la base de données en utilisant les paramètres passés
382
	 * @param string $dbName
383
	 * @param string $serverName
384
	 * @param string $port
385
	 * @param string $user
386
	 * @param string $password
387
	 */
388
	public static function connect($dbName,$serverName="127.0.0.1",$port="3306",$user="root",$password=""){
389
		self::$db=new Database($dbName,$serverName,$port,$user,$password);
390
		self::$db->connect();
391
	}
392
}
393