Completed
Push — master ( 3845e2...5afe3b )
by Jean-Christophe
02:10
created

DAO::getManyToMany()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 16

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
dl 0
loc 16
rs 9.7333
c 1
b 0
f 0
cc 3
nc 3
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
use Ubiquity\orm\traits\DAORelationsTrait;
12
use Ubiquity\orm\parser\ConditionParser;
13
use Ubiquity\orm\traits\DAOUQueries;
14
15
/**
16
 * Gateway class between database and object model
17
 * @author jc
18
 * @version 1.1.0.0
19
 * @package orm
20
 */
21
class DAO {
22
	use DAOUpdatesTrait,DAORelationsTrait,DAOUQueries;
23
	
24
	
25
	/**
26
	 * @var Database
27
	 */
28
	public static $db;
29
30
	/**
31
	 * Loads member associated with $instance by a ManyToOne relationship
32
	 * @param object $instance
33
	 * @param string $member
34
	 * @param boolean|array $included if true, loads associate members with associations, if array, example : ["client.*","commands"]
35
	 * @param boolean $useCache
36
	 */
37
	public static function getManyToOne($instance, $member, $included=false,$useCache=NULL) {
38
		$fieldAnnot=OrmUtils::getMemberJoinColumns($instance, $member);
39
		if($fieldAnnot!==null){
40
			$annotationArray=$fieldAnnot[1];
41
			$member=$annotationArray["member"];
42
			$value=Reflexion::getMemberValue($instance, $member);
43
			$key=OrmUtils::getFirstKey($annotationArray["className"]);
44
			$kv=array ($key => $value );
45
			$obj=self::getOne($annotationArray["className"], $kv, $included, $useCache);
0 ignored issues
show
Bug introduced by
It seems like $useCache defined by parameter $useCache on line 37 can also be of type boolean; however, Ubiquity\orm\DAO::getOne() does only seem to accept array|null, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
46
			if ($obj !== null) {
47
				Logger::info("DAO", "Loading the member " . $member . " for the object " . \get_class($instance),"getManyToOne");
48
				$accesseur="set" . ucfirst($member);
49
				if (method_exists($instance, $accesseur)) {
50
					$instance->$accesseur($obj);
51
					$instance->_rest[$member]=$obj->_rest;
52
					return $obj;
53
				}
54
			}
55
		}
56
	}
57
58
	private static function _getOneToManyFromArray(&$ret, $array, $fkv, $mappedBy) {
59
		$elementAccessor="get" . ucfirst($mappedBy);
60
		foreach ( $array as $element ) {
61
			$elementRef=$element->$elementAccessor();
62
			if (!is_null($elementRef)) {
63
				if(is_object($elementRef))
64
					$idElementRef=OrmUtils::getFirstKeyValue($elementRef);
65
				else
66
					$idElementRef=$elementRef;
67
				if ($idElementRef == $fkv)
68
					$ret[]=$element;
69
			}
70
		}
71
	}
72
73
	/**
74
	 * Assign / load the child records in the $member member of $instance.
75
	 * @param object $instance
76
	 * @param string $member Member on which a oneToMany annotation must be present
77
	 * @param boolean|array $included if true, loads associate members with associations, if array, example : ["client.*","commands"]
78
	 * @param boolean $useCache
79
	 * @param array $annot used internally
80
	 */
81
	public static function getOneToMany($instance, $member, $included=true,$useCache=NULL, $annot=null) {
82
		$ret=array ();
83
		$class=get_class($instance);
84
		if (!isset($annot))
85
			$annot=OrmUtils::getAnnotationInfoMember($class, "#oneToMany", $member);
86
			if ($annot !== false) {
87
				$fkAnnot=OrmUtils::getAnnotationInfoMember($annot["className"], "#joinColumn", $annot["mappedBy"]);
88
				if ($fkAnnot !== false) {
89
					$fkv=OrmUtils::getFirstKeyValue($instance);
90
					$ret=self::_getAll($annot["className"], ConditionParser::simple($fkAnnot["name"] . "= ?",$fkv), $included, $useCache);
91
					self::setToMember($member, $instance, $ret, $class, "getOneToMany");
92
				}
93
			}
94
			return $ret;
95
	}
96
97
	/**
98
	 * @param object $instance
99
	 * @param string $member
100
	 * @param array $array
101
	 * @param string $mappedBy
102
	 */
103
	public static function affectsOneToManyFromArray($instance, $member, $array=null, $mappedBy=null) {
104
		$ret=array ();
105
		$class=get_class($instance);
106
		if (!isset($mappedBy)){
107
			$annot=OrmUtils::getAnnotationInfoMember($class, "#oneToMany", $member);
108
			$mappedBy=$annot["mappedBy"];
109
		}
110
		if ($mappedBy !== false) {
111
				$fkv=OrmUtils::getFirstKeyValue($instance);
112
				self::_getOneToManyFromArray($ret, $array, $fkv, $mappedBy);
113
				self::setToMember($member, $instance, $ret, $class, "getOneToMany");
114
		}
115
		return $ret;
116
	}
117
118
	private static function setToMember($member, $instance, $value, $class, $part) {
119
		$accessor="set" . ucfirst($member);
120
		if (method_exists($instance, $accessor)) {
121
			Logger::info("DAO", "Affectation de " . $member . " pour l'objet " . $class,$part);
122
			$instance->$accessor($value);
123
			$instance->_rest[$member]=$value;
124
		} else {
125
			Logger::warn("DAO", "L'accesseur " . $accessor . " est manquant pour " . $class,$part);
126
		}
127
	}
128
129
	/**
130
	 * Assigns / loads the child records in the $member member of $instance.
131
	 * If $ array is null, the records are loaded from the database
132
	 * @param object $instance
133
	 * @param string $member Member on which a ManyToMany annotation must be present
134
	 * @param boolean|array $included if true, loads associate members with associations, if array, example : ["client.*","commands"]
135
	 * @param array $array optional parameter containing the list of possible child records
136
	 * @param boolean $useCache
137
	 */
138
	public static function getManyToMany($instance, $member,$included=false,$array=null,$useCache=NULL){
139
		$ret=[];
140
		$class=get_class($instance);
141
		$parser=new ManyToManyParser($instance, $member);
142
		if ($parser->init()) {
143
			if (is_null($array)) {
144
				$accessor="get" . ucfirst($parser->getMyPk());
145
				$condition=" INNER JOIN `" . $parser->getJoinTable() . "` on `".$parser->getJoinTable()."`.`".$parser->getFkField()."`=`".$parser->getTargetEntityTable()."`.`".$parser->getPk()."` WHERE `".$parser->getJoinTable()."`.`". $parser->getMyFkField() . "`= ?";
146
				$ret=self::_getAll($parser->getTargetEntityClass(),ConditionParser::simple($condition, $instance->$accessor()),$included,$useCache);
147
			}else{
148
				$ret=self::getManyToManyFromArray($instance, $array, $class, $parser);
149
			}
150
			self::setToMember($member, $instance, $ret, $class, "getManyToMany");
151
		}
152
		return $ret;
153
	}
154
155
	/**
156
	 * @param object $instance
157
	 * @param array $array
158
	 * @param boolean $useCache
159
	 */
160
	public static function affectsManyToManys($instance,$array=NULL,$useCache=NULL){
161
		$metaDatas=OrmUtils::getModelMetadata(\get_class($instance));
162
		$manyToManyFields=$metaDatas["#manyToMany"];
163
		if(\sizeof($manyToManyFields)>0){
164
			foreach ($manyToManyFields as $member){
165
				self::getManyToMany($instance, $member,false,$array,$useCache);
166
			}
167
		}
168
	}
169
170
	private static function getManyToManyFromArray($instance, $array, $class, $parser) {
171
		$ret=[];
172
		$continue=true;
173
		$accessorToMember="get" . ucfirst($parser->getInversedBy());
174
		$myPkAccessor="get" . ucfirst($parser->getMyPk());
175
176
		if (!method_exists($instance, $myPkAccessor)) {
177
			Logger::warn("DAO", "L'accesseur au membre clé primaire " . $myPkAccessor . " est manquant pour " . $class,"ManyToMany");
178
		}
179
		if (count($array) > 0){
180
			$continue=method_exists(reset($array), $accessorToMember);
181
		}
182
		if ($continue) {
183
			foreach ( $array as $targetEntityInstance ) {
184
				$instances=$targetEntityInstance->$accessorToMember();
185
				if (is_array($instances)) {
186
					foreach ( $instances as $inst ) {
187
						if ($inst->$myPkAccessor() == $instance->$myPkAccessor())
188
							array_push($ret, $targetEntityInstance);
189
					}
190
				}
191
			}
192
		} else {
193
			Logger::warn("DAO", "L'accesseur au membre " . $parser->getInversedBy() . " est manquant pour " . $parser->getTargetEntity(),"ManyToMany");
194
		}
195
		return $ret;
196
	}
197
198
	/**
199
	 * Returns an array of $className objects from the database
200
	 * @param string $className class name of the model to load
201
	 * @param string $condition Part following the WHERE of an SQL statement
202
	 * @param boolean|array $included if true, loads associate members with associations, if array, example : ["client.*","commands"]
203
	 * @param array|null $parameters
204
	 * @param boolean $useCache use the active cache if true
205
	 * @return array
206
	 */
207
	public static function getAll($className, $condition='', $included=true,$parameters=null,$useCache=NULL) {
208
		return self::_getAll($className, new ConditionParser($condition,null,$parameters),$included,$useCache);
209
	}
210
	
211
	protected static function _getOne($className,ConditionParser $conditionParser,$included,$useCache){
212
		$conditionParser->limitOne();
213
		$retour=self::_getAll($className, $conditionParser, $included,$useCache);
214
		if (sizeof($retour) < 1){
215
			return null;
216
		}
217
		return \reset($retour);
218
	}
219
	
220
221
	
222
	/**
223
	 * @param string $className
224
	 * @param ConditionParser $conditionParser
225
	 * @param boolean|array $included
226
	 * @param boolean $useCache
227
	 * @return array
228
	 */
229
	protected static function _getAll($className, ConditionParser $conditionParser, $included=true,$useCache=NULL) {
230
		$included=self::getIncludedForStep($included);
231
		$objects=array ();
232
		$invertedJoinColumns=null;
233
		$oneToManyFields=null;
234
		$manyToManyFields=null;
235
		
236
		$metaDatas=OrmUtils::getModelMetadata($className);
237
		$tableName=$metaDatas["#tableName"];
238
		$hasIncluded=$included || (is_array($included) && sizeof($included)>0);
239
		if($hasIncluded){
240
			self::_initRelationFields($included, $metaDatas, $invertedJoinColumns, $oneToManyFields, $manyToManyFields);
241
		}
242
		$condition=SqlUtils::checkWhere($conditionParser->getCondition());
243
		$members=\array_diff($metaDatas["#fieldNames"],$metaDatas["#notSerializable"]);
244
		$query=self::$db->prepareAndExecute($tableName, $condition,$members,$conditionParser->getParams(),$useCache);
245
		$oneToManyQueries=[];
246
		$manyToOneQueries=[];
247
		$manyToManyParsers=[];
248
		
249
		foreach ( $query as $row ) {
250
			$object=self::loadObjectFromRow($row, $className, $invertedJoinColumns, $oneToManyFields,$manyToManyFields,$members, $oneToManyQueries,$manyToOneQueries,$manyToManyParsers);
251
			$key=OrmUtils::getKeyValues($object);
252
			$objects[$key]=$object;
253
		}
254
		if($hasIncluded){
255
			self::_affectsRelationObjects($manyToOneQueries, $oneToManyQueries, $manyToManyParsers, $objects, $included, $useCache);
256
		}
257
		return $objects;
258
	}
259
	
260
	public static function paginate($className,$page=1,$rowsPerPage=20,$condition=null,$included=true){
261
		if(!isset($condition)){
262
			$condition="1=1";
263
		}
264
		return self::getAll($className,$condition." LIMIT ".$rowsPerPage." OFFSET ".(($page-1)*$rowsPerPage),$included);
265
	}
266
	
267
	public static function getRownum($className,$ids){
268
		$tableName=OrmUtils::getTableName($className);
269
		self::parseKey($ids,$className);
270
		$condition=SqlUtils::getCondition($ids,$className);
271
		$keys=implode(",", OrmUtils::getKeyFields($className));
272
		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);
273
	}
