Completed
Push — master ( 9bd3b4...2d1963 )
by Jean-Christophe
02:37
created

DAO::setToMember()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 10
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 10
rs 9.4285
c 0
b 0
f 0
cc 2
eloc 8
nc 2
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
14
/**
15
 * Gateway class between database and object model
16
 * @author jc
17
 * @version 1.1.0.0
18
 * @package orm
19
 */
20
class DAO {
21
	use DAOUpdatesTrait,DAORelationsTrait;
22
	
23
	/**
24
	 * @var Database
25
	 */
26
	public static $db;
27
28
	/**
29
	 * Loads member associated with $instance by a ManyToOne type relationship
30
	 * @param object $instance
31
	 * @param string $member
32
	 * @param boolean|array $included if true, loads associate members with associations, if array, example : ["client.*","commands"]
33
	 * @param boolean $useCache
34
	 */
35
	public static function getManyToOne($instance, $member, $included=false,$useCache=NULL) {
36
		$fieldAnnot=OrmUtils::getMemberJoinColumns($instance, $member);
37
		if($fieldAnnot!==null){
38
			$field=$fieldAnnot[0];
0 ignored issues
show
Unused Code introduced by
$field is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

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