Completed
Branch master (099915)
by Fabio
08:02
created

TMappedStatement::setObjectProperty()   C

Complexity

Conditions 12
Paths 10

Size

Total Lines 36
Code Lines 28

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 21
CRAP Score 13.0239

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 12
eloc 28
c 1
b 0
f 0
nc 10
nop 4
dl 0
loc 36
ccs 21
cts 26
cp 0.8077
crap 13.0239
rs 6.9666

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/**
3
 * TMappedStatement and related classes.
4
 *
5
 * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
6
 * @link https://github.com/pradosoft/prado
7
 * @license https://github.com/pradosoft/prado/blob/master/LICENSE
8
 * @package Prado\Data\SqlMap\Statements
9
 */
10
11
namespace Prado\Data\SqlMap\Statements;
12
13
use Prado\Collections\TList;
14
use Prado\Data\ActiveRecord\TActiveRecord;
15
use Prado\Data\SqlMap\Configuration\TResultProperty;
16
use Prado\Data\SqlMap\Configuration\TSqlMapInsert;
17
use Prado\Data\SqlMap\Configuration\TSqlMapStatement;
18
use Prado\Data\SqlMap\DataMapper\TLazyLoadList;
19
use Prado\Data\SqlMap\DataMapper\TPropertyAccess;
20
use Prado\Data\SqlMap\DataMapper\TSqlMapExecutionException;
21
use Prado\Data\SqlMap\TSqlMapManager;
22
use Prado\Exceptions\TInvalidDataValueException;
23
24
/**
25
 * TMappedStatement class executes SQL mapped statements. Mapped Statements can
26
 * hold any SQL statement and use Parameter Maps and Result Maps for input and output.
27
 *
28
 * This class is usualy instantiated during SQLMap configuration by TSqlDomBuilder.
29
 *
30
 * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
31
 * @package Prado\Data\SqlMap\Statements
32
 * @since 3.0
33
 */
