Completed
Push — master ( 0d2afc...b4e9b7 )
by Peter
64:04 queued 61:12
created

EntityManager::updateResult()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 2.032

Importance

Changes 0
Metric Value
dl 0
loc 9
ccs 4
cts 5
cp 0.8
rs 9.9666
c 0
b 0
f 0
cc 2
nc 2
nop 1
crap 2.032
1
<?php
2
3
/**
4
 * This software package is licensed under AGPL or Commercial license.
5
 *
6
 * @package   maslosoft/mangan
7
 * @licence   AGPL or Commercial
8
 * @copyright Copyright (c) Piotr Masełkowski <[email protected]>
9
 * @copyright Copyright (c) Maslosoft
10
 * @copyright Copyright (c) Others as mentioned in code
11
 * @link      https://maslosoft.com/mangan/
12
 */
13
14
namespace Maslosoft\Mangan;
15
16
use Maslosoft\Addendum\Interfaces\AnnotatedInterface;
17
use Maslosoft\Mangan\Events\Event;
18
use Maslosoft\Mangan\Events\ModelEvent;
19
use Maslosoft\Mangan\Exceptions\BadAttributeException;
20
use Maslosoft\Mangan\Exceptions\ManganException;
21
use Maslosoft\Mangan\Helpers\CollectionNamer;
22
use Maslosoft\Mangan\Helpers\PkManager;
23
use Maslosoft\Mangan\Interfaces\CriteriaInterface;
24
use Maslosoft\Mangan\Interfaces\EntityManagerInterface;
25
use Maslosoft\Mangan\Interfaces\ScenariosInterface;
26
use Maslosoft\Mangan\Meta\ManganMeta;
27
use Maslosoft\Mangan\Options\EntityOptions;
28
use Maslosoft\Mangan\Signals\AfterDelete;
29
use Maslosoft\Mangan\Signals\AfterSave;
30
use Maslosoft\Mangan\Signals\BeforeDelete;
31
use Maslosoft\Mangan\Signals\BeforeSave;
32
use Maslosoft\Mangan\Transformers\RawArray;
33
use Maslosoft\Mangan\Transformers\SafeArray;
34
use Maslosoft\Signals\Signal;
35
use MongoCollection;
36
37
/**
38
 * EntityManager
39
 *
40
 * @author Piotr Maselkowski <pmaselkowski at gmail.com>
41
 */
