Completed
Push — master ( 32110c...bc25d3 )
by Jean-Christophe
02:12
created

DAO::affectsOneToManyFromArray()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 14
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

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