Completed
Push — master ( 5f0535...74f4b9 )
by Jean-Christophe
01:50
created

DAO::connect()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 9
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 9
rs 9.6666
c 0
b 0
f 0
cc 2
eloc 7
nc 2
nop 8

How to fix   Many Parameters   

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

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