42
class EntityManager implements EntityManagerInterface
43
{
44
45
	/**
46
	 * Model
47
	 * @var AnnotatedInterface
48
	 */
49
	public $model = null;
50
51
	/**
52
	 *
53
	 * @var ScopeManager
54
	 */
55
	private $sm = null;
56
57
	/**
58
	 *
59
	 * @var
60
	 */
61
	public $meta = null;
62
63
	/**
64
	 * Options
65
	 * @var EntityOptions
66
	 */
67
	public $options = null;
68
69
	/**
70
	 * Current collection name
71
	 * @var string
72
	 */
73
	public $collectionName = '';
74
75
	/**
76
	 * Validator instance
77
	 * @var Validator
78
	 */
79
	private $validator = null;
80
81
	/**
82
	 * Current collection
83
	 * @var MongoCollection
84
	 */
85
	private $_collection = null;
86
87
	/**
88
	 * Result of last operation
89
	 * @var array
90
	 */
91
	private $lastResult = [];
92
93
	/**
94
	 * Create entity manager
95
	 * @param AnnotatedInterface $model
96
	 * @param Mangan             $mangan
97
	 * @throws ManganException
98
	 */
99 122
	public function __construct(AnnotatedInterface $model, Mangan $mangan = null)
100
	{
101 122
		$this->model = $model;
102 122
		$this->sm = new ScopeManager($model);
103 122
		$this->options = new EntityOptions($model);
104 122
		$this->collectionName = CollectionNamer::nameCollection($model);
105 122
		$this->meta = ManganMeta::create($model);
106 122
		$this->validator = new Validator($model);
107 122
		if (null === $mangan)
108
		{
109 119
			$mangan = Mangan::fromModel($model);
110
		}
111 122
		if (!$this->collectionName)
112
		{
113
			throw new ManganException(sprintf('Invalid collection name for model: `%s`', $this->meta->type()->name));
114
		}
115 122
		$this->_collection = new MongoCollection($mangan->getDbInstance(), $this->collectionName);
116 122
	}
117
118
	/**
119
	 * Create model related entity manager.
120
	 * This will create customized entity manger if defined in model with EntityManager annotation.
121
	 * If no custom entity manager is defined this will return default EntityManager.
122
	 * @param AnnotatedInterface $model
123
	 * @param Mangan             $mangan
124
	 * @return EntityManagerInterface
125
	 */
126 113
	public static function create($model, Mangan $mangan = null)
127
	{
128 113
		$emClass = ManganMeta::create($model)->type()->entityManager ?: static::class;
129 113
		return new $emClass($model, $mangan);
130
	}
131
132
	/**
133
	 * Set attributes en masse.
134
	 * Attributes will be filtered according to SafeAnnotation.
135
	 * Only attributes marked as safe will be set, other will be ignored.
136
	 *
137
	 * @param mixed[] $attributes
138
	 */
139
	public function setAttributes($attributes)
140
	{
141
		SafeArray::toModel($attributes, $this->model, $this->model);
142
	}
143
144
	/**
145
	 * Inserts a row into the table based on this active record attributes.
146
	 * If the table's primary key is auto-incremental and is null before insertion,
147
	 * it will be populated with the actual value after insertion.
148
	 *
149
	 * Note, validation is not performed in this method. You may call {@link validate} to perform the validation.
150
	 * After the record is inserted to DB successfully, its {@link isNewRecord} property will be set false,
151
	 * and its {@link scenario} property will be set to be 'update'.
152
	 *
153
	 * @param AnnotatedInterface $model if want to insert different model than set in constructor
154
	 *
155
	 * @return boolean whether the attributes are valid and the record is inserted successfully.
156
	 * @throws ManganException if the record is not new
157
	 * @throws ManganException on fail of insert or insert of empty document
158
	 * @throws ManganException on fail of insert, when safe flag is set to true
159
	 * @throws ManganException on timeout of db operation , when safe flag is set to true
160
	 */
161 36
	public function insert(AnnotatedInterface $model = null)
162
	{
163 36
		$model = $model ?: $this->model;
164 36
		if ($this->beforeSave($model, EntityManagerInterface::EventBeforeInsert))
165
		{
166 36
			$rawData = RawArray::fromModel($model);
167
168 36
			$opts = $this->options->getSaveOptions();
169 36
			$rawResult = $this->_collection->insert($rawData, $opts);
170 36
			$result = $this->insertResult($rawResult);
171
172 36
			if ($result)
173
			{
174 36
				$this->afterSave($model, EntityManagerInterface::EventAfterInsert);
175 36
				return true;
176
			}
177
			throw new ManganException('Can\t save the document to disk, or attempting to save an empty document. ' . ucfirst($rawResult['errmsg']));
178
		}
179
		AspectManager::removeAspect($model, self::AspectSaving);
180
		return false;
181
	}
182
183
	/**
184
	 * Updates the row represented by this active document.
185
	 * All loaded attributes will be saved to the database.
186
	 * Note, validation is not performed in this method. You may call {@link validate} to perform the validation.
187
	 *
188
	 * @param array $attributes list of attributes that need to be saved. Defaults to null,
189
	 *                          meaning all attributes that are loaded from DB will be saved.
190
	 * @return boolean whether the update is successful
191
	 * @throws ManganException if the record is new
192
	 * @throws ManganException on fail of update
193
	 * @throws ManganException on timeout of db operation , when safe flag is set to true
194
	 */
195 6
	public function update(array $attributes = null)
196
	{
197 6
		if ($this->beforeSave($this->model, EntityManagerInterface::EventBeforeUpdate))
198
		{
199 6
			$criteria = PkManager::prepareFromModel($this->model);
200 6
			$result = $this->updateOne($criteria, $attributes);
201 5
			if ($result)
202
			{
203 5
				$this->afterSave($this->model, EntityManagerInterface::EventAfterUpdate);
204 5
				return true;
205
			}
206
			throw new ManganException('Can\t save the document to disk, or attempting to save an empty document.');
207
		}
208
		AspectManager::removeAspect($this->model, self::AspectSaving);
209
		return false;
210
	}
211
212
	/**
213
	 * Updates one document with the specified criteria and attributes
214
	 *
215
	 * This is more *raw* update:
216
	 *
217
	 * * Does not raise any events or signals
218
	 * * Does not perform any validation
219
	 *
220
	 * @param array|CriteriaInterface $criteria   query criteria.
221
	 * @param array                   $attributes list of attributes that need to be saved. Defaults to null,
222
	 * @param bool Whether tu force update/upsert document
223
	 *                                            meaning all attributes that are loaded from DB will be saved.
224
	 * @return bool
225
	 */
226 85
	public function updateOne($criteria = null, array $attributes = null, $modify = false)
227
	{
228 85
		$criteria = $this->sm->apply($criteria);
229 85
		$rawData = RawArray::fromModel($this->model, $attributes);
0 ignored issues
show
Documentation introduced by
$attributes is of type null|array, but the function expects a array<integer,string>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
230
231
		// filter attributes if set in param
232 85
		if ($attributes !== null)
233
		{
234 3
			$meta = ManganMeta::create($this->model);
235 3
			foreach ($attributes as $key)
236
			{
237 3
				if ($meta->$key === false)
238
				{
239 3
					throw new BadAttributeException(sprintf("Unknown attribute `%s` on model `%s`", $key, get_class($this->model)));
240
				}
241
			}
242 2
			$modify = true;
243 2
			foreach ($rawData as $key => $value)
244
			{
245 2
				if (!in_array($key, $attributes))
246
				{
247 2
					unset($rawData[$key]);
248
				}
249
			}
250
		}
251
		else
252
		{
253 85
			$fields = array_keys(ManganMeta::create($this->model)->fields());
254 85
			$setFields = array_keys($rawData);
255 85
			$diff = array_diff($fields, $setFields);
256
257 85
			if (!empty($diff))
258
			{
259 6
				$modify = true;
260
			}
261
		}
262 85
		if ($modify)
263
		{
264
			// Id could be altered, so skip it as it cannot be changed
265 8
			unset($rawData['_id']);
266 8
			$data = ['$set' => $rawData];
267
		}
268
		else
269
		{
270 82
			$data = $rawData;
271
		}
272 85
		$conditions = $criteria->getConditions();
273 85
		$opts = $this->options->getSaveOptions(['multiple' => false, 'upsert' => true]);
274 85
		$collection = $this->getCollection();
275
276 85
		$result = $collection->update($conditions, $data, $opts);
277 85
		return $this->updateResult($result);
278
	}
279
280
	/**
281
	 * Atomic, in-place update method.
282
	 *
283
	 * @param Modifier          $modifier updating rules to apply
284
	 * @param CriteriaInterface $criteria condition to limit updating rules
285
	 * @return boolean
286
	 */
287 1
	public function updateAll(Modifier $modifier, CriteriaInterface $criteria = null)
288
	{
289 1
		if ($modifier->canApply())
290
		{
291 1
			$criteria = $this->sm->apply($criteria);
292 1
			$conditions = $criteria->getConditions();
293 1
			$mods = $modifier->getModifiers();
294 1
			$opts = $this->options->getSaveOptions([
295 1
				'upsert' => false,
296
				'multiple' => true
297
			]);
298 1
			$result = $this->getCollection()->update($conditions, $mods, $opts);
299 1
			return $this->updateResult($result);
300
		}
301
		else
302
		{
303
			return false;
304
		}
305
	}
306
307
	/**
308
	 * Replaces the current document.
309
	 *
310
	 * **NOTE: This will overwrite entire document.**
311
	 * Any filtered out properties will be removed as well.
312
	 *
313
	 * The record is inserted as a document into the database collection, if exists it will be replaced.
314
	 *
315
	 * Validation will be performed before saving the record. If the validation fails,
316
	 * the record will not be saved. You can call {@link getErrors()} to retrieve the
317
	 * validation errors.
318
	 *
319
	 * @param boolean $runValidation whether to perform validation before saving the record.
320
	 *                               If the validation fails, the record will not be saved to database.
321
	 *
322
	 * @return boolean whether the saving succeeds
323
	 */
324
	public function replace($runValidation = true)
325
	{
326
		if (!$runValidation || $this->validator->validate())
327
		{
328
			$model = $this->model;
329
			if ($this->beforeSave($model))
330
			{
331
				$data = RawArray::fromModel($model);
332
				$rawResult = $this->_collection->save($data, $this->options->getSaveOptions());
333
				$result = $this->insertResult($rawResult);
334
335
				if ($result)
336
				{
337
					$this->afterSave($model);
338
					return true;
339
				}
340
				$msg = '';
341
				if (!empty($rawResult['errmsg']))
342
				{
343
					$msg = $rawResult['errmsg'];
344
				}
345
				throw new ManganException("Can't save the document to disk, or attempting to save an empty document. $msg");
346
			}
347
			AspectManager::removeAspect($model, self::AspectSaving);
348
			return false;
349
		}
350
		else
351
		{
352
			AspectManager::removeAspect($this->model, self::AspectSaving);
353
			return false;
354
		}
355
	}
356
357
	/**
358
	 * Saves the current document.
359
	 *
360
	 * The record is inserted as a document into the database collection or updated if exists.
361
	 *
362
	 * Filtered out properties will remain in database - it is partial safe.
363
	 *
364
	 * Validation will be performed before saving the record. If the validation fails,
365
	 * the record will not be saved. You can call {@link getErrors()} to retrieve the
366
	 * validation errors.
367
	 *
368
	 * @param boolean $runValidation whether to perform validation before saving the record.
369
	 *                               If the validation fails, the record will not be saved to database.
370
	 *
371
	 * @return boolean whether the saving succeeds
372
	 */
373 82
	public function save($runValidation = true)
374
	{
375 82
		return $this->upsert($runValidation);
376
	}
377
378
	/**
379
	 * Updates or inserts the current document. This will try to update existing fields.
380
	 * Will keep already stored data if present in document.
381
	 *
382
	 * If document does not exist, a new one will be inserted.
383
	 *
384
	 * @param boolean $runValidation
385
	 * @return boolean
386
	 * @throws ManganException
387
	 */
388 86
	public function upsert($runValidation = true)
389
	{
390 86
		if (!$runValidation || $this->validator->validate())
391
		{
392 86
			$model = $this->model;
393 86
			if ($this->beforeSave($model))
394
			{
395 83
				$criteria = PkManager::prepareFromModel($this->model);
396 83
				foreach ($criteria->getConditions() as $field => $value)
397
				{
398 83
					if (empty($this->model->$field))
399
					{
400 83
						$this->model->$field = $value;
401
					}
402
				}
403 83
				$result = $this->updateOne($criteria);
404
405 83
				if ($result)
406
				{
407 83
					$this->afterSave($model);
408 83
					return true;
409
				}
410
				$errmsg = '';
411
				if (!empty($this->lastResult['errmsg']))
412
				{
413
					$errmsg = ucfirst($this->lastResult['errmsg']) . '.';
414
				}
415
				throw new ManganException("Can't save the document to disk, or attempting to save an empty document. $errmsg");
416
			}
417 3
			AspectManager::removeAspect($this->model, self::AspectSaving);
418 3
			return false;
419
		}
420
		else
421
		{
422 3
			AspectManager::removeAspect($this->model, self::AspectSaving);
423 3
			return false;
424
		}
425
	}
426
427
	/**
428
	 * Reloads document from database.
429
	 * It return true if document is reloaded and false if it's no longer exists.
430
	 *
431
	 * @return boolean
432
	 */
433 2
	public function refresh()
434
	{
435 2
		$conditions = PkManager::prepareFromModel($this->model)->getConditions();
436 2
		$data = $this->getCollection()->findOne($conditions);
437 2
		if (null !== $data)
438
		{
439 2
			RawArray::toModel($data, $this->model, $this->model);
440 2
			return true;
441
		}
442
		else
443
		{
444 2
			return false;
445
		}
446
	}
447
448
	/**
449
	 * Deletes the document from database.
450
	 * @return boolean whether the deletion is successful.
451
	 */
452 10
	public function delete()
453
	{
454 10
		if ($this->beforeDelete())
455
		{
456 9
			$conditions = PkManager::prepareFromModel($this->model);
457 9
			$result = $this->deleteOne($conditions);
458
459 9
			if ($result !== false)
460
			{
461 9
				$this->afterDelete();
462 9
				return true;
463
			}
464
			else
465
			{
466 1
				return false;
467
			}
468
		}
469
		else
470
		{
471 1
			return false;
472
		}
473
	}
474
475
	/**
476
	 * Deletes one document with the specified primary keys.
477
	 * <b>Does not raise beforeDelete</b>
478
	 * See {@link find()} for detailed explanation about $condition and $params.
479
	 * @param array|CriteriaInterface $criteria query criteria.
480
	 * @return bool
481
	 */
482 11
	public function deleteOne($criteria = null)
483
	{
484 11
		$criteria = $this->sm->apply($criteria);
485
486 11
		$result = $this->getCollection()->remove($criteria->getConditions(), $this->options->getSaveOptions([
487 11
			'justOne' => true
488
		]));
489 11
		return $this->deleteResult($result);
490
	}
491
492
	/**
493
	 * Deletes document with the specified primary key.
494
	 * See {@link find()} for detailed explanation about $condition and $params.
495
	 * @param mixed                   $pkValue  primary key value(s). Use array for multiple primary keys. For
496
	 *                                          composite key, each key value must be an array (column name=>column
497
	 *                                          value).
498
	 * @param array|CriteriaInterface $criteria query criteria.
499
	 *
500
	 * @return bool
501
	 */
502 3
	public function deleteByPk($pkValue, $criteria = null)
503
	{
504 3
		if ($this->beforeDelete())
505
		{
506 2
			$criteria = $this->sm->apply($criteria);
507 2
			$criteria->mergeWith(PkManager::prepare($this->model, $pkValue));
508
509 2
			$result = $this->getCollection()->remove($criteria->getConditions(), $this->options->getSaveOptions([
510 2
				'justOne' => true
511
			]));
512 2
			return $this->deleteResult($result);
513
		}
514 1
		return false;
515
	}
516
517
	/**
518
	 * Deletes documents with the specified primary keys.
519
	 * See {@link find()} for detailed explanation about $condition and $params.
520
	 * @param mixed[]                 $pkValues Primary keys array
521
	 * @param array|CriteriaInterface $criteria query criteria.
522
	 * @return bool
523
	 */
524 3
	public function deleteAllByPk($pkValues, $criteria = null)
525
	{
526 3
		if ($this->beforeDelete())
527
		{
528 2
			$criteria = $this->sm->apply($criteria);
529 2
			$criteria->mergeWith(PkManager::prepareAll($this->model, $pkValues, $criteria));
0 ignored issues
show
Bug introduced by
It seems like \Maslosoft\Mangan\Helper..., $pkValues, $criteria) can be null; however, mergeWith() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
530 2
			$conditions = $criteria->getConditions();
531 2
			$opts = $this->options->getSaveOptions([
532 2
				'justOne' => false
533
			]);
534 2
			$result = $this->getCollection()->remove($conditions, $opts);
535 2
			return $this->deleteResult($result);
536
		}
537 1
		return false;
538
	}