274
275
	/**
276
	 * @param array $row
277
	 * @param string $className
278
	 * @param array $invertedJoinColumns
279
	 * @param array $oneToManyFields
280
	 * @param array $members
281
	 * @param array $oneToManyQueries
282
	 * @param array $manyToOneQueries
283
	 * @param array $manyToManyParsers
284
	 * @return object
285
	 */
286
	private static function loadObjectFromRow($row, $className, $invertedJoinColumns, $oneToManyFields, $manyToManyFields,$members,&$oneToManyQueries,&$manyToOneQueries,&$manyToManyParsers) {
287
		$o=new $className();
288
		foreach ( $row as $k => $v ) {
289
			if(sizeof($fields=\array_keys($members,$k))>0){
290
				foreach ($fields as $field){
291
					$accesseur="set" . ucfirst($field);
292
					if (method_exists($o, $accesseur)) {
293
						$o->$accesseur($v);
294
					}
295
				}
296
			}
297
			$o->_rest[$k]=$v;
298
			if (isset($invertedJoinColumns) && isset($invertedJoinColumns[$k])) {
299
				$fk="_".$k;
300
				$o->$fk=$v;
301
				self::prepareManyToOne($manyToOneQueries,$v, $fk,$invertedJoinColumns[$k]);
302
			}
303
		}
304
		if (isset($oneToManyFields)) {
305
			foreach ( $oneToManyFields as $k => $annot ) {
306
				self::prepareOneToMany($oneToManyQueries,$o, $k, $annot);
307
			}
308
		}
309
		if (isset($manyToManyFields)) {
310
			foreach ( $manyToManyFields as $k => $annot ) {
311
				self::prepareManyToMany($manyToManyParsers,$o, $k, $annot);
312
			}
313
		}
314
		return $o;
315
	}
