Completed
Push — master ( d1d4a9...91184f )
by Peter
12:35
created

EntityManager::getModel()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 4
ccs 2
cts 2
cp 1
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 0
crap 1
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\Traits\Finder\CreateModel;
33
use Maslosoft\Mangan\Transformers\RawArray;
34
use Maslosoft\Mangan\Transformers\SafeArray;
35
use Maslosoft\Signals\Signal;
36
use MongoCollection;
37
38
/**
39
 * EntityManager
40
 *
41
 * @author Piotr Maselkowski <pmaselkowski at gmail.com>
42
 */
43
class EntityManager implements EntityManagerInterface
44
{
45
46
	use CreateModel;
47
48
	/**
49
	 * Model
50
	 * @var AnnotatedInterface
51
	 */
52
	public $model = null;
53
54
	/**
55
	 *
56
	 * @var ScopeManager
57
	 */
58
	private $sm = null;
59
60
	/**
61
	 *
62
	 * @var
63
	 */
64
	public $meta = null;
65
66
	/**
67
	 * Options
68
	 * @var EntityOptions
69
	 */
70
	public $options = null;
71
72
	/**
73
	 * Current collection name
74
	 * @var string
75
	 */
76
	public $collectionName = '';
77
78
	/**
79
	 * Validator instance
80
	 * @var Validator
81
	 */
82
	private $validator = null;
83
84
	/**
85
	 * Current collection
86
	 * @var MongoCollection
87
	 */
88
	private $_collection = null;
89
90
	/**
91
	 * Result of last operation
92
	 * @var bool|array
93
	 */
94
	private $lastResult = [];
95
96
	/**
97
	 * Create entity manager
98
	 * @param AnnotatedInterface $model
99
	 * @param Mangan             $mangan
100
	 * @throws ManganException
101
	 */
102 124
	public function __construct(AnnotatedInterface $model, Mangan $mangan = null)
103
	{
104 124
		$this->model = $model;
105 124
		$this->sm = new ScopeManager($model);
106 124
		$this->options = new EntityOptions($model);
107 124
		$this->collectionName = CollectionNamer::nameCollection($model);
108 124
		$this->meta = ManganMeta::create($model);
109 124
		$this->validator = new Validator($model);
110 124
		if (null === $mangan)
111
		{
112 121
			$mangan = Mangan::fromModel($model);
113
		}
114 124
		if (!$this->collectionName)
115
		{
116
			throw new ManganException(sprintf('Invalid collection name for model: `%s`', $this->meta->type()->name));
117
		}
118 124
		$this->_collection = new MongoCollection($mangan->getDbInstance(), $this->collectionName);
119 124
	}
120
121
	/**
122
	 * @return AnnotatedInterface
123
	 */
124 1
	public function getModel(): AnnotatedInterface
125
	{
126 1
		return $this->model;
127
	}
128
129
	/**
130
	 * Create model related entity manager.
131
	 * This will create customized entity manger if defined in model with EntityManager annotation.
132
	 * If no custom entity manager is defined this will return default EntityManager.
133
	 * @param AnnotatedInterface $model
134
	 * @param Mangan             $mangan
135
	 * @return EntityManagerInterface
136
	 */
137 114
	public static function create($model, Mangan $mangan = null)
138
	{
139 114
		$emClass = ManganMeta::create($model)->type()->entityManager ?: static::class;
140 114
		return new $emClass($model, $mangan);
141
	}
142
143
	/**
144
	 * Set attributes en masse.
145
	 * Attributes will be filtered according to SafeAnnotation.
146
	 * Only attributes marked as safe will be set, other will be ignored.
147
	 *
148
	 * @param mixed[] $attributes
149
	 */
150
	public function setAttributes($attributes)
151
	{
152
		SafeArray::toModel($attributes, $this->model, $this->model);
153
	}
154
155
	/**
156
	 * Inserts a row into the table based on this active record attributes.
157
	 * If the table's primary key is auto-incremental and is null before insertion,
158
	 * it will be populated with the actual value after insertion.
159
	 *
160
	 * Note, validation is not performed in this method. You may call {@link validate} to perform the validation.
161
	 * After the record is inserted to DB successfully, its {@link isNewRecord} property will be set false,
162
	 * and its {@link scenario} property will be set to be 'update'.
163
	 *
164
	 * @param AnnotatedInterface $model if want to insert different model than set in constructor
165
	 *
166
	 * @return boolean whether the attributes are valid and the record is inserted successfully.
167
	 * @throws ManganException if the record is not new
168
	 * @throws ManganException on fail of insert or insert of empty document
169
	 * @throws ManganException on fail of insert, when safe flag is set to true
170
	 * @throws ManganException on timeout of db operation , when safe flag is set to true
171
	 */
172 36
	public function insert(AnnotatedInterface $model = null)
173
	{
174 36
		$model = $model ?: $this->model;
175 36
		if ($this->beforeSave($model, EntityManagerInterface::EventBeforeInsert))
176
		{
177 36
			$rawData = RawArray::fromModel($model);
178
179 36
			$opts = $this->options->getSaveOptions();
180 36
			$rawResult = $this->_collection->insert($rawData, $opts);
181 36
			$result = $this->insertResult($rawResult);
182
183 36
			if ($result)
184
			{
185 36
				$this->afterSave($model, EntityManagerInterface::EventAfterInsert);
186 36
				return true;
187
			}
188
			throw new ManganException('Can\t save the document to disk, or attempting to save an empty document. ' . ucfirst($rawResult['errmsg']));
189
		}
190
		AspectManager::removeAspect($model, self::AspectSaving);
191
		return false;
192
	}
193
194
	/**
195
	 * Updates the row represented by this active document.
196
	 * All loaded attributes will be saved to the database.
197
	 * Note, validation is not performed in this method. You may call {@link validate} to perform the validation.
198
	 *
199
	 * @param array $attributes list of attributes that need to be saved. Defaults to null,
200
	 *                          meaning all attributes that are loaded from DB will be saved.
201
	 * @return boolean whether the update is successful
202
	 * @throws ManganException if the record is new
203
	 * @throws ManganException on fail of update
204
	 * @throws ManganException on timeout of db operation , when safe flag is set to true
205
	 */
206 6
	public function update(array $attributes = null)
207
	{
208 6
		if ($this->beforeSave($this->model, EntityManagerInterface::EventBeforeUpdate))
209
		{
210 6
			$criteria = PkManager::prepareFromModel($this->model);
211 6
			$result = $this->updateOne($criteria, $attributes);
212 5
			if ($result)
213
			{
214 5
				$this->afterSave($this->model, EntityManagerInterface::EventAfterUpdate);
215 5
				return true;
216
			}
217
			throw new ManganException('Can\t save the document to disk, or attempting to save an empty document.');
218
		}
219
		AspectManager::removeAspect($this->model, self::AspectSaving);
220
		return false;
221
	}
222
223
	/**
224
	 * Updates one document with the specified criteria and attributes
225
	 *
226
	 * This is more *raw* update:
227
	 *
228
	 * * Does not raise any events or signals
229
	 * * Does not perform any validation
230
	 *
231
	 * @param array|CriteriaInterface $criteria   query criteria.
232
	 * @param array                   $attributes list of attributes that need to be saved. Defaults to null,
233
	 * @param bool Whether tu force update/upsert document
234
	 *                                            meaning all attributes that are loaded from DB will be saved.
235
	 * @return bool
236
	 */
237 86
	public function updateOne($criteria = null, array $attributes = null, $modify = false)
238
	{
239 86
		$criteria = $this->sm->apply($criteria);
240 86
		$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...
241
242
		// filter attributes if set in param
243 86
		if ($attributes !== null)
244
		{
245 3
			$meta = ManganMeta::create($this->model);
246 3
			foreach ($attributes as $key)
247
			{
248 3
				if ($meta->$key === false)
249
				{
250 3
					throw new BadAttributeException(sprintf("Unknown attribute `%s` on model `%s`", $key, get_class($this->model)));
251
				}
252
			}
253 2
			$modify = true;
254 2
			foreach ($rawData as $key => $value)
255
			{
256 2
				if (!in_array($key, $attributes))
257
				{
258 2
					unset($rawData[$key]);
259
				}
260
			}
261
		}
262
		else
263
		{
264 86
			$fields = array_keys(ManganMeta::create($this->model)->fields());
265 86
			$setFields = array_keys($rawData);
266 86
			$diff = array_diff($fields, $setFields);
267
268 86
			if (!empty($diff))
269
			{
270 6
				$modify = true;
271
			}
272
		}
273 86
		if ($modify)
274
		{
275
			// Id could be altered, so skip it as it cannot be changed
276 8
			unset($rawData['_id']);
277 8
			$data = ['$set' => $rawData];
278
		}
279
		else
280
		{
281 83
			$data = $rawData;
282
		}
283 86
		$conditions = $criteria->getConditions();
284 86
		$opts = $this->options->getSaveOptions(['multiple' => false, 'upsert' => true]);
285 86
		$collection = $this->getCollection();
286
287 86
		$result = $collection->update($conditions, $data, $opts);
288 86
		return $this->updateResult($result);
289
	}
290
291
	/**
292
	 * Atomic, in-place update method. This method does not raise
293
	 * events and does not emit signals.
294
	 *
295
	 * @param Modifier          $modifier updating rules to apply
296
	 * @param CriteriaInterface $criteria condition to limit updating rules
297
	 * @return boolean
298
	 */
299 1
	public function updateAll(Modifier $modifier, CriteriaInterface $criteria = null)
300
	{
301 1
		if ($modifier->canApply())
302
		{
303 1
			$criteria = $this->sm->apply($criteria);
304 1
			$conditions = $criteria->getConditions();
305 1
			$mods = $modifier->getModifiers();
306 1
			$opts = $this->options->getSaveOptions([
307 1
				'upsert' => false,
308
				'multiple' => true
309
			]);
310 1
			$result = $this->getCollection()->update($conditions, $mods, $opts);
311 1
			return $this->updateResult($result);
312
		}
313
		else
314
		{
315
			return false;
316
		}
317
	}
318
319
	/**
320
	 * Find and modify single document atomically.
321
	 *
322
	 * By default this function will return updated document, ie document
323
	 * with applied Modifier operations.
324
	 *
325
	 * To return document before applied updates, set parameter
326
	 * `$returnUpdated` to false.
327
	 *
328
	 * This function will raise events and signals before operation on
329
	 * current model.
330
	 *
331
	 * The events and signals after operation will be performed
332
	 * on the returned model, depending on `$returnUpdated` parameter.
333
	 *
334
	 * @param array|CriteriaInterface $criteria
335
	 * @param Modifier                $modifier
336
	 * @param bool                    $returnUpdated
337
	 * @return AnnotatedInterface|null
338
	 */
339 2
	public function findAndModify($criteria, Modifier $modifier, $returnUpdated = true)
340
	{
341 2
		if (!$this->beforeSave($this->model, EntityManagerInterface::EventBeforeUpdate))
342
		{
343
			AspectManager::removeAspect($this->model, self::AspectSaving);
344
			return null;
345
		}
346 2
		if ($modifier->canApply())
347
		{
348 2
			$criteria = $this->sm->apply($criteria);
349 2
			$conditions = $criteria->getConditions();
350 2
			$mods = $modifier->getModifiers();
351 2
			$opts = [];
352 2
			if($returnUpdated)
353
			{
354 2
				$opts = ['new' => true];
355
			}
356 2
			$data = $this->getCollection()->findAndModify($conditions, $mods, null, $opts);
357 2
			if(empty($data))
358
			{
359 1
				return null;
360
			}
361 1
			$model = $this->createModel($data, $this->model);
0 ignored issues
show
Unused Code introduced by
The call to EntityManager::createModel() has too many arguments starting with $this->model.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
362 1
			ScenarioManager::setScenario($model, ScenariosInterface::Update);
363 1
			return $model;
364
		}
365
		else
366
		{
367
			return false;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return false; (false) is incompatible with the return type declared by the interface Maslosoft\Mangan\Interfa...nterface::findAndModify of type Maslosoft\Addendum\Inter...AnnotatedInterface|null.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

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

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
368
		}
369
	}
370
371
372
	/**
373
	 * Replaces the current document.
374
	 *
375
	 * **NOTE: This will overwrite entire document.**
376
	 * Any filtered out properties will be removed as well.
377
	 *
378
	 * The record is inserted as a document into the database collection, if exists it will be replaced.
379
	 *
380
	 * Validation will be performed before saving the record. If the validation fails,
381
	 * the record will not be saved. You can call {@link getErrors()} to retrieve the
382
	 * validation errors.
383
	 *
384
	 * @param boolean $runValidation whether to perform validation before saving the record.
385
	 *                               If the validation fails, the record will not be saved to database.
386
	 *
387
	 * @return boolean whether the saving succeeds
388
	 */
389
	public function replace($runValidation = true)
390
	{
391
		$this->beforeValidate($this->model);
392
		if (!$runValidation || $this->validator->validate())
393
		{
394
			$model = $this->model;
395
			if ($this->beforeSave($model))
396
			{
397
				$data = RawArray::fromModel($model);
398
				$rawResult = $this->_collection->save($data, $this->options->getSaveOptions());
399
				$result = $this->insertResult($rawResult);
400
401
				if ($result)
402
				{
403
					$this->afterSave($model);
404
					return true;
405
				}
406
				$msg = '';
407
				if (!empty($rawResult['errmsg']))
408
				{
409
					$msg = $rawResult['errmsg'];
410
				}
411
				throw new ManganException("Can't save the document to disk, or attempting to save an empty document. $msg");
412
			}
413
			AspectManager::removeAspect($model, self::AspectSaving);
414
			return false;
415
		}
416
		else
417
		{
418
			AspectManager::removeAspect($this->model, self::AspectSaving);
419
			return false;
420
		}
421
	}
422
423
	/**
424
	 * Saves the current document.
425
	 *
426
	 * The record is inserted as a document into the database collection or updated if exists.
427
	 *
428
	 * Filtered out properties will remain in database - it is partial safe.
429
	 *
430
	 * Validation will be performed before saving the record. If the validation fails,
431
	 * the record will not be saved. You can call {@link getErrors()} to retrieve the
432
	 * validation errors.
433
	 *
434
	 * @param boolean $runValidation whether to perform validation before saving the record.
435
	 *                               If the validation fails, the record will not be saved to database.
436
	 *
437
	 * @return boolean whether the saving succeeds
438
	 */
439 83
	public function save($runValidation = true)
440
	{
441 83
		return $this->upsert($runValidation);
442
	}
443
444
	/**
445
	 * Updates or inserts the current document. This will try to update existing fields.
446
	 * Will keep already stored data if present in document.
447
	 *
448
	 * If document does not exist, a new one will be inserted.
449
	 *
450
	 * @param boolean $runValidation
451
	 * @return boolean
452
	 * @throws ManganException
453
	 */
454 87
	public function upsert($runValidation = true)
455
	{
456 87
		$this->beforeValidate($this->model);
457 87
		if (!$runValidation || $this->validator->validate())
458
		{
459 87
			$model = $this->model;
460 87
			if ($this->beforeSave($model))
461
			{
462 84
				$criteria = PkManager::prepareFromModel($this->model);
463 84
				foreach ($criteria->getConditions() as $field => $value)
464
				{
465 84
					if (empty($this->model->$field))
466
					{
467 84
						$this->model->$field = $value;
468
					}
469
				}
470 84
				$result = $this->updateOne($criteria);
471
472 84
				if ($result)
473
				{
474 84
					$this->afterSave($model);
475 84
					return true;
476
				}
477
				$errmsg = '';
478
				if (!empty($this->lastResult['errmsg']))
479
				{
480
					$errmsg = ucfirst($this->lastResult['errmsg']) . '.';
481
				}
482
				throw new ManganException("Can't save the document to disk, or attempting to save an empty document. $errmsg");
483
			}
484 3
			AspectManager::removeAspect($this->model, self::AspectSaving);
485 3
			return false;
486
		}
487
		else
488
		{
489 3
			AspectManager::removeAspect($this->model, self::AspectSaving);
490 3
			return false;
491
		}
492
	}
493
494
	/**
495
	 * Reloads document from database.
496
	 * It return true if document is reloaded and false if it's no longer exists.
497
	 *
498
	 * @return boolean
499
	 */
500 2
	public function refresh()
501
	{
502 2
		$conditions = PkManager::prepareFromModel($this->model)->getConditions();
503 2
		$data = $this->getCollection()->findOne($conditions);
504 2
		if (null !== $data)
505
		{
506 2
			RawArray::toModel($data, $this->model, $this->model);
507 2
			return true;
508
		}
509
		else
510
		{
511 2
			return false;
512
		}
513
	}
514
515
	/**
516
	 * Deletes the document from database.
517
	 * @return boolean whether the deletion is successful.
518
	 */
519 10
	public function delete()
520
	{
521 10
		if ($this->beforeDelete())
522
		{
523 9
			$conditions = PkManager::prepareFromModel($this->model);
524 9
			$result = $this->deleteOne($conditions);
525
526 9
			if ($result !== false)
527
			{
528 9
				$this->afterDelete();
529 9
				return true;
530
			}
531
			else
532
			{
533 1
				return false;
534
			}
535
		}
536
		else
537
		{
538 1
			return false;
539
		}
540
	}
541
542
	/**
543
	 * Deletes one document with the specified primary keys.
544
	 * <b>Does not raise beforeDelete</b>
545
	 * See {@link find()} for detailed explanation about $condition and $params.
546
	 * @param array|CriteriaInterface $criteria query criteria.
547
	 * @return bool
548
	 */
549 11
	public function deleteOne($criteria = null)
550
	{
551 11
		$criteria = $this->sm->apply($criteria);
552
553 11
		$result = $this->getCollection()->remove($criteria->getConditions(), $this->options->getSaveOptions([
554 11
			'justOne' => true
555
		]));
556 11
		return $this->deleteResult($result);
557
	}
558
559
	/**
560
	 * Deletes document with the specified primary key.
561
	 * See {@link find()} for detailed explanation about $condition and $params.
562
	 * @param mixed                   $pkValue  primary key value(s). Use array for multiple primary keys. For
563
	 *                                          composite key, each key value must be an array (column name=>column
564
	 *                                          value).
565
	 * @param array|CriteriaInterface $criteria query criteria.
566
	 *
567
	 * @return bool
568
	 */
569 3
	public function deleteByPk($pkValue, $criteria = null)
570
	{
571 3
		if ($this->beforeDelete())
572
		{
573 2
			$criteria = $this->sm->apply($criteria);
574 2
			$criteria->mergeWith(PkManager::prepare($this->model, $pkValue));
575
576 2
			$result = $this->getCollection()->remove($criteria->getConditions(), $this->options->getSaveOptions([
577 2
				'justOne' => true
578
			]));
579 2
			return $this->deleteResult($result);
580
		}
581 1
		return false;
582
	}
583
584
	/**
585
	 * Deletes documents with the specified primary keys.
586
	 * See {@link find()} for detailed explanation about $condition and $params.
587
	 * @param mixed[]                 $pkValues Primary keys array
588
	 * @param array|CriteriaInterface $criteria query criteria.
589
	 * @return bool
590
	 */
591 3
	public function deleteAllByPk($pkValues, $criteria = null)
592
	{
593 3
		if ($this->beforeDelete())
594
		{
595 2
			$criteria = $this->sm->apply($criteria);
596 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...
597 2
			$conditions = $criteria->getConditions();
598 2
			$opts = $this->options->getSaveOptions([
599 2
				'justOne' => false
600
			]);
601 2
			$result = $this->getCollection()->remove($conditions, $opts);
602 2
			return $this->deleteResult($result);
603
		}
604 1
		return false;
605
	}
606
607
	/**
608
	 * Deletes documents with the specified primary keys.
609
	 *
610
	 * **Does not raise beforeDelete event and does not emit signals**
611
	 *
612
	 * See {@link find()} for detailed explanation about $condition and $params.
613
	 *
614
	 * @param array|CriteriaInterface $criteria query criteria.
615
	 * @return bool
616
	 */
617 5
	public function deleteAll($criteria = null)
618
	{
619 5
		$criteria = $this->sm->apply($criteria);
620
621 5
		$conditions = $criteria->getConditions();
622
623
		// NOTE: Do not use [justOne => false] here
624 5
		$opts = $this->options->getSaveOptions();
625 5
		$result = $this->getCollection()->remove($conditions, $opts);
626 5
		return $this->deleteResult($result);
627
	}
628
629 119
	public function getCollection()
630
	{
631 119
		return $this->_collection;
632
	}
633
634
	/**
635
	 * Make status uniform
636
	 * @param bool|array $result
637
	 * @return bool Return true if succeed
638
	 */
639 19
	private function deleteResult($result)
640
	{
641 19
		$this->lastResult = $result;
642 19
		if (is_array($result))
643
		{
644 19
			return $result['n'] > 0;
645
		}
646
		return $result;
647
	}
648
649
	/**
650
	 * Make status uniform
651
	 * @param bool|array $result
652
	 * @return bool Return true if succeed
653
	 */
654 36
	private function insertResult($result)
655
	{
656 36
		$this->lastResult = $result;
657 36
		if (is_array($result))
658
		{
659 36
			return $result['ok'] > 0;
660
		}
661
		return $result;
662
	}
663
664
	/**
665
	 * Make status uniform
666
	 * @param bool|array $result
667
	 * @return bool Return true if succeed
668
	 */
669 86
	private function updateResult($result)
670
	{
671 86
		$this->lastResult = $result;
672 86
		if (is_array($result))
673
		{
674 86
			return $result['ok'] > 0;
675
		}
676
		return $result;
677
	}
678
679
// <editor-fold defaultstate="collapsed" desc="Event and Signal handling">
680
681 87
	private function beforeValidate($model)
682
	{
683 87
		AspectManager::addAspect($model, self::AspectSaving);
684 87
	}
685
686
	/**
687
	 * Take care of EventBeforeSave
688
	 * @see EventBeforeSave
689
	 * @param                 $model
690
	 * @param string $event
691
	 * @return boolean
692
	 */
693 120
	private function beforeSave($model, $event = null)
694
	{
695 120
		AspectManager::addAspect($model, self::AspectSaving);
696 120
		$result = Event::Valid($model, EntityManagerInterface::EventBeforeSave);
697 120
		if ($result)
698
		{
699 117
			if (!empty($event))
700
			{
701 44
				Event::trigger($model, $event);
702
			}
703 117
			(new Signal)->emit(new BeforeSave($model));
704
		}
705 120
		return $result;
706
	}
707
708
	/**
709
	 * Take care of EventAfterSave
710
	 * @see EventAfterSave
711
	 * @param                 $model
712
	 * @param null|ModelEvent $event
713
	 */
714 116
	private function afterSave($model, $event = null)
715
	{
716 116
		Event::trigger($model, EntityManagerInterface::EventAfterSave);
717 116
		if (!empty($event))
718
		{
719 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...
720
		}
721 116
		(new Signal)->emit(new AfterSave($model));
722 116
		ScenarioManager::setScenario($model, ScenariosInterface::Update);
723 116
		AspectManager::removeAspect($model, self::AspectSaving);
724 116
	}
725
726
	/**
727
	 * This method is invoked before deleting a record.
728
	 * The default implementation raises the {@link onBeforeDelete} event.
729
	 * You may override this method to do any preparation work for record deletion.
730
	 * Make sure you call the parent implementation so that the event is raised properly.
731
	 * @return boolean whether the record should be deleted. Defaults to true.
732
	 */
733 14
	private function beforeDelete()
734
	{
735 14
		AspectManager::addAspect($this->model, self::AspectRemoving);
736 14
		$result = Event::valid($this->model, EntityManagerInterface::EventBeforeDelete);
737 14
		if ($result)
738
		{
739 13
			(new Signal)->emit(new BeforeDelete($this->model));
740 13
			ScenarioManager::setScenario($this->model, ScenariosInterface::Delete);
741
		}
742 14
		return $result;
743
	}
744
745
	/**
746
	 * This method is invoked after deleting a record.
747
	 * The default implementation raises the {@link onAfterDelete} event.
748
	 * You may override this method to do postprocessing after the record is deleted.
749
	 * Make sure you call the parent implementation so that the event is raised properly.
750
	 */
751 9
	private function afterDelete()
752
	{
753 9
		$event = new ModelEvent($this->model);
754 9
		Event::trigger($this->model, EntityManagerInterface::EventAfterDelete, $event);
755 9
		(new Signal)->emit(new AfterDelete($this->model));
756 9
		AspectManager::removeAspect($this->model, self::AspectRemoving);
757 9
	}
758
759
// </editor-fold>
760
}
761