34
class TMappedStatement extends \Prado\TComponent implements IMappedStatement
35
{
36
	/**
37
	 * @var TSqlMapStatement current SQL statement.
38
	 */
39
	private $_statement;
40
41
	/**
42
	 * @var TPreparedCommand SQL command prepareer
43
	 */
44
	private $_command;
45
46
	/**
47
	 * @var TSqlMapper sqlmap used by this mapper.
0 ignored issues
show
Bug introduced by
The type Prado\Data\SqlMap\Statements\TSqlMapper was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
48
	 */
49
	private $_manager;
50
51
	/**
52
	 * @var TPostSelectBinding[] post select statement queue.
53
	 */
54
	private $_selectQueue = [];
55
56
	/**
57
	 * @var bool true when data is mapped to a particular row.
58
	 */
59
	private $_IsRowDataFound = false;
60
61
	/**
62
	 * @var TSQLMapObjectCollectionTree group by object collection tree
63
	 */
64
	private $_groupBy;
65
66
	/**
67
	 * @var Post select is to query for list.
68
	 */
69
	const QUERY_FOR_LIST = 0;
70
71
	/**
72
	 * @var Post select is to query for list.
73
	 */
74
	const QUERY_FOR_ARRAY = 1;
75
76
	/**
77
	 * @var Post select is to query for object.
78
	 */
79
	const QUERY_FOR_OBJECT = 2;
80
81
	/**
82
	 * @return string Name used to identify the TMappedStatement amongst the others.
83
	 * This the name of the SQL statement by default.
84
	 */
85 2
	public function getID()
86
	{
87 2
		return $this->_statement->ID;
0 ignored issues
show
Bug Best Practice introduced by
The property ID does not exist on Prado\Data\SqlMap\Configuration\TSqlMapStatement. Since you implemented __get, consider adding a @property annotation.
Loading history...
88
	}
89
90
	/**
91
	 * @return TSqlMapStatement The SQL statment used by this MappedStatement
92
	 */
93 9
	public function getStatement()
94
	{
95 9
		return $this->_statement;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->_statement returns the type Prado\Data\SqlMap\Configuration\TSqlMapStatement which is incompatible with the return type mandated by Prado\Data\SqlMap\Statem...atement::getStatement() of Prado\Data\SqlMap\Statements\TSqlMapStatement.

In the issue above, the returned value is violating the contract defined by the mentioned interface.

Let's take a look at an example:

interface HasName {
    /** @return string */
    public function getName();
}

class Name {
    public $name;
}

class User implements HasName {
    /** @return string|Name */
    public function getName() {
        return new Name('foo'); // This is a violation of the ``HasName`` interface
                                // which only allows a string value to be returned.
    }
}
Loading history...
96
	}
97
98
	/**
99
	 * @return TSqlMapManager The SqlMap used by this MappedStatement
100
	 */
101 118
	public function getManager()
102
	{
103 118
		return $this->_manager;
104
	}
105
106
	/**
107
	 * @return TPreparedCommand command to prepare SQL statements.
108
	 */
109 3
	public function getCommand()
110
	{
111 3
		return $this->_command;
112
	}
113
114
	/**
115
	 * Empty the group by results cache.
116
	 */
117 3
	protected function initialGroupByResults()
118
	{
119 3
		$this->_groupBy = new TSqlMapObjectCollectionTree();
120 3
	}
121
122
	/**
123
	 * Creates a new mapped statement.
124
	 * @param TSqlMapManager $sqlMap an sqlmap.
125
	 * @param TSqlMapStatement $statement An SQL statement.
126
	 */
127 2
	public function __construct(TSqlMapManager $sqlMap, TSqlMapStatement $statement)
128
	{
129 2
		$this->_manager = $sqlMap;
0 ignored issues
show
Documentation Bug introduced by
It seems like $sqlMap of type Prado\Data\SqlMap\TSqlMapManager is incompatible with the declared type Prado\Data\SqlMap\Statements\TSqlMapper of property $_manager.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
130 2
		$this->_statement = $statement;
131 2
		$this->_command = new TPreparedCommand();
132 2
		$this->initialGroupByResults();
133 2
	}
134
135 1
	public function getSqlString()
136
	{
137 1
		return $this->getStatement()->getSqlText()->getPreparedStatement()->getPreparedSql();
138
	}
139
140
	/**
141
	 * Execute SQL Query.
142
	 * @param IDbConnection $connection database connection
143
	 * @param array $sql SQL statement and parameters.
144
	 * @param mixed $command
145
	 * @param mixed $max
146
	 * @param mixed $skip
147
	 * @throws TSqlMapExecutionException if execution error or false record set.
148
	 * @throws TSqlMapQueryExecutionException if any execution error
149
	 * @return mixed record set if applicable.
150
	 */
151
	/*	protected function executeSQLQuery($connection, $sql)
152
		{
153
			try
154
			{
155
				if(!($recordSet = $connection->execute($sql['sql'],$sql['parameters'])))
156
				{
157
					throw new TSqlMapExecutionException(
158
						'sqlmap_execution_error_no_record', $this->getID(),
159
						$connection->ErrorMsg());
160
				}
161
				return $recordSet;
162
			}
163
			catch (Exception $e)
164
			{
165
				throw new TSqlMapQueryExecutionException($this->getStatement(), $e);
166
			}
167
		}*/
168
169
	/**
170
	 * Execute SQL Query with limits.
171
	 * @param IDbConnection $connection database connection
0 ignored issues
show
Bug introduced by
The type Prado\Data\SqlMap\Statements\IDbConnection was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
172
	 * @param $command
173
	 * @param int $max The maximum number of rows to return.
174
	 * @param int $skip The number of rows to skip over.
175
	 * @throws TSqlMapExecutionException if execution error or false record set.
176
	 * @throws TSqlMapQueryExecutionException if any execution error
177
	 * @return mixed record set if applicable.
178
	 */
179
	protected function executeSQLQueryLimit($connection, $command, $max, $skip)
180
	{
181
		if ($max > -1 || $skip > -1) {
182
			$maxStr = $max > 0 ? ' LIMIT ' . $max : '';
183
			$skipStr = $skip > 0 ? ' OFFSET ' . $skip : '';
184
			$command->setText($command->getText() . $maxStr . $skipStr);
185
		}
186
		$connection->setActive(true);
187
		return $command->query();
188
189
		/*//var_dump($command);
190
		try
191
		{
192
			$recordSet = $connection->selectLimit($sql['sql'],$max,$skip,$sql['parameters']);
193
			if(!$recordSet)
194
			{
195
				throw new TSqlMapExecutionException(
196
							'sqlmap_execution_error_query_for_list',
197
							$connection->ErrorMsg());
198
			}
199
			return $recordSet;
200
		}
201
		catch (Exception $e)
202
		{
203
			throw new TSqlMapQueryExecutionException($this->getStatement(), $e);
204
		}*/
205
	}
206
207
	/**
208
	 * Executes the SQL and retuns a List of result objects.
209
	 * @param IDbConnection $connection database connection
210
	 * @param mixed $parameter The object used to set the parameters in the SQL.
211
	 * @param null|object $result result collection object.
212
	 * @param int $skip The number of rows to skip over.
213
	 * @param int $max The maximum number of rows to return.
214
	 * @param null|callable $delegate row delegate handler
215
	 * @return array a list of result objects
216
	 * @see executeQueryForList()
217
	 */
218 32
	public function executeQueryForList($connection, $parameter, $result = null, $skip = -1, $max = -1, $delegate = null)
219
	{
220 32
		$sql = $this->_command->create($this->_manager, $connection, $this->_statement, $parameter, $skip, $max);
221 32
		return $this->runQueryForList($connection, $parameter, $sql, $result, $delegate);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->runQueryFo...ql, $result, $delegate) returns the type array which is incompatible with the return type mandated by Prado\Data\SqlMap\Statem...::executeQueryForList() of Prado\Data\SqlMap\Statements\TList.

In the issue above, the returned value is violating the contract defined by the mentioned interface.

Let's take a look at an example:

interface HasName {
    /** @return string */
    public function getName();
}

class Name {
    public $name;
}

class User implements HasName {
    /** @return string|Name */
    public function getName() {
        return new Name('foo'); // This is a violation of the ``HasName`` interface
                                // which only allows a string value to be returned.
    }
}
Loading history...
222
	}
223
224
	/**
225
	 * Executes the SQL and retuns a List of result objects.
226
	 *
227
	 * This method should only be called by internal developers, consider using
228
	 * <tt>executeQueryForList()</tt> first.
229
	 *
230
	 * @param IDbConnection $connection database connection
231
	 * @param mixed $parameter The object used to set the parameters in the SQL.
232
	 * @param array $sql SQL string and subsititution parameters.
233
	 * @param object $result result collection object.
234
	 * @param null|callable $delegate row delegate handler
235
	 * @return array a list of result objects
236
	 * @see executeQueryForList()
237
	 */
238 35
	public function runQueryForList($connection, $parameter, $sql, $result, $delegate = null)
