Completed
Push — master ( 27ae86...5d0263 )
by Jean-Christophe
02:00
created

DAO::getOne()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 12
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 3
Bugs 0 Features 0
Metric Value
dl 0
loc 12
rs 9.4285
c 3
b 0
f 0
cc 3
eloc 10
nc 4
nop 4
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
use Ubiquity\orm\traits\DAORelationsTrait;
12
13
/**
14
 * Gateway class between database and object model
15
 * @author jc
16
 * @version 1.1.0.0
17
 * @package orm
18
 */
19
class DAO {
20
	use DAOUpdatesTrait,DAORelationsTrait;
21
	
22
	/**
23
	 * @var Database
24
	 */
25
	public static $db;
26
27
	/**
28
	 * Loads member associated with $instance by a ManyToOne type relationship
29
	 * @param object $instance
30
	 * @param string $member
31
	 * @param boolean $useCache
32
	 */
33
	public static function getManyToOne($instance, $member, $useCache=NULL) {
34
		$fieldAnnot=OrmUtils::getMemberJoinColumns($instance, $member);
35
		if($fieldAnnot!==null){
36
			$field=$fieldAnnot[0];
37
			$value=Reflexion::getMemberValue($instance, $field);
38
			$annotationArray=$fieldAnnot[1];
39
			$member=$annotationArray["member"];
40
			$key=OrmUtils::getFirstKey($annotationArray["className"]);
41
			$kv=array ($key => $value );
42
			$obj=self::getOne($annotationArray["className"], $kv, false, $useCache);
43
			if ($obj !== null) {
44
				Logger::log("getManyToOne", "Chargement de " . $member . " pour l'objet " . \get_class($instance));
45
				$accesseur="set" . ucfirst($member);
46
				if (method_exists($instance, $accesseur)) {
47
					$instance->$accesseur($obj);
48
					$instance->_rest[$member]=$obj->_rest;
49
					return;
50
				}
51
			}
52
		}
53
	}
54
55
	private static function _getOneToManyFromArray(&$ret, $array, $fkv, $mappedBy) {
56
		$elementAccessor="get" . ucfirst($mappedBy);
57
		foreach ( $array as $element ) {
58
			$elementRef=$element->$elementAccessor();
59
			if (!is_null($elementRef)) {
60
				if(is_object($elementRef))
61
					$idElementRef=OrmUtils::getFirstKeyValue($elementRef);
62
				else
63
					$idElementRef=$elementRef;
64
				if ($idElementRef == $fkv)
65
					$ret[]=$element;
66
			}
67
		}
68
	}
69
70
	/**
71
	 * Assign / load the child records in the $member member of $instance.
72
	 * @param object $instance
73
	 * @param string $member Member on which a oneToMany annotation must be present
74
	 * @param boolean $useCache
75
	 * @param array $annot used internally
76
	 */
77
	public static function getOneToMany($instance, $member, $useCache=NULL, $annot=null) {
78
		$ret=array ();
79
		$class=get_class($instance);
80
		if (!isset($annot))
81
			$annot=OrmUtils::getAnnotationInfoMember($class, "#oneToMany", $member);
82
			if ($annot !== false) {
83
				$fkAnnot=OrmUtils::getAnnotationInfoMember($annot["className"], "#joinColumn", $annot["mappedBy"]);
84
				if ($fkAnnot !== false) {
85
					$fkv=OrmUtils::getFirstKeyValue($instance);
86
					$ret=self::getAll($annot["className"], $fkAnnot["name"] . "='" . $fkv . "'", true, $useCache);
87
					self::setToMember($member, $instance, $ret, $class, "getOneToMany");
88
				}
89
			}
90
			return $ret;
91
	}
92
93
	/**
94
	 * @param object $instance
95
	 * @param string $member
96
	 * @param array $array
97
	 * @param string $mappedBy
98
	 */
99
	public static function affectsOneToManyFromArray($instance, $member, $array=null, $mappedBy=null) {
100
		$ret=array ();
101
		$class=get_class($instance);
102
		if (!isset($mappedBy)){
103
			$annot=OrmUtils::getAnnotationInfoMember($class, "#oneToMany", $member);
104
			$mappedBy=$annot["mappedBy"];
105
		}
106
		if ($mappedBy !== false) {
107
				$fkv=OrmUtils::getFirstKeyValue($instance);
108
				self::_getOneToManyFromArray($ret, $array, $fkv, $mappedBy);
109
				self::setToMember($member, $instance, $ret, $class, "getOneToMany");
110
		}
111
		return $ret;
112
	}
113
114
	private static function setToMember($member, $instance, $value, $class, $part) {
115
		$accessor="set" . ucfirst($member);
116
		if (method_exists($instance, $accessor)) {
117
			Logger::log($part, "Affectation de " . $member . " pour l'objet " . $class);
118
			$instance->$accessor($value);
119
			$instance->_rest[$member]=$value;
120
		} else {
121
			Logger::warn($part, "L'accesseur " . $accessor . " est manquant pour " . $class);
122
		}
123
	}
124
125
	/**
126
	 * Assigns / loads the child records in the $member member of $instance.
127
	 * If $ array is null, the records are loaded from the database
128
	 * @param object $instance
129
	 * @param string $member Member on which a ManyToMany annotation must be present
130
	 * @param array $array optional parameter containing the list of possible child records
131
	 * @param boolean $useCache
132
	 */
133
	public static function getManyToMany($instance, $member,$array=null,$useCache=NULL){
134
		$ret=[];
135
		$class=get_class($instance);
136
		$parser=new ManyToManyParser($instance, $member);
137
		if ($parser->init()) {
138
			if (is_null($array)) {
139
				$accessor="get" . ucfirst($parser->getMyPk());
140
				$condition=" INNER JOIN `" . $parser->getJoinTable() . "` on `".$parser->getJoinTable()."`.`".$parser->getFkField()."`=`".$parser->getTargetEntityTable()."`.`".$parser->getPk()."` WHERE `".$parser->getJoinTable()."`.`". $parser->getMyFkField() . "`='" . $instance->$accessor() . "'";
141
				$ret=self::getAll($parser->getTargetEntityClass(),$condition,true,$useCache);
142
			}else{
143
				$ret=self::getManyToManyFromArray($instance, $array, $class, $parser);
144
			}
145
			self::setToMember($member, $instance, $ret, $class, "getManyToMany");
146
		}
147
		return $ret;
148
	}
149
150
	/**
151
	 * @param object $instance
152
	 * @param array $array
153
	 * @param boolean $useCache
154
	 */
155
	public static function affectsManyToManys($instance,$array=NULL,$useCache=NULL){
156
		$metaDatas=OrmUtils::getModelMetadata(\get_class($instance));
157
		$manyToManyFields=$metaDatas["#manyToMany"];
158
		if(\sizeof($manyToManyFields)>0){
159
			foreach ($manyToManyFields as $member){
160
				self::getManyToMany($instance, $member,$array,$useCache);
161
			}
162
		}
163
	}
164
165
	private static function getManyToManyFromArray($instance, $array, $class, $parser) {
166
		$ret=[];
167
		$continue=true;
168
		$accessorToMember="get" . ucfirst($parser->getInversedBy());
169
		$myPkAccessor="get" . ucfirst($parser->getMyPk());
170
171
		if (!method_exists($instance, $myPkAccessor)) {
172
			Logger::warn("ManyToMany", "L'accesseur au membre clé primaire " . $myPkAccessor . " est manquant pour " . $class);
173
		}
174
		if (count($array) > 0){
175
			$continue=method_exists(reset($array), $accessorToMember);
176
		}
177
		if ($continue) {
178
			foreach ( $array as $targetEntityInstance ) {
179
				$instances=$targetEntityInstance->$accessorToMember();
180
				if (is_array($instances)) {
181
					foreach ( $instances as $inst ) {
182
						if ($inst->$myPkAccessor() == $instance->$myPkAccessor())
183
							array_push($ret, $targetEntityInstance);
184
					}
185
				}
186
			}
187
		} else {
188
			Logger::warn("ManyToMany", "L'accesseur au membre " . $parser->getInversedBy() . " est manquant pour " . $parser->getTargetEntity());
189
		}
190
		return $ret;
191
	}
192
193
	/**
194
	 * Returns an array of $className objects from the database
195
	 * @param string $className class name of the model to load
196
	 * @param string $condition Part following the WHERE of an SQL statement
197
	 * @param boolean $loadManyToOne if true, charges associate members with manyToOne association
0 ignored issues
show
Bug introduced by
There is no parameter named $loadManyToOne. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
198
	 * @param boolean $loadOneToMany if true, charges associate members with oneToMany association
0 ignored issues
show
Bug introduced by
There is no parameter named $loadOneToMany. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
199
	 * @param boolean $useCache use the active cache if true
200
	 * @return array
201
	 */
202
	public static function getAll($className, $condition='', $included=true,$useCache=NULL) {
203
		$included=self::getIncludedForStep($included);
204
		$objects=array ();
205
		$invertedJoinColumns=null;
206
		$oneToManyFields=null;
207
		$manyToManyFields=null;
208
		
209
		$metaDatas=OrmUtils::getModelMetadata($className);
210
		$tableName=$metaDatas["#tableName"];
211
		$hasIncluded=$included || (is_array($included) && sizeof($included)>0);
212
		if($hasIncluded){
213
			if (isset($metaDatas["#invertedJoinColumn"]))
214
				$invertedJoinColumns=$metaDatas["#invertedJoinColumn"];
215
			if (isset($metaDatas["#oneToMany"])) {
216
				$oneToManyFields=$metaDatas["#oneToMany"];
217
			}
218
			if (isset($metaDatas["#manyToMany"])) {
219
				$manyToManyFields=$metaDatas["#manyToMany"];
220
			}
221
			if(is_array($included)){
222
				if(isset($invertedJoinColumns)){
223
					$invertedJoinColumns=self::getInvertedJoinColumns($included, $invertedJoinColumns);
224
				}
225
				if(isset($oneToManyFields)){
226
					$oneToManyFields=self::getToManyFields($included, $oneToManyFields);
227
				}
228
				if(isset($manyToManyFields)){
229
					$manyToManyFields=self::getToManyFields($included, $manyToManyFields);
230
				}
231
			}
232
		}
233
		$condition=SqlUtils::checkWhere($condition);
234
		$members=\array_diff($metaDatas["#fieldNames"],$metaDatas["#notSerializable"]);
235
		$query=self::$db->prepareAndExecute($tableName, $condition,$members,$useCache);
236
		Logger::log("getAll", "SELECT * FROM " . $tableName . $condition);
237
		$oneToManyQueries=[];
238
		$manyToOneQueries=[];
239
		$manyToManyParsers=[];
240
		
241
		foreach ( $query as $row ) {
242
			$object=self::loadObjectFromRow($row, $className, $invertedJoinColumns, $oneToManyFields,$manyToManyFields,$members, $oneToManyQueries,$manyToOneQueries,$manyToManyParsers);
243
			$key=OrmUtils::getKeyValues($object);
244
			$objects[$key]=$object;
245
		}
246
		if($hasIncluded){
247
			self::_affectsRelationObjects($manyToOneQueries, $oneToManyQueries, $manyToManyParsers, $objects, $included, $useCache);
248
		}
249
		return $objects;
250
	}
251
	
252
	public static function paginate($className,$page=1,$rowsPerPage=20,$condition=null){
253
		if(!isset($condition)){
254
			$condition="1=1";
255
		}
256
		return self::getAll($className,$condition." LIMIT ".$rowsPerPage." OFFSET ".(($page-1)*$rowsPerPage));
257
	}
258
	
259
	public static function getRownum($className,$ids){
260
		$tableName=OrmUtils::getTableName($className);
261
		self::parseKey($ids,$className);
262
		$condition=SqlUtils::getCondition($ids,$className);
263
		$keys=implode(",", OrmUtils::getKeyFields($className));
264
		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);
265
	}