316
317
	/**
318
	 * Returns the number of objects of $className from the database respecting the condition possibly passed as parameter
319
	 * @param string $className complete classname of the model to load
320
	 * @param string $condition Part following the WHERE of an SQL statement
321
	 * @param array|null $parameters The query parameters
322
	 * @return int count of objects
323
	 */
324
	public static function count($className, $condition='',$parameters=null) {
325
		$tableName=OrmUtils::getTableName($className);
326
		if ($condition != '')
327
			$condition=" WHERE " . $condition;
328
		return self::$db->prepareAndFetchColumn("SELECT COUNT(*) FROM " . $tableName . $condition,$parameters);
329
	}
330
331
	/**
332
	 * Returns an instance of $className from the database, from $keyvalues values of the primary key
333
	 * @param String $className complete classname of the model to load
334
	 * @param Array|string $keyValues primary key values or condition
335
	 * @param boolean|array $included if true, charges associate members with association
336
	 * @param array|null $parameters the request parameters
337
	 * @param boolean $useCache use cache if true
338
	 * @return object the instance loaded or null if not found
339
	 */
340
	public static function getOne($className, $keyValues, $included=true,$parameters=null,$useCache=NULL) {
341
		$conditionParser=new ConditionParser();
342
		if(!isset($parameters)){
343
			$conditionParser->addKeyValues($keyValues,$className);
344
		}else{
345
			$conditionParser->setCondition($keyValues);
0 ignored issues
show
Bug introduced by
It seems like $keyValues defined by parameter $keyValues on line 340 can also be of type array; however, Ubiquity\orm\parser\Cond...nParser::setCondition() does only seem to accept string, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
346
			$conditionParser->setParams($parameters);
347
		}
348
		return self::_getOne($className, $conditionParser, $included, $useCache);
349
	}