239
	{
240 35
		$registry = $this->getManager()->getTypeHandlers();
241 35
		$list = $result instanceof \ArrayAccess ? $result :
242 35
							$this->_statement->createInstanceOfListClass($registry);
243 35
		$connection->setActive(true);
244 35
		$reader = $sql->query();
245
		//$reader = $this->executeSQLQueryLimit($connection, $sql, $max, $skip);
246 35
		if ($delegate !== null) {
247 1
			foreach ($reader as $row) {
248 1
				$obj = $this->applyResultMap($row);
249 1
				$param = new TResultSetListItemParameter($obj, $parameter, $list);
250 1
				$this->raiseRowDelegate($delegate, $param);
251
			}
252
		} else {
253
			//var_dump($sql,$parameter);
254 34
			foreach ($reader as $row) {
255
//				var_dump($row);
256 33
				$list[] = $this->applyResultMap($row);
257
			}
258
		}
259
260 35
		if (!$this->_groupBy->isEmpty()) {
261 1
			$list = $this->_groupBy->collect();
262 1
			$this->initialGroupByResults();
263
		}
264
265 35
		$this->executePostSelect($connection);
266 35
		$this->onExecuteQuery($sql);
267
268 35
		return $list;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $list also could return the type ArrayAccess which is incompatible with the documented return type array.
Loading history...
269
	}
270
271
	/**
272
	 * Executes the SQL and retuns all rows selected in a map that is keyed on
273
	 * the property named in the keyProperty parameter.  The value at each key
274
	 * will be the value of the property specified in the valueProperty parameter.
275
	 * If valueProperty is null, the entire result object will be entered.
276
	 * @param IDbConnection $connection database connection
277
	 * @param mixed $parameter The object used to set the parameters in the SQL.
278
	 * @param string $keyProperty The property of the result object to be used as the key.
279
	 * @param null|string $valueProperty The property of the result object to be used as the value (or null).
280
	 * @param int $skip The number of rows to skip over.
281
	 * @param int $max The maximum number of rows to return.
282
	 * @param null|callable $delegate row delegate handler
283
	 * @return array An array of object containing the rows keyed by keyProperty.
284
	 */
285 4
	public function executeQueryForMap($connection, $parameter, $keyProperty, $valueProperty = null, $skip = -1, $max = -1, $delegate = null)
286
	{
287 4
		$sql = $this->_command->create($this->_manager, $connection, $this->_statement, $parameter, $skip, $max);
288 4
		return $this->runQueryForMap($connection, $parameter, $sql, $keyProperty, $valueProperty, $delegate);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->runQueryFo...lueProperty, $delegate) returns the type array which is incompatible with the return type mandated by Prado\Data\SqlMap\Statem...t::executeQueryForMap() of Prado\Data\SqlMap\Statements\TMap.

In the issue above, the returned value is violating the contract defined by the mentioned interface.

Let's take a look at an example:

interface HasName {
    /** @return string */
    public function getName();
}

class Name {
    public $name;
}

class User implements HasName {
    /** @return string|Name */
    public function getName() {
        return new Name('foo'); // This is a violation of the ``HasName`` interface
                                // which only allows a string value to be returned.
    }
}
Loading history...
289
	}
290
291
	/**
292
	 * Executes the SQL and retuns all rows selected in a map that is keyed on
293
	 * the property named in the keyProperty parameter.  The value at each key
294
	 * will be the value of the property specified in the valueProperty parameter.
295
	 * If valueProperty is null, the entire result object will be entered.
296
	 *
297
	 * This method should only be called by internal developers, consider using
298
	 * <tt>executeQueryForMap()</tt> first.
299
	 *
300
	 * @param IDbConnection $connection database connection
301
	 * @param mixed $parameter The object used to set the parameters in the SQL.
302
	 * @param mixed $command
303
	 * @param string $keyProperty The property of the result object to be used as the key.
304
	 * @param null|string $valueProperty The property of the result object to be used as the value (or null).
305
	 * @param null|callable $delegate row delegate, a callback function
306
	 * @return array An array of object containing the rows keyed by keyProperty.
307
	 * @see executeQueryForMap()
308
	 */
309 4
	public function runQueryForMap($connection, $parameter, $command, $keyProperty, $valueProperty = null, $delegate = null)
310
	{
311 4
		$map = [];
312
		//$recordSet = $this->executeSQLQuery($connection, $sql);
313 4
		$connection->setActive(true);
314 4
		$reader = $command->query();
315 4
		if ($delegate !== null) {
316
			//while($row = $recordSet->fetchRow())
317 1
			foreach ($reader as $row) {
318 1
				$obj = $this->applyResultMap($row);
319 1
				$key = TPropertyAccess::get($obj, $keyProperty);
320 1
				$value = ($valueProperty === null) ? $obj :
321 1
							TPropertyAccess::get($obj, $valueProperty);
322 1
				$param = new TResultSetMapItemParameter($key, $value, $parameter, $map);
323 1
				$this->raiseRowDelegate($delegate, $param);
324
			}
325
		} else {
326
			//while($row = $recordSet->fetchRow())
327 3
			foreach ($reader as $row) {
328 3
				$obj = $this->applyResultMap($row);
329 3
				$key = TPropertyAccess::get($obj, $keyProperty);
330 3
				$map[$key] = ($valueProperty === null) ? $obj :
331 3
								TPropertyAccess::get($obj, $valueProperty);
332
			}
333
		}
334 4
		$this->onExecuteQuery($command);
335 4
		return $map;
336
	}
