Completed
Push — master ( af6f2f...54d848 )
by Jean-Christophe
01:36
created

DAO::getOneManyToOne()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 15
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 15
rs 9.4285
cc 3
eloc 12
nc 3
nop 4
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
		foreach ( $query as $row ) {
204
			$o=self::loadObjectFromRow($row, $className, $invertedJoinColumns, $oneToManyFields, $useCache);
205
			$objects[]=$o;
206
		}
207
		return $objects;
208
	}
209
210
	private static function loadObjectFromRow($row, $className, $invertedJoinColumns, $oneToManyFields, $useCache=NULL) {
211
		$o=new $className();
212
		foreach ( $row as $k => $v ) {
213
			$accesseur="set" . ucfirst($k);
214
			if (method_exists($o, $accesseur)) {
215
				$o->$accesseur($v);
216
			}
217
			if (isset($invertedJoinColumns) && isset($invertedJoinColumns[$k])) {
218
				self::getOneManyToOne($o, $v, $invertedJoinColumns[$k], $useCache);
219
			}
220
		}
221
		if (isset($oneToManyFields)) {
222
			foreach ( $oneToManyFields as $k => $annot ) {
223
				self::getOneToMany($o, $k, null, $useCache, $annot);
224
			}
225
		}
226
		return $o;
227
	}
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
	 * @param boolean $useCache
246
	 */
247
	public static function getOne($className, $keyValues, $loadManyToOne=true, $loadOneToMany=false, $useCache=NULL) {
248
		if (!is_array($keyValues)) {
249
			if (strrpos($keyValues, "=") === false) {
250
				$keyValues="`" . OrmUtils::getFirstKey($className) . "`='" . $keyValues . "'";
251
			} elseif ($keyValues == "")
252
				$keyValues="";
253
		}
254
		$condition=self::getCondition($keyValues,$className);
255
		$retour=self::getAll($className, $condition, $loadManyToOne, $loadOneToMany, $useCache);
256
		if (sizeof($retour) < 1)
257
			return null;
258
		else
259
			return $retour[0];
260
	}
261
262
	/**
263
	 * Supprime $instance dans la base de données
264
	 * @param Classe $instance instance à supprimer
265
	 */
266
	public static function remove($instance) {
267
		$tableName=OrmUtils::getTableName(get_class($instance));
268
		$keyAndValues=OrmUtils::getKeyFieldsAndValues($instance);
269
		$sql="DELETE FROM " . $tableName . " WHERE " . SqlUtils::getWhere($keyAndValues);
270
		Logger::log("delete", $sql);
271
		$statement=self::$db->prepareStatement($sql);
272
		foreach ( $keyAndValues as $key => $value ) {
273
			self::$db->bindValueFromStatement($statement, $key, $value);
274
		}
275
		return $statement->execute();
276
	}
277
278
	/**
279
	 * Insère $instance dans la base de données
280
	 * @param object $instance instance à insérer
281
	 * @param $insertMany si vrai, sauvegarde des instances reliées à $instance par un ManyToMany
282
	 */
283
	public static function insert($instance, $insertMany=false) {
284
		$tableName=OrmUtils::getTableName(get_class($instance));
285
		$keyAndValues=Reflexion::getPropertiesAndValues($instance);
286
		$keyAndValues=array_merge($keyAndValues, OrmUtils::getManyToOneMembersAndValues($instance));
287
		$sql="INSERT INTO " . $tableName . "(" . SqlUtils::getInsertFields($keyAndValues) . ") VALUES(" . SqlUtils::getInsertFieldsValues($keyAndValues) . ")";
288
		Logger::log("insert", $sql);
289
		Logger::log("Key and values", json_encode($keyAndValues));
290
		$statement=self::$db->prepareStatement($sql);
291
		foreach ( $keyAndValues as $key => $value ) {
292
			self::$db->bindValueFromStatement($statement, $key, $value);
293
		}
294
		$result=$statement->execute();
295
		if ($result) {
296
			$accesseurId="set" . ucfirst(OrmUtils::getFirstKey(get_class($instance)));
297
			$instance->$accesseurId(self::$db->lastInserId());
298
			if ($insertMany) {
299
				self::insertOrUpdateAllManyToMany($instance);
300
			}
301
		}
302
		return $result;
303
	}
304
305
	/**
306
	 * Met à jour les membres de $instance annotés par un ManyToMany
307
	 * @param object $instance
308
	 */
309
	public static function insertOrUpdateAllManyToMany($instance) {
310
		$members=OrmUtils::getAnnotationInfo(get_class($instance), "#manyToMany");
311
		if ($members !== false) {
312
			$members=\array_keys($members);
313
			foreach ( $members as $member ) {
314
				self::insertOrUpdateManyToMany($instance, $member);
315
			}
316
		}
317
	}
318
319
	/**
320
	 * Met à jour le membre $member de $instance annoté par un ManyToMany
321
	 * @param Object $instance
322
	 * @param String $member
323
	 */
324
	public static function insertOrUpdateManyToMany($instance, $member) {
325
		$parser=new ManyToManyParser($instance, $member);
326
		if ($parser->init()) {
327
			$myField=$parser->getMyFkField();
328
			$field=$parser->getFkField();
329
			$sql="INSERT INTO `" . $parser->getJoinTable() . "`(`" . $myField . "`,`" . $field . "`) VALUES (:" . $myField . ",:" . $field . ");";
330
			$memberAccessor="get" . ucfirst($member);
331
			$memberValues=$instance->$memberAccessor();
332
			$myKey=$parser->getMyPk();
333
			$myAccessorId="get" . ucfirst($myKey);
334
			$accessorId="get" . ucfirst($parser->getPk());
335
			$id=$instance->$myAccessorId();
336
			if (!is_null($memberValues)) {
337
				self::$db->execute("DELETE FROM `" . $parser->getJoinTable() . "` WHERE `" . $myField . "`='" . $id . "'");
338
				$statement=self::$db->prepareStatement($sql);
339
				foreach ( $memberValues as $targetInstance ) {
340
					$foreignId=$targetInstance->$accessorId();
341
					$foreignInstances=self::getAll($parser->getTargetEntity(), "`" . $parser->getPk() . "`" . "='" . $foreignId . "'");
342
					if (!OrmUtils::exists($targetInstance, $parser->getPk(), $foreignInstances)) {
343
						self::insert($targetInstance, false);
344
						$foreignId=$targetInstance->$accessorId();
345
						Logger::log("InsertMany", "Insertion d'une instance de " . get_class($instance));
346
					}
347
					self::$db->bindValueFromStatement($statement, $myField, $id);
348
					self::$db->bindValueFromStatement($statement, $field, $foreignId);
349
					$statement->execute();
350
					Logger::log("InsertMany", "Insertion des valeurs dans la table association '" . $parser->getJoinTable() . "'");
351
				}
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="", $cache=false) {
389
		self::$db=new Database($dbName, $serverName, $port, $user, $password, $cache);
390
		self::$db->connect();
391
	}
392
}
393