539
540
	/**
541
	 * Deletes documents with the specified primary keys.
542
	 *
543
	 * **Does not raise beforeDelete event and does not emit signals**
544
	 *
545
	 * See {@link find()} for detailed explanation about $condition and $params.
546
	 *
547
	 * @param array|CriteriaInterface $criteria query criteria.
548
	 * @return bool
549
	 */
550 5
	public function deleteAll($criteria = null)
551
	{
552 5
		$criteria = $this->sm->apply($criteria);
553
554 5
		$conditions = $criteria->getConditions();
555
556
		// NOTE: Do not use [justOne => false] here
557 5
		$opts = $this->options->getSaveOptions();
558 5
		$result = $this->getCollection()->remove($conditions, $opts);
559 5
		return $this->deleteResult($result);
560
	}
561
562 117
	public function getCollection()
563
	{
564 117
		return $this->_collection;
565
	}
566
567
	/**
568
	 * Make status uniform
569
	 * @param bool|array $result
570
	 * @return bool Return true if succeed
571
	 */
572 19
	private function deleteResult($result)
573
	{
574 19
		$this->lastResult = $result;
0 ignored issues
show
Documentation Bug introduced by
It seems like $result can also be of type boolean. However, the property $lastResult is declared as type array. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
575 19
		if (is_array($result))
576
		{
577 19
			return $result['n'] > 0;
578
		}
579
		return $result;
580
	}