266
267
	/**
268
	 * @param array $row
269
	 * @param string $className
270
	 * @param array $invertedJoinColumns
271
	 * @param array $oneToManyFields
272
	 * @param array $members
273
	 * @param array $oneToManyQueries
274
	 * @param array $manyToOneQueries
275
	 * @return object
276
	 */
277
	private static function loadObjectFromRow($row, $className, $invertedJoinColumns, $oneToManyFields, $manyToManyFields,$members,&$oneToManyQueries,&$manyToOneQueries,&$manyToManyParsers) {
278
		$o=new $className();
279
		foreach ( $row as $k => $v ) {
280
			if(sizeof($fields=\array_keys($members,$k))>0){
281
				foreach ($fields as $field){
282
					$accesseur="set" . ucfirst($field);
283
					if (method_exists($o, $accesseur)) {
284
						$o->$accesseur($v);
285
					}
286
				}
287
			}
288
			$o->_rest[$k]=$v;
289
			if (isset($invertedJoinColumns) && isset($invertedJoinColumns[$k])) {
290
				$fk="_".$k;
291
				$o->$fk=$v;
292
				self::prepareManyToOne($manyToOneQueries,$v, $fk,$invertedJoinColumns[$k]);
293
			}
294
		}
295
		if (isset($oneToManyFields)) {
296
			foreach ( $oneToManyFields as $k => $annot ) {
297
				self::prepareOneToMany($oneToManyQueries,$o, $k, $annot);
298
			}
299
		}
300
		if (isset($manyToManyFields)) {
301
			foreach ( $manyToManyFields as $k => $annot ) {
302
				self::prepareManyToMany($manyToManyParsers,$o, $k, $annot);
303
			}
304
		}
305
		return $o;
306
	}