350
	
351
	private static function parseKey(&$keyValues,$className){
352
		if (!is_array($keyValues)) {
353
			if (strrpos($keyValues, "=") === false && strrpos($keyValues, ">") === false && strrpos($keyValues, "<") === false) {
354
				$keyValues="`" . OrmUtils::getFirstKey($className) . "`='" . $keyValues . "'";
355
			}
356
		}
357
	}
358
359
	/**
360
	 * Establishes the connection to the database using the past parameters
361
	 * @param string $dbType
362
	 * @param string $dbName
363
	 * @param string $serverName
364
	 * @param string $port
365
	 * @param string $user
366
	 * @param string $password
367
	 * @param array $options
368
	 * @param boolean $cache
369
	 */
370
	public static function connect($dbType,$dbName, $serverName="127.0.0.1", $port="3306", $user="root", $password="", $options=[],$cache=false) {
371
		self::$db=new Database($dbType,$dbName, $serverName, $port, $user, $password, $options,$cache);
372
		try {
373
			self::$db->connect();
374
		} catch (\Exception $e) {
375
			Logger::error("DAO", $e->getMessage());
376
			throw $e;
377
		}
378
	}
379
380
	/**
381
	 * Returns true if the connection to the database is established
382
	 * @return boolean
383
	 */
384
	public static function isConnected(){
385
		return self::$db!==null && (self::$db instanceof Database) && self::$db->isConnected();
386
	}
387
}
388