337
338
	/**
339
	 * Raises delegate handler.
340
	 * This method is invoked for each new list item. It is the responsibility
341
	 * of the handler to add the item to the list.
342
	 * @param callable $handler to be executed
343
	 * @param mixed $param event parameter
344
	 */
345 2
	protected function raiseRowDelegate($handler, $param)
346
	{
347 2
		if (is_string($handler)) {
348
			call_user_func($handler, $this, $param);
349 2
		} elseif (is_callable($handler, true)) {
350
			// an array: 0 - object, 1 - method name/path
351 2
			[$object, $method] = $handler;
352 2
			if (is_string($object)) {	// static method call
353
				call_user_func($handler, $this, $param);
354
			} else {
355 2
				if (($pos = strrpos($method, '.')) !== false) {
356
					$object = $this->getSubProperty(substr($method, 0, $pos));
357
					$method = substr($method, $pos + 1);
358
				}
359 2
				$object->$method($this, $param);
360
			}
361
		} else {
362
			throw new TInvalidDataValueException('sqlmap_invalid_delegate', $this->getID(), $handler);
363
		}
364 2
	}
365
366
	/**
367
	 * Executes an SQL statement that returns a single row as an object of the
368
	 * type of the <tt>$result</tt> passed in as a parameter.
369
	 * @param IDbConnection $connection database connection
370
	 * @param mixed $parameter The parameter data (object, arrary, primitive) used to set the parameters in the SQL
371
	 * @param null|mixed $result The result object.
372
	 * @return object the object.
373
	 */
374 86
	public function executeQueryForObject($connection, $parameter, $result = null)
375
	{
376 86
		$sql = $this->_command->create($this->_manager, $connection, $this->_statement, $parameter);
377 86
		return $this->runQueryForObject($connection, $sql, $result);
378
	}
379
380
	/**
381
	 * Executes an SQL statement that returns a single row as an object of the
382
	 * type of the <tt>$result</tt> passed in as a parameter.
383
	 *
384
	 * This method should only be called by internal developers, consider using
385
	 * <tt>executeQueryForObject()</tt> first.
386
	 *
387
	 * @param IDbConnection $connection database connection
388
	 * @param $command
389
	 * @param object &$result The result object.
390
	 * @return object the object.
391
	 * @see executeQueryForObject()
392
	 */
393 86
	public function runQueryForObject($connection, $command, &$result)
394
	{
395 86
		$object = null;
396 86
		$connection->setActive(true);
397 86
		foreach ($command->query() as $row) {
398 86
			$object = $this->applyResultMap($row, $result);
399
		}
400
401 86
		if (!$this->_groupBy->isEmpty()) {
402
			$list = $this->_groupBy->collect();
403
			$this->initialGroupByResults();
404
			$object = $list[0];
405
		}
406
407 86
		$this->executePostSelect($connection);
408 86
		$this->onExecuteQuery($command);
409
410 86
		return $object;
411
	}
412
413
	/**
414
	 * Execute an insert statement. Fill the parameter object with the ouput
415
	 * parameters if any, also could return the insert generated key.
416
	 * @param IDbConnection $connection database connection
417
	 * @param mixed $parameter The parameter object used to fill the statement.
418
	 * @return string the insert generated key.
419
	 */
420 20
	public function executeInsert($connection, $parameter)
421
	{
422 20
		$generatedKey = $this->getPreGeneratedSelectKey($connection, $parameter);
423
424 20
		$command = $this->_command->create($this->_manager, $connection, $this->_statement, $parameter);
425
//		var_dump($command,$parameter);
426 20
		$result = $command->execute();
0 ignored issues
show
Unused Code introduced by
The assignment to $result is dead and can be removed.
Loading history...
427
428 20
		if ($generatedKey === null) {
0 ignored issues
show
introduced by
The condition $generatedKey === null is always false.
Loading history...
429 19
			$generatedKey = $this->getPostGeneratedSelectKey($connection, $parameter);
430
		}
431
432 20
		$this->executePostSelect($connection);
433 20
		$this->onExecuteQuery($command);
434 20
		return $generatedKey;
435
	}
436
437
	/**
438
	 * Gets the insert generated ID before executing an insert statement.
439
	 * @param IDbConnection $connection database connection
440
	 * @param mixed $parameter insert statement parameter.
441
	 * @return string new insert ID if pre-select key statement was executed, null otherwise.
442
	 */
443 20
	protected function getPreGeneratedSelectKey($connection, $parameter)
444
	{
445 20
		if ($this->_statement instanceof TSqlMapInsert) {
446 16
			$selectKey = $this->_statement->getSelectKey();
447 16
			if (($selectKey !== null) && !$selectKey->getIsAfter()) {
448 1
				return $this->executeSelectKey($connection, $parameter, $selectKey);
449
			}
450
		}
451 19
	}
452
453
	/**
454
	 * Gets the inserted row ID after executing an insert statement.
455
	 * @param IDbConnection $connection database connection
456
	 * @param mixed $parameter insert statement parameter.
457
	 * @return string last insert ID, null otherwise.
458
	 */
459 19
	protected function getPostGeneratedSelectKey($connection, $parameter)
460
	{
461 19
		if ($this->_statement instanceof TSqlMapInsert) {
462 15
			$selectKey = $this->_statement->getSelectKey();
463 15
			if (($selectKey !== null) && $selectKey->getIsAfter()) {
464 1
				return $this->executeSelectKey($connection, $parameter, $selectKey);
465
			}
466
		}
467 18
	}