307
308
	/**
309
	 * Returns the number of objects of $className from the database respecting the condition possibly passed as parameter
310
	 * @param string $className complete classname of the model to load
311
	 * @param string $condition Part following the WHERE of an SQL statement
312
	 * @return int count of objects
313
	 */
314
	public static function count($className, $condition='') {
315
		$tableName=OrmUtils::getTableName($className);
316
		if ($condition != '')
317
			$condition=" WHERE " . $condition;
318
		return self::$db->query("SELECT COUNT(*) FROM " . $tableName . $condition)->fetchColumn();
319
	}
320
321
	/**
322
	 * Returns an instance of $className from the database, from $keyvalues values of the primary key
323
	 * @param String $className complete classname of the model to load
324
	 * @param Array|string $keyValues primary key values or condition
325
	 * @param boolean $included if true, charges associate members with association
326
	 * @param boolean $useCache use cache if true
327
	 * @return object the instance loaded or null if not found
328
	 */
329
	public static function getOne($className, $keyValues, $included=true,$useCache=NULL) {
330
		self::parseKey($keyValues,$className);
331
		$condition=SqlUtils::getCondition($keyValues,$className);
332
		$limit="";
333
		if(\stripos($condition, " limit ")===false)
334
			$limit=" limit 1";
335
		$retour=self::getAll($className, $condition.$limit, $included,$useCache);
336
		if (sizeof($retour) < 1){
337
			return null;
338
		}
339
		return \reset($retour);
340
	}
