Completed
Push — master ( 4e6026...e0b9fa )
by Jean-Christophe
01:30
created

DAO::isConnected()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 3
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

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