Completed
Push — master ( f97bb0...2ac44b )
by Jean-Christophe
01:48
created

DAO::getOne()   B

Complexity

Conditions 7
Paths 12

Size

Total Lines 16
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

Changes 3
Bugs 0 Features 0
Metric Value
dl 0
loc 16
rs 8.2222
c 3
b 0
f 0
cc 7
eloc 12
nc 12
nop 5
1
<?php
2
3
namespace Ubiquity\orm;
4
5
use Ubiquity\db\Database;
6
use Ubiquity\log\Logger;
7
use Ubiquity\orm\parser\ManyToManyParser;
8
use Ubiquity\db\SqlUtils;
9
use Ubiquity\orm\parser\Reflexion;
10
use Ubiquity\orm\traits\DAOUpdatesTrait;
11
12
/**
13
 * Gateway class between database and object model
14
 * @author jc
15
 * @version 1.0.0.6
16
 * @package orm
17
 */
18
class DAO {
19
	use DAOUpdatesTrait;
20
	public static $db;
21
22
	/**
23
	 * Loads member associated with $instance by a ManyToOne type relationship
24
	 * @param object $instance
25
	 * @param string $member
26
	 * @param boolean $useCache
27
	 */
28
	public static function getManyToOne($instance, $member, $useCache=NULL) {
29
		$fieldAnnot=OrmUtils::getMemberJoinColumns($instance, $member);
30
		if($fieldAnnot!==null){
31
			$field=$fieldAnnot[0];
32
			$value=Reflexion::getMemberValue($instance, $field);
33
			$annotationArray=$fieldAnnot[1];
34
			$member=$annotationArray["member"];
35
			$key=OrmUtils::getFirstKey($annotationArray["className"]);
36
			$kv=array ($key => $value );
37
			$obj=self::getOne($annotationArray["className"], $kv, false, false, $useCache);
38
			if ($obj !== null) {
39
				Logger::log("getManyToOne", "Chargement de " . $member . " pour l'objet " . \get_class($instance));
40
				$accesseur="set" . ucfirst($member);
41
				if (method_exists($instance, $accesseur)) {
42
					$instance->$accesseur($obj);
43
					$instance->_rest[$member]=$obj->_rest;
44
					return;
45
				}
46
			}
47
		}
48
	}
49
50
	private static function _getOneToManyFromArray(&$ret, $array, $fkv, $mappedBy) {
51
		$elementAccessor="get" . ucfirst($mappedBy);
52
		foreach ( $array as $element ) {
53
			$elementRef=$element->$elementAccessor();
54
			if (!is_null($elementRef)) {
55
				$idElementRef=OrmUtils::getFirstKeyValue($elementRef);
56
				if ($idElementRef == $fkv)
57
					$ret[]=$element;
58
			}
59
		}
60
	}
61
62
	/**
63
	 * Assign / load the child records in the $member member of $instance.
64
	 * @param object $instance
65
	 * @param string $member Member on which a oneToMany annotation must be present
66
	 * @param boolean $useCache
67
	 * @param array $annot used internally
68
	 */
69
	public static function getOneToMany($instance, $member, $useCache=NULL, $annot=null) {
70
		$ret=array ();
71
		$class=get_class($instance);
72
		if (!isset($annot))
73
			$annot=OrmUtils::getAnnotationInfoMember($class, "#oneToMany", $member);
74
			if ($annot !== false) {
75
				$fkAnnot=OrmUtils::getAnnotationInfoMember($annot["className"], "#joinColumn", $annot["mappedBy"]);
76
				if ($fkAnnot !== false) {
77
					$fkv=OrmUtils::getFirstKeyValue($instance);
78
					$ret=self::getAll($annot["className"], $fkAnnot["name"] . "='" . $fkv . "'", true, false, $useCache);
79
					self::setToMember($member, $instance, $ret, $class, "getOneToMany");
80
				}
81
			}
82
			return $ret;
83
	}
84
85
	/**
86
	 * @param object $instance
87
	 * @param string $member
88
	 * @param array $array
89
	 * @param string $mappedBy
90
	 */
91
	public static function affectsOneToManyFromArray($instance, $member, $array=null, $mappedBy=null) {
92
		$ret=array ();
93
		$class=get_class($instance);
94
		if (!isset($mappedBy)){
95
			$annot=OrmUtils::getAnnotationInfoMember($class, "#oneToMany", $member);
96
			$mappedBy=$annot["mappedBy"];
97
		}
98
		if ($mappedBy !== false) {
99
				$fkv=OrmUtils::getFirstKeyValue($instance);
100
				self::_getOneToManyFromArray($ret, $array, $fkv, $mappedBy);
101
				self::setToMember($member, $instance, $ret, $class, "getOneToMany");
102
		}
103
		return $ret;
104
	}
105
106
	private static function setToMember($member, $instance, $value, $class, $part) {
107
		$accessor="set" . ucfirst($member);
108
		if (method_exists($instance, $accessor)) {
109
			Logger::log($part, "Affectation de " . $member . " pour l'objet " . $class);
110
			$instance->$accessor($value);
111
			$instance->_rest[$member]=$value;
112
		} else {
113
			Logger::warn($part, "L'accesseur " . $accessor . " est manquant pour " . $class);
114
		}
115
	}
116
117
	/**
118
	 * Assigns / loads the child records in the $member member of $instance.
119
	 * If $ array is null, the records are loaded from the database
120
	 * @param object $instance
121
	 * @param string $member Member on which a ManyToMany annotation must be present
122
	 * @param array $array optional parameter containing the list of possible child records
123
	 * @param boolean $useCache
124
	 */
125
	public static function getManyToMany($instance, $member,$array=null,$useCache=NULL){
126
		$ret=array ();
127
		$class=get_class($instance);
128
		$parser=new ManyToManyParser($instance, $member);
129
		if ($parser->init()) {
130
			if (is_null($array)) {
131
				$accessor="get" . ucfirst($parser->getMyPk());
132
				$condition=" INNER JOIN `" . $parser->getJoinTable() . "` on `".$parser->getJoinTable()."`.`".$parser->getFkField()."`=`".$parser->getTargetEntityTable()."`.`".$parser->getPk()."` WHERE `" . $parser->getMyFkField() . "`='" . $instance->$accessor() . "'";
133
				$ret=self::getAll($parser->getTargetEntityClass(),$condition,true,false,$useCache);
134
			}else{
135
				self::getManyToManyFromArray($ret, $instance, $array, $class, $parser);
136
			}
137
			self::setToMember($member, $instance, $ret, $class, "getManyToMany");
138
		}
139
		return $ret;
140
	}
141
142
	/**
143
	 * @param object $instance
144
	 * @param array $array
145
	 * @param boolean $useCache
146
	 */
147
	public static function affectsManyToManys($instance,$array=NULL,$useCache=NULL){
148
		$metaDatas=OrmUtils::getModelMetadata(\get_class($instance));
149
		$manyToManyFields=$metaDatas["#manyToMany"];
150
		if(\sizeof($manyToManyFields)>0){
151
			foreach ($manyToManyFields as $member){
152
				self::getManyToMany($instance, $member,$array,$useCache);
153
			}
154
		}
155
	}
156
157
	private static function getManyToManyFromArray(&$ret, $instance, $array, $class, $parser) {
158
		$continue=true;
159
		$accessorToMember="get" . ucfirst($parser->getInversedBy());
160
		$myPkAccessor="get" . ucfirst($parser->getMyPk());
161
162
		if (!method_exists($instance, $myPkAccessor)) {
163
			Logger::warn("ManyToMany", "L'accesseur au membre clé primaire " . $myPkAccessor . " est manquant pour " . $class);
164
		}
165
		if (count($array) > 0)
166
			$continue=method_exists($array[0], $accessorToMember);
167
		if ($continue) {
168
			foreach ( $array as $targetEntityInstance ) {
169
				$instances=$targetEntityInstance->$accessorToMember();
170
				if (is_array($instances)) {
171
					foreach ( $instances as $inst ) {
172
						if ($inst->$myPkAccessor() == $instance->$myPkAccessor())
173
							array_push($ret, $targetEntityInstance);
174
					}
175
				}
176
			}
177
		} else {
178
			Logger::warn("ManyToMany", "L'accesseur au membre " . $parser->getInversedBy() . " est manquant pour " . $parser->getTargetEntity());
179
		}
180
	}
181
182
	/**
183
	 * Returns an array of $className objects from the database
184
	 * @param string $className class name of the model to load
185
	 * @param string $condition Part following the WHERE of an SQL statement
186
	 * @param boolean $loadManyToOne if true, charges associate members with manyToOne association
187
	 * @param boolean $loadOneToMany if true, charges associate members with oneToMany association
188
	 * @param boolean $useCache use the active cache if true
189
	 * @return array
190
	 */
191
	public static function getAll($className, $condition='', $loadManyToOne=true, $loadOneToMany=false,$useCache=NULL) {
192
		$objects=array ();
193
		$invertedJoinColumns=null;
194
		$oneToManyFields=null;
195
		$metaDatas=OrmUtils::getModelMetadata($className);
196
		$tableName=$metaDatas["#tableName"];
197
		if ($loadManyToOne && isset($metaDatas["#invertedJoinColumn"]))
198
			$invertedJoinColumns=$metaDatas["#invertedJoinColumn"];
199
		if ($loadOneToMany && isset($metaDatas["#oneToMany"])) {
200
			$oneToManyFields=$metaDatas["#oneToMany"];
201
		}
202
		$condition=SqlUtils::checkWhere($condition);
203
		$members=\array_diff($metaDatas["#fieldNames"],$metaDatas["#notSerializable"]);
204
		$query=self::$db->prepareAndExecute($tableName, $condition,$members,$useCache);
205
		Logger::log("getAll", "SELECT * FROM " . $tableName . $condition);
206
		$oneToManyQueries=[];
207
		$manyToOneQueries=[];
208
209
		foreach ( $query as $row ) {
210
			$object=self::loadObjectFromRow($row, $className, $invertedJoinColumns, $oneToManyFields,$members, $oneToManyQueries,$manyToOneQueries);
211
			$key=OrmUtils::getFirstKeyValue($object);
212
			$objects[$key]=$object;
213
		}
214
215
		if($loadManyToOne && \sizeof($manyToOneQueries)>0){
216
			self::_affectsObjectsFromArray($manyToOneQueries, $objects, function($object,$member,$manyToOneObjects,$fkField){
217
				self::affectsManyToOneFromArray($object,$member,$manyToOneObjects,$fkField);
218
			});
219
		}
220
221
		if($loadOneToMany && \sizeof($oneToManyQueries)>0){
222
			self::_affectsObjectsFromArray($oneToManyQueries, $objects, function($object,$member,$relationObjects,$fkField){
223
				self::affectsOneToManyFromArray($object,$member,$relationObjects,$fkField);
224
			});
225
		}
226
		return $objects;
227
	}
228
229
	private static function _affectsObjectsFromArray($queries,$objects,$affectsCallback,$useCache=NULL){
230
		foreach ($queries as $key=>$conditions){
231
			list($class,$member,$fkField)=\explode("|", $key);
232
			$condition=\implode(" OR ", $conditions);
233
			$relationObjects=self::getAll($class,$condition,true,false,$useCache);
234
			foreach ($objects as $object){
235
				$affectsCallback($object, $member,$relationObjects,$fkField);
236
			}
237
		}
238
	}
239
240
	private static function affectsManyToOneFromArray($object,$member,$manyToOneObjects,$fkField){
241
		$class=\get_class($object);
242
		if(isset($object->$fkField)){
243
			$value=$manyToOneObjects[$object->$fkField];
244
			self::setToMember($member, $object, $value, $class, "getManyToOne");
245
		}
246
	}
247
248
	/**
249
	 * @param array $row
250
	 * @param string $className
251
	 * @param array $invertedJoinColumns
252
	 * @param array $oneToManyFields
253
	 * @param array $members
254
	 * @param array $oneToManyQueries
255
	 * @param array $manyToOneQueries
256
	 * @return object
257
	 */
258
	private static function loadObjectFromRow($row, $className, $invertedJoinColumns, $oneToManyFields, $members,&$oneToManyQueries,&$manyToOneQueries) {
259
		$o=new $className();
260
		foreach ( $row as $k => $v ) {
261
			if(($field=\array_search($k, $members))!==false){
262
				$accesseur="set" . ucfirst($field);
263
				if (method_exists($o, $accesseur)) {
264
					$o->$accesseur($v);
265
				}
266
			}
267
			$o->_rest[$k]=$v;
268
			if (isset($invertedJoinColumns) && isset($invertedJoinColumns[$k])) {
269
				$fk="_".$k;
270
				$o->$fk=$v;
271
				self::prepareManyToOne($manyToOneQueries,$v, $fk,$invertedJoinColumns[$k]);
272
			}
273
		}
274
		if (isset($oneToManyFields)) {
275
			foreach ( $oneToManyFields as $k => $annot ) {
276
				self::prepareOneToMany($oneToManyQueries,$o, $k, $annot);
277
			}
278
		}
279
		return $o;
280
	}
281
282
283
	/**
284
	 * Prepares members associated with $instance with a oneToMany type relationship
285
	 * @param $ret array of sql conditions
286
	 * @param object $instance
287
	 * @param string $member Member on which a OneToMany annotation must be present
288
	 * @param array $annot used internally
289
	 */
290
	private static function prepareOneToMany(&$ret,$instance, $member, $annot=null) {
291
		$class=get_class($instance);
292
		if (!isset($annot))
293
			$annot=OrmUtils::getAnnotationInfoMember($class, "#oneToMany", $member);
294
			if ($annot !== false) {
295
				$fkAnnot=OrmUtils::getAnnotationInfoMember($annot["className"], "#joinColumn", $annot["mappedBy"]);
296
				if ($fkAnnot !== false) {
297
					$fkv=OrmUtils::getFirstKeyValue($instance);
298
					$key=$annot["className"]."|".$member."|".$annot["mappedBy"];
299
					if(!isset($ret[$key])){
300
						$ret[$key]=[];
301
					}
302
					$ret[$key][$fkv]=$fkAnnot["name"] . "='" . $fkv . "'";
303
				}
304
			}
305
	}
306
307
	/**
308
	 * Prepares members associated with $instance with a manyToOne type relationship
309
	 * @param $ret array of sql conditions
310
	 * @param mixed $value
311
	 * @param string $fkField
312
	 * @param array $annotationArray
313
	 */
314
	private static function prepareManyToOne(&$ret, $value, $fkField,$annotationArray) {
315
		$member=$annotationArray["member"];
316
		$fk=OrmUtils::getFirstKey($annotationArray["className"]);
317
		$key=$annotationArray["className"]."|".$member."|".$fkField;
318
		if(!isset($ret[$key])){
319
			$ret[$key]=[];
320
		}
321
		$ret[$key][$value]=$fk . "='" . $value . "'";
322
	}
323
324
	/**
325
	 * Returns the number of objects of $className from the database respecting the condition possibly passed as parameter
326
	 * @param string $className complete classname of the model to load
327
	 * @param string $condition Part following the WHERE of an SQL statement
328
	 * @return int count of objects
329
	 */
330
	public static function count($className, $condition='') {
331
		$tableName=OrmUtils::getTableName($className);
332
		if ($condition != '')
333
			$condition=" WHERE " . $condition;
334
		return self::$db->query("SELECT COUNT(*) FROM " . $tableName . $condition)->fetchColumn();
335
	}
336
337
	/**
338
	 * Returns an instance of $className from the database, from $keyvalues values of the primary key
339
	 * @param String $className complete classname of the model to load
340
	 * @param Array|string $keyValues primary key values or condition
341
	 * @param $loadManyToOne if true, charges associate members with manyToOne association
342
	 * @param $loadOneToMany if true, charges associate members with oneToMany association
343
	 * @param boolean $useCache use cache if true
344
	 * @return object the instance loaded or null if not found
345
	 */
346
	public static function getOne($className, $keyValues, $loadManyToOne=true, $loadOneToMany=false, $useCache=NULL) {
347
		if (!is_array($keyValues)) {
348
			if (strrpos($keyValues, "=") === false && strrpos($keyValues, ">") === false && strrpos($keyValues, "<") === false) {
349
				$keyValues="`" . OrmUtils::getFirstKey($className) . "`='" . $keyValues . "'";
350
			}
351
		}
352
		$condition=SqlUtils::getCondition($keyValues,$className);
353
		$limit="";
354
		if(\stripos($condition, " limit ")===false)
355
			$limit=" limit 1";
356
		$retour=self::getAll($className, $condition.$limit, $loadManyToOne, $loadOneToMany,$useCache);
357
		if (sizeof($retour) < 1){
358
			return null;
359
		}
360
		return \reset($retour);
361
	}
362
363
	/**
364
	 * Establishes the connection to the database using the past parameters
365
	 * @param string $dbType
366
	 * @param string $dbName
367
	 * @param string $serverName
368
	 * @param string $port
369
	 * @param string $user
370
	 * @param string $password
371
	 * @param array $options
372
	 * @param boolean $cache
373
	 */
374
	public static function connect($dbType,$dbName, $serverName="127.0.0.1", $port="3306", $user="root", $password="", $options=[],$cache=false) {
375
		self::$db=new Database($dbType,$dbName, $serverName, $port, $user, $password, $options,$cache);
376
		try {
377
			self::$db->connect();
378
		} catch (\Exception $e) {
379
			Logger::error("DAO", $e->getMessage());
380
			throw $e;
381
		}
382
	}
383
384
	/**
385
	 * Returns true if the connection to the database is estabished
386
	 * @return boolean
387
	 */
388
	public static function isConnected(){
389
		return self::$db!==null && (self::$db instanceof Database) && self::$db->isConnected();
390
	}
391
}
392