341
	
342
	private static function parseKey(&$keyValues,$className){
343
		if (!is_array($keyValues)) {
344
			if (strrpos($keyValues, "=") === false && strrpos($keyValues, ">") === false && strrpos($keyValues, "<") === false) {
345
				$keyValues="`" . OrmUtils::getFirstKey($className) . "`='" . $keyValues . "'";
346
			}
347
		}
348
	}
349
350
	/**
351
	 * Establishes the connection to the database using the past parameters
352
	 * @param string $dbType
353
	 * @param string $dbName
354
	 * @param string $serverName
355
	 * @param string $port
356
	 * @param string $user
357
	 * @param string $password
358
	 * @param array $options
359
	 * @param boolean $cache
360
	 */
361
	public static function connect($dbType,$dbName, $serverName="127.0.0.1", $port="3306", $user="root", $password="", $options=[],$cache=false) {
362
		self::$db=new Database($dbType,$dbName, $serverName, $port, $user, $password, $options,$cache);
363
		try {
364
			self::$db->connect();
365
		} catch (\Exception $e) {
366
			Logger::error("DAO", $e->getMessage());
367
			throw $e;
368
		}
369
	}
370
371
	/**
372
	 * Returns true if the connection to the database is estabished
373
	 * @return boolean
374
	 */
375
	public static function isConnected(){
376
		return self::$db!==null && (self::$db instanceof Database) && self::$db->isConnected();
377
	}
378
}
379