468
469
	/**
470
	 * Execute the select key statement, used to obtain last insert ID.
471
	 * @param IDbConnection $connection database connection
472
	 * @param mixed $parameter insert statement parameter
473
	 * @param TSqlMapSelectKey $selectKey select key statement
0 ignored issues
show
Bug introduced by
The type Prado\Data\SqlMap\Statements\TSqlMapSelectKey was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
474
	 * @return string last insert ID.
475
	 */
476 2
	protected function executeSelectKey($connection, $parameter, $selectKey)
477
	{
478 2
		$mappedStatement = $this->getManager()->getMappedStatement($selectKey->getID());
479 2
		$generatedKey = $mappedStatement->executeQueryForObject(
480 2
			$connection,
481 2
			$parameter,
482 2
			null
483
		);
484 2
		if (strlen($prop = $selectKey->getProperty()) > 0) {
485 2
			TPropertyAccess::set($parameter, $prop, $generatedKey);
486
		}
487 2
		return $generatedKey;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $generatedKey returns the type object which is incompatible with the documented return type string.
Loading history...
488
	}
489
490
	/**
491
	 * Execute an update statement. Also used for delete statement.
492
	 * Return the number of rows effected.
493
	 * @param IDbConnection $connection database connection
494
	 * @param mixed $parameter The object used to set the parameters in the SQL.
495
	 * @return int The number of rows effected.
496
	 */
497 8
	public function executeUpdate($connection, $parameter)
498
	{
499 8
		$sql = $this->_command->create($this->getManager(), $connection, $this->_statement, $parameter);
500 8
		$affectedRows = $sql->execute();
501
		//$this->executeSQLQuery($connection, $sql);
502 8
		$this->executePostSelect($connection);
503 8
		$this->onExecuteQuery($sql);
504 8
		return $affectedRows;
505
	}
506
507
	/**
508
	 * Process 'select' result properties
509
	 * @param IDbConnection $connection database connection
510
	 */
511 117
	protected function executePostSelect($connection)
512
	{
513 117
		while (count($this->_selectQueue)) {
514 6
			$postSelect = array_shift($this->_selectQueue);
515 6
			$method = $postSelect->getMethod();
516 6
			$statement = $postSelect->getStatement();
517 6
			$property = $postSelect->getResultProperty()->getProperty();
518 6
			$keys = $postSelect->getKeys();
519 6
			$resultObject = $postSelect->getResultObject();
520
521 6
			if ($method == self::QUERY_FOR_LIST || $method == self::QUERY_FOR_ARRAY) {
522 4
				$values = $statement->executeQueryForList($connection, $keys, null);
523
524 4
				if ($method == self::QUERY_FOR_ARRAY) {
525 1
					$values = $values->toArray();
526
				}
527 4
				TPropertyAccess::set($resultObject, $property, $values);
528 2
			} elseif ($method == self::QUERY_FOR_OBJECT) {
529 2
				$value = $statement->executeQueryForObject($connection, $keys, null);
530 2
				TPropertyAccess::set($resultObject, $property, $value);
531
			}
532
		}
533 117
	}
534
535
	/**
536
	 * Raise the execute query event.
537
	 * @param array $sql prepared SQL statement and subsititution parameters
538
	 */
539 120
	public function onExecuteQuery($sql)