581
582
	/**
583
	 * Make status uniform
584
	 * @param bool|array $result
585
	 * @return bool Return true if succeed
586
	 */
587 36
	private function insertResult($result)
588
	{
589 36
		$this->lastResult = $result;
0 ignored issues
show
Documentation Bug introduced by
It seems like $result can also be of type boolean. However, the property $lastResult is declared as type array. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
590 36
		if (is_array($result))
591
		{
592 36
			return $result['ok'] > 0;
593
		}
594
		return $result;
595
	}
596
597
	/**
598
	 * Make status uniform
599
	 * @param bool|array $result
600
	 * @return bool Return true if succeed
601
	 */
602 85
	private function updateResult($result)
603
	{
604 85
		$this->lastResult = $result;
0 ignored issues
show
Documentation Bug introduced by
It seems like $result can also be of type boolean. However, the property $lastResult is declared as type array. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
605 85
		if (is_array($result))
606
		{
607 85
			return $result['ok'] > 0;
608
		}
609
		return $result;
610
	}
611
612
// <editor-fold defaultstate="collapsed" desc="Event and Signal handling">
613
614
	/**
615
	 * Take care of EventBeforeSave
616
	 * @see EventBeforeSave
617
	 * @param                 $model
618
	 * @param null|ModelEvent $event
619
	 * @return boolean
620
	 */
