Completed
Push — master ( 61f3b2...32110c )
by Jean-Christophe
02:56
created

DAO::_getOneToManyFromArray()   B

Complexity

Conditions 5
Paths 6

Size

Total Lines 14
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
dl 0
loc 14
rs 8.8571
c 1
b 0
f 0
cc 5
eloc 11
nc 6
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::info("DAO", "Chargement de " . $member . " pour l'objet " . \get_class($instance),"getManyToOne");
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::info("DAO", "Affectation de " . $member . " pour l'objet " . $class,$part);
118
			$instance->$accessor($value);
119
			$instance->_rest[$member]=$value;
120
		} else {
121
			Logger::warn("DAO", "L'accesseur " . $accessor . " est manquant pour " . $class,$part);
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("DAO", "L'accesseur au membre clé primaire " . $myPkAccessor . " est manquant pour " . $class,"ManyToMany");
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("DAO", "L'accesseur au membre " . $parser->getInversedBy() . " est manquant pour " . $parser->getTargetEntity(),"ManyToMany");
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|array $included if true, charges associate members with associations, if array, example : ["client.*","commands"]
198
	 * @param boolean $useCache use the active cache if true
199
	 * @return array
200
	 */
201
	public static function getAll($className, $condition='', $included=true,$useCache=NULL) {
202
		$included=self::getIncludedForStep($included);
203
		$objects=array ();
204
		$invertedJoinColumns=null;
205
		$oneToManyFields=null;
206
		$manyToManyFields=null;
207
		
208
		$metaDatas=OrmUtils::getModelMetadata($className);
209
		$tableName=$metaDatas["#tableName"];
210
		$hasIncluded=$included || (is_array($included) && sizeof($included)>0);
211
		if($hasIncluded){
212
			self::_initRelationFields($included, $metaDatas, $invertedJoinColumns, $oneToManyFields, $manyToManyFields);
213
		}
214
		$condition=SqlUtils::checkWhere($condition);
215
		$members=\array_diff($metaDatas["#fieldNames"],$metaDatas["#notSerializable"]);
216
		$query=self::$db->prepareAndExecute($tableName, $condition,$members,$useCache);
217
		$oneToManyQueries=[];
218
		$manyToOneQueries=[];
219
		$manyToManyParsers=[];
220
		
221
		foreach ( $query as $row ) {
222
			$object=self::loadObjectFromRow($row, $className, $invertedJoinColumns, $oneToManyFields,$manyToManyFields,$members, $oneToManyQueries,$manyToOneQueries,$manyToManyParsers);
223
			$key=OrmUtils::getKeyValues($object);
224
			$objects[$key]=$object;
225
		}
226
		if($hasIncluded){
227
			self::_affectsRelationObjects($manyToOneQueries, $oneToManyQueries, $manyToManyParsers, $objects, $included, $useCache);
228
		}
229
		return $objects;
230
	}
231
	
232
	public static function paginate($className,$page=1,$rowsPerPage=20,$condition=null,$included=true){
233
		if(!isset($condition)){
234
			$condition="1=1";
235
		}
236
		return self::getAll($className,$condition." LIMIT ".$rowsPerPage." OFFSET ".(($page-1)*$rowsPerPage),$included);
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
	/**
248
	 * @param array $row
249
	 * @param string $className
250
	 * @param array $invertedJoinColumns
251
	 * @param array $oneToManyFields
252
	 * @param array $members
253
	 * @param array $oneToManyQueries
254
	 * @param array $manyToOneQueries
255
	 * @param array $manyToManyParsers
256
	 * @return object
257
	 */
258
	private static function loadObjectFromRow($row, $className, $invertedJoinColumns, $oneToManyFields, $manyToManyFields,$members,&$oneToManyQueries,&$manyToOneQueries,&$manyToManyParsers) {
259
		$o=new $className();
260
		foreach ( $row as $k => $v ) {
261
			if(sizeof($fields=\array_keys($members,$k))>0){
262
				foreach ($fields as $field){
263
					$accesseur="set" . ucfirst($field);
264
					if (method_exists($o, $accesseur)) {
265
						$o->$accesseur($v);
266
					}
267
				}
268
			}
269
			$o->_rest[$k]=$v;
270
			if (isset($invertedJoinColumns) && isset($invertedJoinColumns[$k])) {
271
				$fk="_".$k;
272
				$o->$fk=$v;
273
				self::prepareManyToOne($manyToOneQueries,$v, $fk,$invertedJoinColumns[$k]);
274
			}
275
		}
276
		if (isset($oneToManyFields)) {
277
			foreach ( $oneToManyFields as $k => $annot ) {
278
				self::prepareOneToMany($oneToManyQueries,$o, $k, $annot);
279
			}
280
		}
281
		if (isset($manyToManyFields)) {
282
			foreach ( $manyToManyFields as $k => $annot ) {
283
				self::prepareManyToMany($manyToManyParsers,$o, $k, $annot);
284
			}
285
		}
286
		return $o;
287
	}
288
289
	/**
290
	 * Returns the number of objects of $className from the database respecting the condition possibly passed as parameter
291
	 * @param string $className complete classname of the model to load
292
	 * @param string $condition Part following the WHERE of an SQL statement
293
	 * @return int count of objects
294
	 */
295
	public static function count($className, $condition='') {
296
		$tableName=OrmUtils::getTableName($className);
297
		if ($condition != '')
298
			$condition=" WHERE " . $condition;
299
		return self::$db->query("SELECT COUNT(*) FROM " . $tableName . $condition)->fetchColumn();
300
	}
301
302
	/**
303
	 * Returns an instance of $className from the database, from $keyvalues values of the primary key
304
	 * @param String $className complete classname of the model to load
305
	 * @param Array|string $keyValues primary key values or condition
306
	 * @param boolean|array $included if true, charges associate members with association
307
	 * @param boolean $useCache use cache if true
308
	 * @return object the instance loaded or null if not found
309
	 */
310
	public static function getOne($className, $keyValues, $included=true,$useCache=NULL) {
311
		self::parseKey($keyValues,$className);
312
		$condition=SqlUtils::getCondition($keyValues,$className);
313
		$limit="";
314
		if(\stripos($condition, " limit ")===false)
315
			$limit=" limit 1";
316
		$retour=self::getAll($className, $condition.$limit, $included,$useCache);
317
		if (sizeof($retour) < 1){
318
			return null;
319
		}
320
		return \reset($retour);
321
	}
322
	
323
	private static function parseKey(&$keyValues,$className){
324
		if (!is_array($keyValues)) {
325
			if (strrpos($keyValues, "=") === false && strrpos($keyValues, ">") === false && strrpos($keyValues, "<") === false) {
326
				$keyValues="`" . OrmUtils::getFirstKey($className) . "`='" . $keyValues . "'";
327
			}
328
		}
329
	}
330
331
	/**
332
	 * Establishes the connection to the database using the past parameters
333
	 * @param string $dbType
334
	 * @param string $dbName
335
	 * @param string $serverName
336
	 * @param string $port
337
	 * @param string $user
338
	 * @param string $password
339
	 * @param array $options
340
	 * @param boolean $cache
341
	 */
342
	public static function connect($dbType,$dbName, $serverName="127.0.0.1", $port="3306", $user="root", $password="", $options=[],$cache=false) {
343
		self::$db=new Database($dbType,$dbName, $serverName, $port, $user, $password, $options,$cache);
344
		try {
345
			self::$db->connect();
346
		} catch (\Exception $e) {
347
			Logger::error("DAO", $e->getMessage());
348
			throw $e;
349
		}
350
	}
351
352
	/**
353
	 * Returns true if the connection to the database is estabished
354
	 * @return boolean
355
	 */
356
	public static function isConnected(){
357
		return self::$db!==null && (self::$db instanceof Database) && self::$db->isConnected();
358
	}
359
}
360