Test Failed
Push — master ( b46f28...e0ce72 )
by Jean-Christophe
12:10
created

DAO::pool()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 6
Bugs 0 Features 1
Metric Value
eloc 3
c 6
b 0
f 1
dl 0
loc 5
ccs 0
cts 4
cp 0
rs 10
cc 2
nc 2
nop 1
crap 6
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\traits\DAOUpdatesTrait;
10
use Ubiquity\orm\traits\DAORelationsTrait;
11
use Ubiquity\orm\parser\ConditionParser;
12
use Ubiquity\orm\traits\DAOUQueries;
13
use Ubiquity\orm\traits\DAOCoreTrait;
14
use Ubiquity\orm\traits\DAORelationsPrepareTrait;
15
use Ubiquity\exceptions\DAOException;
16
use Ubiquity\orm\traits\DAORelationsAssignmentsTrait;
17
use Ubiquity\orm\parser\Reflexion;
18
use Ubiquity\orm\traits\DAOTransactionsTrait;
19
use Ubiquity\controllers\Startup;
20
use Ubiquity\cache\CacheManager;
21
22
/**
23
 * Gateway class between database and object model.
24
 * This class is part of Ubiquity
25
 *
26
 * @author jcheron <[email protected]>
27
 * @version 1.2.1
28
 *
29
 */