540
	{
541 120
		$this->raiseEvent('OnExecuteQuery', $this, $sql);
0 ignored issues
show
Bug introduced by
$sql of type array is incompatible with the type Prado\TEventParameter expected by parameter $param of Prado\TComponent::raiseEvent(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

541
		$this->raiseEvent('OnExecuteQuery', $this, /** @scrutinizer ignore-type */ $sql);
Loading history...
542 120
	}
543
544
	/**
545
	 * Apply result mapping.
546
	 * @param array $row a result set row retrieved from the database
547
	 * @param null|&object $resultObject the result object, will create if necessary.
0 ignored issues
show
Documentation Bug introduced by
The doc comment null|&object at position 2 could not be parsed: Unknown type name '&' at position 2 in null|&object.
Loading history...
548
	 * @return object the result filled with data, null if not filled.
549
	 */
550 115
	protected function applyResultMap($row, &$resultObject = null)
551
	{
552 115
		if ($row === false) {
553
			return null;
554
		}
555
556 115
		$resultMapName = $this->_statement->getResultMap();
557 115
		$resultClass = $this->_statement->getResultClass();
558
559 115
		$obj = null;
560 115
		if ($this->getManager()->getResultMaps()->contains($resultMapName)) {
561 69
			$obj = $this->fillResultMap($resultMapName, $row, null, $resultObject);
562 55
		} elseif (strlen($resultClass) > 0) {
563 44
			$obj = $this->fillResultClass($resultClass, $row, $resultObject);
564
		} else {
565 11
			$obj = $this->fillDefaultResultMap(null, $row, $resultObject);
566
		}
567 115
		if (class_exists('TActiveRecord', false) && $obj instanceof TActiveRecord) {
568
			//Create a new clean active record.
569
			$obj = TActiveRecord::createRecord(get_class($obj), $obj);
0 ignored issues
show
Bug introduced by
$obj of type Prado\Data\ActiveRecord\TActiveRecord is incompatible with the type array expected by parameter $data of Prado\Data\ActiveRecord\...eRecord::createRecord(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

569
			$obj = TActiveRecord::createRecord(get_class($obj), /** @scrutinizer ignore-type */ $obj);
Loading history...
570
		}
571 115
		return $obj;
572
	}
573
574
	/**
575
	 * Fill the result using ResultClass, will creates new result object if required.
576
	 * @param string $resultClass result object class name
577
	 * @param array $row a result set row retrieved from the database
578
	 * @param object $resultObject the result object, will create if necessary.
579
	 * @return object result object filled with data
580
	 */
581 44
	protected function fillResultClass($resultClass, $row, $resultObject)
582
	{
583 44
		if ($resultObject === null) {
584 44
			$registry = $this->getManager()->getTypeHandlers();
585 44
			$resultObject = $this->_statement->createInstanceOfResultClass($registry, $row);
586
		}
587
588 44
		if ($resultObject instanceof \ArrayAccess) {
589 2
			return $this->fillResultArrayList($row, $resultObject);
590 42
		} elseif (is_object($resultObject)) {
591 23
			return $this->fillResultObjectProperty($row, $resultObject);
592
		} else {
593 21
			return $this->fillDefaultResultMap(null, $row, $resultObject);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->fillDefaul...l, $row, $resultObject) returns the type array which is incompatible with the documented return type object.
Loading history...
594
		}
595
	}
596
597
	/**
598
	 * Apply the result to a TList or an array.
599
	 * @param array $row a result set row retrieved from the database
600
	 * @param object $resultObject result object, array or list
601
	 * @return object result filled with data.
602
	 */
603 2
	protected function fillResultArrayList($row, $resultObject)
604
	{
605 2
		if ($resultObject instanceof TList) {
606 2
			foreach ($row as $v) {
607 2
				$resultObject[] = $v;
608
			}
609
		} else {
610
			foreach ($row as $k => $v) {
611
				$resultObject[$k] = $v;
612
			}
613
		}
614 2
		return $resultObject;
615
	}
616
617
	/**
618
	 * Apply the result to an object.
619
	 * @param array $row a result set row retrieved from the database
620
	 * @param object $resultObject result object, array or list
621
	 * @return object result filled with data.
622
	 */
623 23
	protected function fillResultObjectProperty($row, $resultObject)
624
	{
625 23
		$index = 0;
626 23
		$registry = $this->getManager()->getTypeHandlers();
627 23
		foreach ($row as $k => $v) {
628 23
			$property = new TResultProperty;
629 23
			if (is_string($k) && strlen($k) > 0) {
630 23
				$property->setColumn($k);
631
			}
632 23
			$property->setColumnIndex(++$index);
633 23
			$type = gettype(TPropertyAccess::get($resultObject, $k));
634 23
			$property->setType($type);
635 23
			$value = $property->getPropertyValue($registry, $row);
636 23
			TPropertyAccess::set($resultObject, $k, $value);
637
		}
638 23
		return $resultObject;
639
	}
640
641
	/**
642
	 * Fills the result object according to result mappings.
643
	 * @param string $resultMapName result map name.
644
	 * @param array $row a result set row retrieved from the database
645
	 * @param null|mixed $parentGroup
646
	 * @param null|&object $resultObject result object to fill, will create new instances if required.
0 ignored issues
show
Documentation Bug introduced by
The doc comment null|&object at position 2 could not be parsed: Unknown type name '&' at position 2 in null|&object.
Loading history...
647
	 * @return object result object filled with data.
648
	 */
649 69
	protected function fillResultMap($resultMapName, $row, $parentGroup = null, &$resultObject = null)
650
	{
651 69
		$resultMap = $this->getManager()->getResultMap($resultMapName);
652 69
		$registry = $this->getManager()->getTypeHandlers();
653 69
		$resultMap = $resultMap->resolveSubMap($registry, $row);
654
655 69
		if ($resultObject === null) {
656 68
			$resultObject = $resultMap->createInstanceOfResult($registry);
657
		}
658
659 69
		if (is_object($resultObject)) {
660 61
			if (strlen($resultMap->getGroupBy()) > 0) {
661 1
				return $this->addResultMapGroupBy($resultMap, $row, $parentGroup, $resultObject);
662
			} else {
663 61
				foreach ($resultMap->getColumns() as $property) {
664 61
					$this->setObjectProperty($resultMap, $property, $row, $resultObject);
665
				}
666
			}
667
		} else {
668 8
			$resultObject = $this->fillDefaultResultMap($resultMap, $row, $resultObject);
669
		}
670 69
		return $resultObject;
671
	}
672
673
	/**
674
	 * ResultMap with GroupBy property. Save object collection graph in a tree
675
	 * and collect the result later.
676
	 * @param TResultMap $resultMap result mapping details.
0 ignored issues
show
Bug introduced by
The type Prado\Data\SqlMap\Statements\TResultMap was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
677
	 * @param array $row a result set row retrieved from the database
678
	 * @param mixed $parent
679
	 * @param object &$resultObject the result object
680
	 * @return object result object.
681
	 */
682 1
	protected function addResultMapGroupBy($resultMap, $row, $parent, &$resultObject)
683
	{
684 1
		$group = $this->getResultMapGroupKey($resultMap, $row);
685
686 1
		if (empty($parent)) {
687 1
			$rootObject = ['object' => $resultObject, 'property' => null];
688 1
			$this->_groupBy->add(null, $group, $rootObject);
689
		}
690
691 1
		foreach ($resultMap->getColumns() as $property) {
692
			//set properties.
693 1
			$this->setObjectProperty($resultMap, $property, $row, $resultObject);
694 1
			$nested = $property->getResultMapping();
695
696
			//nested property
697 1
			if ($this->getManager()->getResultMaps()->contains($nested)) {
698 1
				$nestedMap = $this->getManager()->getResultMap($nested);
699 1
				$groupKey = $this->getResultMapGroupKey($nestedMap, $row);
700
701
				//add the node reference first
702 1
				if (empty($parent)) {
703 1
					$this->_groupBy->add($group, $groupKey, '');
704
				}
705
706
				//get the nested result mapping value
707 1
				$value = $this->fillResultMap($nested, $row, $groupKey);
708
709
				//add it to the object tree graph
710 1
				$groupObject = ['object' => $value, 'property' => $property->getProperty()];
711 1
				if (empty($parent)) {
712 1
					$this->_groupBy->add($group, $groupKey, $groupObject);
713
				} else {
714 1
					$this->_groupBy->add($parent, $groupKey, $groupObject);
715
				}
716
			}
717
		}
718 1
		return $resultObject;
719
	}
720
721
	/**
722
	 * Gets the result 'group by' groupping key for each row.
723
	 * @param TResultMap $resultMap result mapping details.
724
	 * @param array $row a result set row retrieved from the database
725
	 * @return string groupping key.
726
	 */
727 1
	protected function getResultMapGroupKey($resultMap, $row)
728
	{
729 1
		$groupBy = $resultMap->getGroupBy();
730 1
		if (isset($row[$groupBy])) {
731 1
			return $resultMap->getID() . $row[$groupBy];
732
		} else {
733 1
			return $resultMap->getID() . crc32(serialize($row));
734
		}
735
	}
736
737
	/**
738
	 * Fill the result map using default settings. If <tt>$resultMap</tt> is null
739
	 * the result object returned will be guessed from <tt>$resultObject</tt>.
740
	 * @param TResultMap $resultMap result mapping details.
741
	 * @param array $row a result set row retrieved from the database
742
	 * @param object $resultObject the result object
743
	 * @return mixed the result object filled with data.
744
	 */
745 40
	protected function fillDefaultResultMap($resultMap, $row, $resultObject)
746
	{
747 40
		if ($resultObject === null) {
748 11
			$resultObject = '';
749
		}
750
751 40
		if ($resultMap !== null) {
752 8
			$result = $this->fillArrayResultMap($resultMap, $row, $resultObject);
0 ignored issues
show
Bug introduced by
It seems like $resultObject can also be of type string; however, parameter $resultObject of Prado\Data\SqlMap\Statem...t::fillArrayResultMap() does only seem to accept object, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

752
			$result = $this->fillArrayResultMap($resultMap, $row, /** @scrutinizer ignore-type */ $resultObject);
Loading history...
753
		} else {
754 32
			$result = $row;
755
		}
756
757
		//if scalar result types
758 40
		if (count($result) == 1 && ($type = gettype($resultObject)) != 'array') {
759 34
			return $this->getScalarResult($result, $type);
760
		} else {
761 6
			return $result;
762
		}
763
	}
764
765
	/**
766
	 * Retrieve the result map as an array.
767
	 * @param TResultMap $resultMap result mapping details.
768
	 * @param array $row a result set row retrieved from the database
769
	 * @param object $resultObject the result object
770
	 * @return array array list of result objects.
771
	 */
772 8
	protected function fillArrayResultMap($resultMap, $row, $resultObject)
773
	{
774 8
		$result = [];
775 8
		$registry = $this->getManager()->getTypeHandlers();
776 8
		foreach ($resultMap->getColumns() as $column) {
777 8
			if (($column->getType() === null)
778 8
				&& ($resultObject !== null) && !is_object($resultObject)) {
779 7
				$column->setType(gettype($resultObject));
780
			}
781 8
			$result[$column->getProperty()] = $column->getPropertyValue($registry, $row);
782
		}
783 8
		return $result;
784
	}
785
786
	/**
787
	 * Converts the first array value to scalar value of given type.
788
	 * @param array $result list of results
789
	 * @param string $type scalar type.
790
	 * @return mixed scalar value.
791
	 */
792 34
	protected function getScalarResult($result, $type)
793
	{
794 34
		$scalar = array_shift($result);
795 34
		settype($scalar, $type);
796 34
		return $scalar;
797
	}
798
799
	/**
800
	 * Set a property of the result object with appropriate value.
801
	 * @param TResultMap $resultMap result mapping details.
802
	 * @param TResultProperty $property the result property to fill.
803
	 * @param array $row a result set row retrieved from the database
804
	 * @param object &$resultObject the result object
805
	 */
806 61
	protected function setObjectProperty($resultMap, $property, $row, &$resultObject)
807
	{
808 61
		$select = $property->getSelect();
809 61
		$key = $property->getProperty();
810 61
		$nested = $property->getNestedResultMap();
811 61
		$registry = $this->getManager()->getTypeHandlers();
812 61
		if ($key === '') {
813
			$resultObject = $property->getPropertyValue($registry, $row);
814 61
		} elseif (strlen($select) == 0 && ($nested === null)) {
815 61
			$value = $property->getPropertyValue($registry, $row);
816
817 61
			$this->_IsRowDataFound = $this->_IsRowDataFound || ($value != null);
818 61
			if (is_array($resultObject) || is_object($resultObject)) {
819 61
				TPropertyAccess::set($resultObject, $key, $value);
820
			} else {
821 61
				$resultObject = $value;
822
			}
823 14
		} elseif ($nested !== null) {
824 4
			if ($property->instanceOfListType($resultObject) || $property->instanceOfArrayType($resultObject)) {
825 1
				if (strlen($resultMap->getGroupBy()) <= 0) {
826
					throw new TSqlMapExecutionException(
827
						'sqlmap_non_groupby_array_list_type',
828
						$resultMap->getID(),
829
						get_class($resultObject),
830 1
						$key
831
					);
832
				}
833
			} else {
834 3
				$obj = $nested->createInstanceOfResult($this->getManager()->getTypeHandlers());
835 3
				if ($this->fillPropertyWithResultMap($nested, $row, $obj) == false) {
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
836 2
					$obj = null;
837
				}
838 4
				TPropertyAccess::set($resultObject, $key, $obj);
839
			}
840
		} else { //'select' ResultProperty
841 10
			$this->enquequePostSelect($select, $resultMap, $property, $row, $resultObject);
842
		}
843 61
	}
844
845
	/**
846
	 * Add nested result property to post select queue.
847
	 * @param string $select post select statement ID
848
	 * @param TResultMap $resultMap current result mapping details.
849
	 * @param TResultProperty $property current result property.
850
	 * @param array $row a result set row retrieved from the database
851
	 * @param object $resultObject the result object
852
	 */
853 10
	protected function enquequePostSelect($select, $resultMap, $property, $row, $resultObject)
854
	{
855 10
		$statement = $this->getManager()->getMappedStatement($select);
856 10
		$key = $this->getPostSelectKeys($resultMap, $property, $row);
857 10
		$postSelect = new TPostSelectBinding;
858 10
		$postSelect->setStatement($statement);
859 10
		$postSelect->setResultObject($resultObject);
860 10
		$postSelect->setResultProperty($property);
861 10
		$postSelect->setKeys($key);
862
863 10
		if ($property->instanceOfListType($resultObject)) {
864 7
			$values = null;
0 ignored issues
show
Unused Code introduced by
The assignment to $values is dead and can be removed.
Loading history...
865 7
			if ($property->getLazyLoad()) {
866 4
				$values = TLazyLoadList::newInstance(
867 4
					$statement,
868 4
					$key,
869 4
					$resultObject,
870 4
					$property->getProperty()
871
				);
872 4
				TPropertyAccess::set($resultObject, $property->getProperty(), $values);
873
			} else {
874 7
				$postSelect->setMethod(self::QUERY_FOR_LIST);
875
			}
876 3
		} elseif ($property->instanceOfArrayType($resultObject)) {
877 1
			$postSelect->setMethod(self::QUERY_FOR_ARRAY);
878
		} else {
879 2
			$postSelect->setMethod(self::QUERY_FOR_OBJECT);
880
		}
881
882 10
		if (!$property->getLazyLoad()) {
883 6
			$this->_selectQueue[] = $postSelect;
884
		}
885 10
	}
886
887
	/**
888
	 * Finds in the post select property the SQL statement primary selection keys.
889
	 * @param TResultMap $resultMap result mapping details
890
	 * @param TResultProperty $property result property
891
	 * @param array $row current row data.
892
	 * @return array list of primary key values.
893
	 */
894 10
	protected function getPostSelectKeys($resultMap, $property, $row)
895
	{
896 10
		$value = $property->getColumn();
897 10
		if (is_int(strpos($value, ',', 0)) || is_int(strpos($value, '=', 0))) {
0 ignored issues
show
introduced by
The condition is_int(strpos($value, ',', 0)) is always true.
Loading history...
898 1
			$keys = [];
899 1
			foreach (explode(',', $value) as $entry) {
900 1
				$pair = explode('=', $entry);
901 1
				$keys[trim($pair[0])] = $row[trim($pair[1])];
902
			}
903 1
			return $keys;
904
		} else {
905 9
			$registry = $this->getManager()->getTypeHandlers();
906 9
			return $property->getPropertyValue($registry, $row);
907
		}
908
	}
909
910
	/**
911
	 * Fills the property with result mapping results.
912
	 * @param TResultMap $resultMap nested result mapping details.
913
	 * @param array $row a result set row retrieved from the database
914
	 * @param object &$resultObject the result object
915
	 * @return bool true if the data was found, false otherwise.
916
	 */
917 3
	protected function fillPropertyWithResultMap($resultMap, $row, &$resultObject)
918
	{
919 3
		$dataFound = false;
920 3
		foreach ($resultMap->getColumns() as $property) {
921 3
			$this->_IsRowDataFound = false;
922 3
			$this->setObjectProperty($resultMap, $property, $row, $resultObject);
923 3
			$dataFound = $dataFound || $this->_IsRowDataFound;
924
		}
925 3
		$this->_IsRowDataFound = $dataFound;
926 3
		return $dataFound;
927
	}
928
929
	public function __wakeup()
930
	{
931
		if (null === $this->_selectQueue) {
932
			$this->_selectQueue = [];
933
		}
934
	}
935
936
	public function __sleep()
937
	{
938
		$exprops = [];
939
		$cn = __CLASS__;
940
		if (!count($this->_selectQueue)) {
941
			$exprops[] = "\0$cn\0_selectQueue";
942
		}
943
		if (null === $this->_groupBy) {
944
			$exprops[] = "\0$cn\0_groupBy";
945
		}
946
		if (!$this->_IsRowDataFound) {
947
			$exprops[] = "\0$cn\0_IsRowDataFound";
948
		}
949
		return array_diff(parent::__sleep(), $exprops);
950
	}
951
}
952