621 118
	private function beforeSave($model, $event = null)
622
	{
623 118
		AspectManager::addAspect($model, self::AspectSaving);
624 118
		$result = Event::Valid($model, EntityManagerInterface::EventBeforeSave);
625 118
		if ($result)
626
		{
627 115
			if (!empty($event))
628
			{
629 42
				Event::trigger($model, $event);
0 ignored issues
show
Documentation introduced by
$event is of type object<Maslosoft\Mangan\Events\ModelEvent>, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
630
			}
631 115
			(new Signal)->emit(new BeforeSave($model));
632
		}
633 118
		return $result;
634
	}
635
636
	/**
637
	 * Take care of EventAfterSave
638
	 * @see EventAfterSave
639
	 * @param                 $model
640
	 * @param null|ModelEvent $event
641
	 */
642 115
	private function afterSave($model, $event = null)
643
	{
644 115
		Event::trigger($model, EntityManagerInterface::EventAfterSave);
645 115
		if (!empty($event))
646
		{
647 41
			Event::trigger($model, $event);
0 ignored issues
show
Documentation introduced by
$event is of type object<Maslosoft\Mangan\Events\ModelEvent>, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
648
		}
649 115
		(new Signal)->emit(new AfterSave($model));
650 115
		ScenarioManager::setScenario($model, ScenariosInterface::Update);
651 115
		AspectManager::removeAspect($model, self::AspectSaving);
652 115
	}