30
class DAO {
31
	use DAOCoreTrait,DAOUpdatesTrait,DAORelationsTrait,DAORelationsPrepareTrait,DAORelationsAssignmentsTrait,DAOUQueries,DAOTransactionsTrait;
32
33
	/**
34
	 *
35
	 * @var Database
36
	 */
37
	public static $db;
38
	public static $useTransformers = false;
39
	public static $transformerOp = 'transform';
40
	private static $conditionParsers = [ ];
41
	protected static $modelsDatabase = [ ];
42
43
	protected static function getDb($model) {
44 76
		return self::getDatabase ( self::$modelsDatabase [$model] ?? 'default');
45 76
	}
46
47
	/**
48
	 * Initialize pooling (To invoke during Swoole startup)
49
	 *
50
	 * @param array $config
51
	 * @param ?string $offset
52
	 */
53
	public static function initPooling(&$config, $offset = null) {
54
		$dbConfig = self::getDbOffset ( $config, $offset );
55
		$wrapperClass = $dbConfig ['wrapper'] ?? \Ubiquity\db\providers\pdo\PDOWrapper::class;
56
		if (\method_exists ( $wrapperClass, 'getPoolClass' )) {
57
			$poolClass = \call_user_func ( $wrapperClass . '::getPoolClass' );
58
			if (\class_exists ( $poolClass, true )) {
59
				self::$pool = new $poolClass ( $config, $offset );
0 ignored issues
show
Bug Best Practice introduced by
The property pool does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
60
			} else {
61
				throw new DAOException ( $poolClass . ' class does not exists!' );
62
			}
63
		} else {
64
			throw new DAOException ( $wrapperClass . ' does not support connection pooling!' );
65
		}
66
		self::startDatabase ( $config, $offset );
67
	}
68
69
	/**
70
	 * Loads member associated with $instance by a ManyToOne relationship
71
	 *
72
	 * @param object|array $instance The instance object or an array with [classname,id]
73
	 * @param string $member The member to load
74
	 * @param boolean|array $included if true, loads associate members with associations, if array, example : ["client.*","commands"]
75
	 * @param boolean|null $useCache
76
	 */
77
	public static function getManyToOne($instance, $member, $included = false, $useCache = NULL) {
78 5
		$classname = self::getClass_ ( $instance );
79 5
		if (is_array ( $instance )) {
80 5
			$instance = self::getById ( $classname, $instance [1], false, $useCache );
81 1
		}
82
		$fieldAnnot = OrmUtils::getMemberJoinColumns ( $classname, $member );
83 5
		if ($fieldAnnot !== null) {
84 5
			$annotationArray = $fieldAnnot [1];
85 5
			$member = $annotationArray ["member"];
86 5
			$value = Reflexion::getMemberValue ( $instance, $member );
87 5
			$key = OrmUtils::getFirstKey ( $annotationArray ["className"] );
88 5
			$kv = array ($key => $value );
89 5
			$obj = self::getById ( $annotationArray ["className"], $kv, $included, $useCache );
90 5
			if ($obj !== null) {
91 5
				Logger::info ( "DAO", "Loading the member " . $member . " for the object " . $classname, "getManyToOne" );
92 5
				$accesseur = "set" . ucfirst ( $member );
93 5
				if (is_object ( $instance ) && method_exists ( $instance, $accesseur )) {
94 5
					$instance->$accesseur ( $obj );
95 5
					$instance->_rest [$member] = $obj->_rest;
96 5
				}
97
				return $obj;
98 5
			}
99
		}
100
	}
101
102
	/**
103
	 * Assign / load the child records in the $member member of $instance.
104
	 *
105
	 * @param object|array $instance The instance object or an array with [classname,id]
106
	 * @param string $member Member on which a oneToMany annotation must be present
107
	 * @param boolean|array $included if true, loads associate members with associations, if array, example : ["client.*","commands"]
108
	 * @param boolean $useCache
109
	 * @param array $annot used internally
110
	 */
111
	public static function getOneToMany($instance, $member, $included = true, $useCache = NULL, $annot = null) {
112 8
		$ret = array ();
113 8
		$class = self::getClass_ ( $instance );
114 8
		if (! isset ( $annot )) {
115 8
			$annot = OrmUtils::getAnnotationInfoMember ( $class, "#oneToMany", $member );
116 8
		}
117
		if ($annot !== false) {
118 8
			$fkAnnot = OrmUtils::getAnnotationInfoMember ( $annot ["className"], "#joinColumn", $annot ["mappedBy"] );
119 8
			if ($fkAnnot !== false) {
120 8
				$fkv = self::getFirstKeyValue_ ( $instance );
121 8
				$db = self::getDb ( $annot ["className"] );
122 8
				$ret = self::_getAll ( $db, $annot ["className"], ConditionParser::simple ( $db->quote . $fkAnnot ["name"] . $db->quote . "= ?", $fkv ), $included, $useCache );
123 8
				if (is_object ( $instance ) && $modifier = self::getAccessor ( $member, $instance, 'getOneToMany' )) {
124 8
					self::setToMember ( $member, $instance, $ret, $modifier );
125 7
				}
126
			}
127
		}
128
		return $ret;
129 8
	}
130
131
	/**
132
	 * Assigns / loads the child records in the $member member of $instance.
133
	 * If $array is null, the records are loaded from the database
134
	 *
135
	 * @param object|array $instance The instance object or an array with [classname,id]
136
	 * @param string $member Member on which a ManyToMany annotation must be present
137
	 * @param boolean|array $included if true, loads associate members with associations, if array, example : ["client.*","commands"]
138
	 * @param array $array optional parameter containing the list of possible child records
139
	 * @param boolean $useCache
140
	 */
141
	public static function getManyToMany($instance, $member, $included = false, $array = null, $useCache = NULL) {
142 5
		$ret = [ ];
143 5
		$class = self::getClass_ ( $instance );
144 5
		$parser = new ManyToManyParser ( $class, $member );
145 5
		if ($parser->init ()) {
146 5
			if (is_null ( $array )) {
147 5
				$pk = self::getFirstKeyValue_ ( $instance );
148 5
				$quote = SqlUtils::$quote;
149 5
				$condition = " INNER JOIN " . $quote . $parser->getJoinTable () . $quote . " on " . $quote . $parser->getJoinTable () . $quote . "." . $quote . $parser->getFkField () . $quote . "=" . $quote . $parser->getTargetEntityTable () . $quote . "." . $quote . $parser->getPk () . $quote . " WHERE " . $quote . $parser->getJoinTable () . $quote . "." . $quote . $parser->getMyFkField () . $quote . "= ?";
150 5
				$targetEntityClass = $parser->getTargetEntityClass ();
151 5
				$ret = self::_getAll ( self::getDb ( $targetEntityClass ), $targetEntityClass, ConditionParser::simple ( $condition, $pk ), $included, $useCache );
152 5
			} else {
153
				$ret = self::getManyToManyFromArray ( $instance, $array, $class, $parser );
154
			}
155
			if (is_object ( $instance ) && $modifier = self::getAccessor ( $member, $instance, 'getManyToMany' )) {
156 5
				self::setToMember ( $member, $instance, $ret, $modifier );
157 4
			}
158
		}
159
		return $ret;
160 5
	}
161
162
	/**
163
	 *
164
	 * @param object $instance
165
	 * @param array $array
166
	 * @param boolean $useCache
167
	 */
168
	public static function affectsManyToManys($instance, $array = NULL, $useCache = NULL) {
169
		$metaDatas = OrmUtils::getModelMetadata ( \get_class ( $instance ) );
170
		$manyToManyFields = $metaDatas ["#manyToMany"];
171
		if (\sizeof ( $manyToManyFields ) > 0) {
172
			foreach ( $manyToManyFields as $member ) {
173
				self::getManyToMany ( $instance, $member, false, $array, $useCache );
174
			}
175
		}
176
	}
177
178
	/**
179
	 * Returns an array of $className objects from the database
180
	 *
181
	 * @param string $className class name of the model to load
182
	 * @param string $condition Part following the WHERE of an SQL statement
183
	 * @param boolean|array $included if true, loads associate members with associations, if array, example : ["client.*","commands"]
184
	 * @param array|null $parameters
185
	 * @param boolean $useCache use the active cache if true
186
	 * @return array
187
	 */
188
	public static function getAll($className, $condition = '', $included = true, $parameters = null, $useCache = NULL) {
189 25
		return self::_getAll ( self::getDb ( $className ), $className, new ConditionParser ( $condition, null, $parameters ), $included, $useCache );
190 25
	}
191
192
	public static function paginate($className, $page = 1, $rowsPerPage = 20, $condition = null, $included = true) {
193 3
		if (! isset ( $condition )) {
194 3
			$condition = "1=1";
195 1
		}
196
		return self::getAll ( $className, $condition . " LIMIT " . $rowsPerPage . " OFFSET " . (($page - 1) * $rowsPerPage), $included );
197 3
	}
198
199
	public static function getRownum($className, $ids) {
200 4
		$tableName = OrmUtils::getTableName ( $className );
201 4
		$db = self::getDb ( $className );
202 4
		$quote = $db->quote;
203 4
		self::parseKey ( $ids, $className, $quote );
204 4
		$condition = SqlUtils::getCondition ( $ids, $className );
205 4
		$keyFields = OrmUtils::getKeyFields ( $className );
206 4
		if (is_array ( $keyFields )) {
207 4
			$keys = implode ( ",", $keyFields );
208 4
		} else {
209
			$keys = "1";
210
		}
211
212
		return $db->queryColumn ( "SELECT num FROM (SELECT *, @rownum:=@rownum + 1 AS num FROM {$quote}{$tableName}{$quote}, (SELECT @rownum:=0) r ORDER BY {$keys}) d WHERE " . $condition );
213 4
	}
214
215
	/**
216
	 * Returns the number of objects of $className from the database respecting the condition possibly passed as parameter
217
	 *
218
	 * @param string $className complete classname of the model to load
219
	 * @param string $condition Part following the WHERE of an SQL statement
220
	 * @param array|null $parameters The query parameters
221
	 * @return int|false count of objects
222
	 */
223
	public static function count($className, $condition = '', $parameters = null) {
224 21
		$tableName = OrmUtils::getTableName ( $className );
225 21
		if ($condition != '') {
226 21
			$condition = " WHERE " . $condition;
227 9
		}
228
		$db = self::getDb ( $className );
229 21
		$quote = $db->quote;
230 21
		return $db->prepareAndFetchColumn ( "SELECT COUNT(*) FROM " . $quote . $tableName . $quote . $condition, $parameters );
231 21
	}
232
233
	/**
234
	 * Returns an instance of $className from the database, from $keyvalues values of the primary key or with a condition
235
	 *
236
	 * @param String $className complete classname of the model to load
237
	 * @param Array|string $condition condition or primary key values
238
	 * @param boolean|array $included if true, charges associate members with association
239
	 * @param array|null $parameters the request parameters
240
	 * @param boolean|null $useCache use cache if true
241
	 * @return object the instance loaded or null if not found
242
	 */
243
	public static function getOne($className, $condition, $included = true, $parameters = null, $useCache = NULL) {
244 20
		$conditionParser = new ConditionParser ();
245 20
		if (! isset ( $parameters )) {
246 20
			$conditionParser->addKeyValues ( $condition, $className );
247 20
		} elseif (! is_array ( $condition )) {
248
			$conditionParser->setCondition ( $condition );
249
			$conditionParser->setParams ( $parameters );
250
		} else {
251
			throw new DAOException ( "The \$keyValues parameter should not be an array if \$parameters is not null" );
252
		}
253
		return self::_getOne ( self::getDb ( $className ), $className, $conditionParser, $included, $useCache );
254 20
	}
255
256
	/**
257
	 * Returns an instance of $className from the database, from $keyvalues values of the primary key
258
	 *
259
	 * @param String $className complete classname of the model to load
260
	 * @param Array|string $keyValues primary key values or condition
261
	 * @param boolean|array $included if true, charges associate members with association
262
	 * @param array|null $parameters the request parameters
263
	 * @param boolean|null $useCache use cache if true
264
	 * @return object the instance loaded or null if not found
265
	 */
266
	public static function getById($className, $keyValues, $included = true, $useCache = NULL) {
267 23
		return self::_getOne ( self::getDb ( $className ), $className, self::getConditionParser ( $className, $keyValues ), $included, $useCache );
268 23
	}
269
270
	protected static function getConditionParser($className, $keyValues) {
271 23
		if (! isset ( self::$conditionParsers [$className] )) {
272 23
			$conditionParser = new ConditionParser ();
273 14
			$conditionParser->addKeyValues ( $keyValues, $className );
274 14
			self::$conditionParsers [$className] = $conditionParser;
275 14
		} else {
276
			self::$conditionParsers [$className]->setKeyValues ( $keyValues );
277 11
		}
278
		return self::$conditionParsers [$className];
279 23
	}
280
281
	/**
282
	 * Establishes the connection to the database using the past parameters
283
	 *
284
	 * @param string $offset
285
	 * @param string $wrapper
286
	 * @param string $dbType
287
	 * @param string $dbName
288
	 * @param string $serverName
289
	 * @param string $port
290
	 * @param string $user
291
	 * @param string $password
292
	 * @param array $options
293
	 * @param boolean $cache
294
	 */
295
	public static function connect($offset, $wrapper, $dbType, $dbName, $serverName = '127.0.0.1', $port = '3306', $user = 'root', $password = '', $options = [], $cache = false) {
296 74
		self::$db [$offset] = new Database ( $wrapper, $dbType, $dbName, $serverName, $port, $user, $password, $options, $cache, self::$pool );
297 74
		try {
298
			self::$db [$offset]->connect ();
299 74
		} catch ( \Exception $e ) {
300
			Logger::error ( "DAO", $e->getMessage () );
301
			throw new DAOException ( $e->getMessage (), $e->getCode (), $e->getPrevious () );
302
		}
303
	}
304 74
305
	/**
306
	 * Establishes the connection to the database using the $config array
307
	 *
308
	 * @param array $config the config array (Startup::getConfig())
309
	 */
310
	public static function startDatabase(&$config, $offset = null) {
311 21
		$db = $offset ? ($config ['database'] [$offset] ?? ($config ['database'] ?? [ ])) : ($config ['database'] ['default'] ?? $config ['database']);
312 21
		if ($db ['dbName'] !== '') {
313 21
			self::connect ( $offset ?? 'default', $db ['wrapper'] ?? \Ubiquity\db\providers\pdo\PDOWrapper::class, $db ['type'], $db ['dbName'], $db ['serverName'] ?? '127.0.0.1', $db ['port'] ?? 3306, $db ['user'] ?? 'root', $db ['password'] ?? '', $db ['options'] ?? [ ], $db ['cache'] ?? false);
314 21
		}
315
	}
316 21
317
	public static function getDbOffset(&$config, $offset = null) {
318 119
		return $offset ? ($config ['database'] [$offset] ?? ($config ['database'] ?? [ ])) : ($config ['database'] ['default'] ?? $config ['database']);
319 119
	}
320
321
	/**
322
	 * Returns true if the connection to the database is established
323
	 *
324
	 * @return boolean
325
	 */
326
	public static function isConnected($offset = 'default') {
327 5
		$db = self::$db [$offset] ?? false;
328 5
		return $db && ($db instanceof Database) && $db->isConnected ();
329 5
	}
330
331
	/**
332
	 * Sets the transformer operation
333
	 *
334
	 * @param string $op
335
	 */
336
	public static function setTransformerOp($op) {
337
		self::$transformerOp = $op;
338
	}
339
340
	/**
341
	 * Closes the active pdo connection to the database
342
	 */
343
	public static function closeDb($offset = 'default') {
344 32
		$db = self::$db [$offset] ?? false;
345 32
		if ($db !== false) {
346 32
			$db->close ();
347 32
		}
348
	}
349 32
350
	/**
351
	 * Defines the database connection to use for $model class
352
	 *
353
	 * @param string $model a model class
354
	 * @param string $database a database connection defined in config.php
355
	 */
356
	public static function setModelDatabase($model, $database = 'default') {
357
		self::$modelsDatabase [$model] = $database;
358
	}
359
360
	/**
361
	 * Defines the database connections to use for models classes
362
	 *
363
	 * @param array $modelsDatabase
364
	 */
365
	public static function setModelsDatabases($modelsDatabase) {
366 9
		self::$modelsDatabase = $modelsDatabase;
367 9
	}
368 9
369
	/**
370
	 * Returns the database instance defined at $offset key in config
371
	 *
372
	 * @param string $offset
373
	 * @return \Ubiquity\db\Database
374
	 */
375
	public static function getDatabase($offset = 'default') {
376 76
		if (! isset ( self::$db [$offset] )) {
377 76
			self::startDatabase ( Startup::$config, $offset );
378 20
		}
379
		SqlUtils::$quote = self::$db [$offset]->quote;
380 76
		return self::$db [$offset];
381 76
	}
382
383
	public static function getDatabases() {
384 4
		$config = Startup::getConfig ();
385 4
		if (isset ( $config ['database'] )) {
386 4
			if (isset ( $config ['database'] ['dbName'] )) {
387 4
				return [ 'default' ];
388 4
			} else {
389
				return \array_keys ( $config ['database'] );
390
			}
391
		}
392
		return [ ];
393
	}
394
395
	public static function updateDatabaseParams(array &$config, array $parameters, $offset = 'default') {
396
		if ($offset === 'default') {
397
			if (isset ( $config ['database'] [$offset] )) {
398
				foreach ( $parameters as $k => $param ) {
399
					$config ['database'] [$offset] [$k] = $param;
400
				}
401
			} else {
402
				foreach ( $parameters as $k => $param ) {
403
					$config ['database'] [$k] = $param;
404
				}
405
			}
406
		} else {
407
			if (isset ( $config ['database'] [$offset] )) {
408
				foreach ( $parameters as $k => $param ) {
409
					$config ['database'] [$offset] [$k] = $param;
410
				}
411
			}
412
		}
413
	}
414
415
	public static function start() {
416 39
		self::$modelsDatabase = CacheManager::getModelsDatabases ();
417 39
	}
418
}
419