653
654
	/**
655
	 * This method is invoked before deleting a record.
656
	 * The default implementation raises the {@link onBeforeDelete} event.
657
	 * You may override this method to do any preparation work for record deletion.
658
	 * Make sure you call the parent implementation so that the event is raised properly.
659
	 * @return boolean whether the record should be deleted. Defaults to true.
660
	 */
661 14
	private function beforeDelete()
662
	{
663 14
		AspectManager::addAspect($this->model, self::AspectRemoving);
664 14
		$result = Event::valid($this->model, EntityManagerInterface::EventBeforeDelete);
665 14
		if ($result)
666
		{
667 13
			(new Signal)->emit(new BeforeDelete($this->model));
668 13
			ScenarioManager::setScenario($this->model, ScenariosInterface::Delete);
669
		}
670 14
		return $result;
671
	}
672
673
	/**
674
	 * This method is invoked after deleting a record.
675
	 * The default implementation raises the {@link onAfterDelete} event.
676
	 * You may override this method to do postprocessing after the record is deleted.
677
	 * Make sure you call the parent implementation so that the event is raised properly.
678
	 */
679 9
	private function afterDelete()
680
	{
681 9
		$event = new ModelEvent($this->model);
682 9
		Event::trigger($this->model, EntityManagerInterface::EventAfterDelete, $event);
683 9
		(new Signal)->emit(new AfterDelete($this->model));
684 9
		AspectManager::removeAspect($this->model, self::AspectRemoving);
685 9
	}
686
687
// </editor-fold>
688
}
689