Passed
Pull Request — master (#29)
by Sébastien
08:57
created

EntityRepository::deleteBy()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
eloc 2
dl 0
loc 4
ccs 0
cts 2
cp 0
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 1
crap 2
1
<?php
2
3
namespace Bdf\Prime\Repository;
4
5
use BadMethodCallException;
6
use Bdf\Event\EventNotifier;
7
use Bdf\Prime\Cache\CacheInterface;
8
use Bdf\Prime\Collection\CollectionFactory;
9
use Bdf\Prime\Collection\CollectionInterface;
10
use Bdf\Prime\Collection\EntityCollection;
11
use Bdf\Prime\Collection\Indexer\SingleEntityIndexer;
12
use Bdf\Prime\Connection\ConnectionInterface;
13
use Bdf\Prime\Connection\Event\ConnectionClosedListenerInterface;
14
use Bdf\Prime\Connection\TransactionManagerInterface;
15
use Bdf\Prime\Entity\Criteria;
16
use Bdf\Prime\Events;
17
use Bdf\Prime\Exception\PrimeException;
18
use Bdf\Prime\Mapper\Mapper;
19
use Bdf\Prime\Mapper\Metadata;
20
use Bdf\Prime\Query\Contract\ReadOperation;
21
use Bdf\Prime\Query\Contract\WriteOperation;
22
use Bdf\Prime\Query\QueryInterface;
23
use Bdf\Prime\Query\QueryRepositoryExtension;
24
use Bdf\Prime\Relations\EntityRelation;
25
use Bdf\Prime\Relations\Relation;
26
use Bdf\Prime\Relations\RelationInterface;
27
use Bdf\Prime\Repository\Write\Writer;
28
use Bdf\Prime\Repository\Write\WriterInterface;
29
use Bdf\Prime\Schema\NullResolver;
30
use Bdf\Prime\Schema\Resolver;
31
use Bdf\Prime\Schema\ResolverInterface;
32
use Bdf\Prime\ServiceLocator;
33
use Closure;
34
use Doctrine\Common\EventSubscriber;
35
use Exception;
36
37
/**
38
 * Db repository
39
 * 
40
 * implementation de l'abstraction d'un dépot de données.
0 ignored issues
show
Coding Style introduced by
Doc comment long description must start with a capital letter
Loading history...
41
 * 
42
 * @todo fix: il est possible de desactiver temporairement le cache sur des methodes d ecriture
0 ignored issues
show
Coding Style introduced by
Comment refers to a TODO task

This check looks TODO comments that have been left in the code.

``TODO``s show that something is left unfinished and should be attended to.

Loading history...
43
 *
44
 * @package Bdf\Prime\Repository
45
 *
46
 * @template E as object
47
 * @implements RepositoryInterface<E>
48
 * @implements RepositoryEventsSubscriberInterface<E>
49
 *
50
 * @mixin RepositoryQueryFactory<E>
51
 */
52
class EntityRepository implements RepositoryInterface, EventSubscriber, ConnectionClosedListenerInterface, RepositoryEventsSubscriberInterface
53
{
54
    use EventNotifier;
55
    
56
    /**
57
     * @var Mapper<E>
0 ignored issues
show
introduced by
Expected "MapperE" but found "Mapper<E>" for @var tag in member variable comment
Loading history...
58
     */
59
    protected $mapper;
60
    
61
    /**
62
     * @var ServiceLocator 
0 ignored issues
show
introduced by
Expected "ServiceLocator" but found "ServiceLocator " for @var tag in member variable comment
Loading history...
63
     */
64
    protected $serviceLocator;
65
    
66
    /**
67
     * Query result cache
68
     * 
69
     * @var CacheInterface
70
     */
71
    protected $resultCache;
72
    
73
    /**
74
     * Disable the global constraints for one query
75
     * 
76
     * @var bool
0 ignored issues
show
Bug introduced by
Expected "boolean" but found "bool" for @var tag in member variable comment
Loading history...
77
     */
78
    protected $withoutConstraints = false;
79
    
80
    /**
81
     * Cache of relation instance
82
     * 
83
     * @var RelationInterface[]
84
     */
85
    protected $relations = [];
86
87
    /**
88
     * The collection factory
89
     *
90
     * @var CollectionFactory
91
     */
92
    protected $collectionFactory;
93
94
    /**
95
     * @var RepositoryQueryFactory<E>
0 ignored issues
show
introduced by
Expected "RepositoryQueryFactoryE" but found "RepositoryQueryFactory<E>" for @var tag in member variable comment
Loading history...
96
     */
97
    protected $queries;
98
99
    /**
100
     * @var WriterInterface<E>
0 ignored issues
show
introduced by
Expected "WriterInterfaceE" but found "WriterInterface<E>" for @var tag in member variable comment
Loading history...
101
     */
102
    protected $writer;
103
104
105
    /**
106
     * Constructor
107
     * 
108
     * @param Mapper<E> $mapper
0 ignored issues
show
introduced by
Expected "MapperE" but found "Mapper<E>" for parameter type
Loading history...
109
     * @param ServiceLocator $serviceLocator
110
     * @param CacheInterface|null $cache
111
     */
112 137
    public function __construct(Mapper $mapper, ServiceLocator $serviceLocator, ?CacheInterface $cache = null)
0 ignored issues
show
Coding Style introduced by
Expected 1 blank line before function; 2 found
Loading history...
113
    {
114 137
        $this->resultCache = $cache;
115 137
        $this->mapper = $mapper;
116 137
        $this->serviceLocator = $serviceLocator;
117
118 137
        $this->collectionFactory = CollectionFactory::forRepository($this);
119 137
        $this->queries = new RepositoryQueryFactory($this, $cache);
120 137
        $this->writer = new Writer($this, $serviceLocator);
121
122 137
        $this->mapper->events($this);
123 137
        $this->connection()->getEventManager()->addEventSubscriber($this);
124 137
    }
125
    
126
    /**
0 ignored issues
show
Coding Style introduced by
Parameter $entity should have a doc-comment as per coding-style.
Loading history...
127
     * {@inheritdoc}
128
     */
129 145
    public function repository($entity): ?RepositoryInterface
130
    {
131 145
        return $this->serviceLocator->repository($entity);
132
    }
133
134
    /**
135
     * {@inheritdoc}
136
     */
137 803
    public function mapper(): Mapper
138
    {
139 803
        return $this->mapper;
140
    }
141
    
142
    /**
143
     * {@inheritdoc}
144
     */
145 605
    public function metadata(): Metadata
146
    {
147 605
        return $this->mapper->metadata();
148
    }
149
    
150
    /**
151
     * {@inheritdoc}
152
     */
153 442
    public function isReadOnly(): bool
154
    {
155 442
        return $this->mapper->isReadOnly();
156
    }
157
    
158
    /**
0 ignored issues
show
Coding Style introduced by
Parameter $criteria should have a doc-comment as per coding-style.
Loading history...
159
     * {@inheritdoc}
160
     */
161 1
    public function criteria(array $criteria = []): Criteria
162
    {
163 1
        return new Criteria($criteria);
164
    }
165
    
166
    /**
0 ignored issues
show
Coding Style introduced by
Parameter $data should have a doc-comment as per coding-style.
Loading history...
167
     * {@inheritdoc}
168
     */
169 68
    public function entity(array $data = [])
170
    {
171 68
        return $this->mapper->entity($data);
172
    }
173
    
174
    /**
175
     * {@inheritdoc}
176
     */
177 54
    public function entityName(): string
178
    {
179 54
        return $this->mapper->metadata()->entityName;
180
    }
181
182
    /**
183
     * {@inheritdoc}
184
     */
185 71
    public function entityClass(): string
186
    {
187 71
        return $this->mapper->metadata()->entityClass;
188
    }
189
190
    /**
0 ignored issues
show
Coding Style introduced by
Parameter $entities should have a doc-comment as per coding-style.
Loading history...
191
     * {@inheritdoc}
192
     */
193 20
    public function collection(array $entities = []): CollectionInterface
194
    {
195 20
        return new EntityCollection($this, $entities);
0 ignored issues
show
Bug introduced by
$entities of type array is incompatible with the type Bdf\Prime\Collection\CollectionInterface expected by parameter $storage of Bdf\Prime\Collection\Ent...llection::__construct(). ( Ignorable by Annotation )

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

195
        return new EntityCollection($this, /** @scrutinizer ignore-type */ $entities);
Loading history...
196
    }
197
198
    /**
199
     * {@inheritdoc}
200
     */
201 471
    public function collectionFactory(): CollectionFactory
202
    {
203 471
        return $this->collectionFactory;
204
    }
205
206
    /**
207
     * Hydrate on property value of an entity
208
     *
209
     * @param E $entity
210
     * @param string $property
211
     * @param mixed  $value
212
     *
213
     * @return void
0 ignored issues
show
introduced by
If there is no return value for a function, there must not be a @return tag.
Loading history...
214
     *
215
     * @see Mapper::hydrateOne()
216
     */
217
    public function hydrateOne($entity, string $property, $value): void
0 ignored issues
show
introduced by
Type hint "E" missing for $entity
Loading history...
218
    {
219
        $this->mapper->hydrateOne($entity, $property, $value);
220
    }
221
222
    /**
223
     * Get attribute value of an entity
224
     *
225
     * @param E $entity
226
     * @param string $property
227
     *
228
     * @return mixed
229
     *
230
     * @see Mapper::extractOne()
231
     */
232 52
    public function extractOne($entity, string $property)
0 ignored issues
show
introduced by
Type hint "E" missing for $entity
Loading history...
233
    {
234 52
        return $this->mapper->extractOne($entity, $property);
235
    }
236
237
    /**
238
     * {@inheritdoc}
239
     */
240 639
    public function connection(): ConnectionInterface
241
    {
242 639
        return $this->serviceLocator->connection($this->mapper->metadata()->connection);
243
    }
244
    
245
    /**
246
     * Set the new connection for next queries
247
     * 
248
     * If work is set, the connection will be available only for the work content
249
     * Else the repository will be linked with this new connection
0 ignored issues
show
introduced by
Doc comment long description must end with a full stop
Loading history...
250
     * 
251
     * @param string   $connection
252
     * @param Closure|null $work
253
     * 
254
     * @return $this|mixed  Returns the work result if set or the instance if not set
255
     */
256 6
    public function on($connection, ?Closure $work = null)
257
    {
258 6
        $original = $this->changeActiveConnection($connection);
259
260 6
        if ($work !== null) {
261
            try {
262 3
                return $work($this);
263
            } finally {
264 3
                $this->changeActiveConnection($original);
265
            }
266
        }
267
        
268 3
        return $this;
269
    }
270
271
    /**
272
     * Launch transactional queries
273
     * 
274
     * @param callable(EntityRepository):R $work
0 ignored issues
show
introduced by
Expected "callableEntityRepositoryR" but found "callable(EntityRepository):R" for parameter type
Loading history...
275
     * @return R
0 ignored issues
show
Coding Style introduced by
Tag @return cannot be grouped with parameter tags in a doc comment
Loading history...
276
     * 
277
     * @throws Exception
278
     * @throws PrimeException
279
     *
280
     * @template R
281
     */
0 ignored issues
show
Coding Style Documentation introduced by
Missing @throws tag for "BadMethodCallException" exception
Loading history...
282 27
    public function transaction(callable $work)
283
    {
284 27
        $connection = $this->connection();
285
286 27
        if (!$connection instanceof TransactionManagerInterface) {
287
            throw new BadMethodCallException('Transactions are not supported by the connection '.$connection->getName());
288
        }
289
290
        // @todo handle Doctrine DBAL Exception ?
0 ignored issues
show
Coding Style Best Practice introduced by
Comments for TODO tasks are often forgotten in the code; it might be better to use a dedicated issue tracker.
Loading history...
291
        // @todo transaction method on connection ?
0 ignored issues
show
Coding Style Best Practice introduced by
Comments for TODO tasks are often forgotten in the code; it might be better to use a dedicated issue tracker.
Loading history...
292
        try {
293 27
            $connection->beginTransaction();
294
295 27
            $result = $work($this);
296
297 26
            if ($result === false) {
298
                $connection->rollback();
299
            } else {
300 26
                $connection->commit();
301
            }
302 1
        } catch (Exception $e) {
303 1
            $connection->rollback();
304
305 1
            throw $e;
306
        }
307
308 26
        return $result;
309
    }
310
311
    /**
312
     * Load relations on given entity
313
     * If the relation is already loaded, the relation will not be reloaded
314
     * Use reloadRelation for force loading
315
     * 
316
     * @param E $entity
317
     * @param string|array $relations
318
     *
319
     * @return void
320
     * @throws PrimeException
321
     *
322
     * @see EntityRepository::reloadRelations() For force load relations
323
     */
324
    #[ReadOperation]
325 43
    public function loadRelations($entity, $relations): void
326
    {
327 43
        foreach (Relation::sanitizeRelations((array)$relations) as $relationName => $meta) {
0 ignored issues
show
Coding Style introduced by
Expected 1 space(s) after cast statement; 0 found
Loading history...
328 43
            $this->relation($relationName)->loadIfNotLoaded(
329 43
                new SingleEntityIndexer($this->mapper, $entity),
330 43
                $meta['relations'],
331 43
                $meta['constraints']
332
            );
333
        }
334 42
    }
335
336
    /**
337
     * Force loading relations on given entity
338
     *
339
     * @param E $entity
340
     * @param string|array $relations
341
     *
342
     * @return void
343
     * @throws PrimeException
344
     *
345
     * @see EntityRepository::loadRelations() For loading relation only if not yet loaded
346
     */
347
    #[ReadOperation]
348 9
    public function reloadRelations($entity, $relations): void
349
    {
350 9
        foreach (Relation::sanitizeRelations((array)$relations) as $relationName => $meta) {
0 ignored issues
show
Coding Style introduced by
Expected 1 space(s) after cast statement; 0 found
Loading history...
351 9
            $this->relation($relationName)->load(
352 9
                new SingleEntityIndexer($this->mapper, $entity),
353 9
                $meta['relations'],
354 9
                $meta['constraints']
355
            );
356
        }
357 9
    }
358
359
    /**
360
     * Get a entity relation wrapper linked to the entity
361
     *
362
     * @param string $relationName
363
     * @param E $entity
364
     *
365
     * @return EntityRelation<E, object>
366
     */
367 71
    public function onRelation(string $relationName, $entity): EntityRelation
0 ignored issues
show
introduced by
Type hint "E" missing for $entity
Loading history...
368
    {
369 71
        return new EntityRelation($entity, $this->relation($relationName));
370
    }
371
372
    /**
0 ignored issues
show
Coding Style introduced by
Parameter $relationName should have a doc-comment as per coding-style.
Loading history...
373
     * {@inheritdoc}
374
     */
375 199
    public function relation(string $relationName): RelationInterface
376
    {
377 199
        if (!isset($this->relations[$relationName])) {
378 50
            $this->relations[$relationName] = Relation::make($this, $relationName, $this->mapper->relation($relationName));
379
        }
380
        
381 195
        return $this->relations[$relationName];
382
    }
383
384
    /**
385
     * {@inheritdoc}
386
     */
387
    #[WriteOperation]
388 12
    public function saveAll($entity, $relations): int
389
    {
390 12
        $relations = Relation::sanitizeRelations((array)$relations);
0 ignored issues
show
Coding Style introduced by
Expected 1 space(s) after cast statement; 0 found
Loading history...
391
392
        return $this->transaction(function() use($entity, $relations) {
0 ignored issues
show
Coding Style introduced by
Expected 1 space after USE keyword; found 0
Loading history...
Coding Style introduced by
Expected 1 space after FUNCTION keyword; 0 found
Loading history...
Bug Best Practice introduced by
The expression return $this->transactio...ion(...) { /* ... */ }) returns the type Bdf\Prime\Repository\R which is incompatible with the type-hinted return integer.
Loading history...
393 12
            $nb = $this->save($entity);
394
395 12
            foreach ($relations as $relationName => $info) {
396 6
                $nb += $this->relation($relationName)->saveAll($entity, $info['relations']);
397
            }
398
399 12
            return $nb;
400 12
        });
401
    }
402
403
    /**
404
     * {@inheritdoc}
405
     */
406
    #[WriteOperation]
407 10
    public function deleteAll($entity, $relations): int
408
    {
409 10
        $relations = Relation::sanitizeRelations((array)$relations);
0 ignored issues
show
Coding Style introduced by
Expected 1 space(s) after cast statement; 0 found
Loading history...
410
411
        return $this->transaction(function() use($entity, $relations) {
0 ignored issues
show
Coding Style introduced by
Expected 1 space after FUNCTION keyword; 0 found
Loading history...
Coding Style introduced by
Expected 1 space after USE keyword; found 0
Loading history...
Bug Best Practice introduced by
The expression return $this->transactio...ion(...) { /* ... */ }) returns the type Bdf\Prime\Repository\R which is incompatible with the type-hinted return integer.
Loading history...
412 10
            $nb = $this->delete($entity);
413
414 10
            foreach ($relations as $relationName => $info) {
415 4
                $nb += $this->relation($relationName)->deleteAll($entity, $info['relations']);
416
            }
417
418 10
            return $nb;
419 10
        });
420
    }
421
422
    /**
0 ignored issues
show
Coding Style introduced by
Parameter $context should have a doc-comment as per coding-style.
Loading history...
423
     * {@inheritdoc}
424
     */
425 525
    public function constraints(string $context = null): array
426
    {
427 525
        if ($this->withoutConstraints === true) {
428 4
            $this->withoutConstraints = false;
429 4
            return [];
430
        }
431
432 524
        $constraints = $this->metadata()->constraints;
433
434 524
        if ($context && is_array($constraints)) {
435 403
            $context .= '.';
436 403
            foreach ($constraints as $key => $value) {
437 32
                $constraints[$context.$key] = $value;
438 32
                unset($constraints[$key]);
439
            }
440
        }
441
442 524
        return $constraints;
443
    }
444
    
445
    /**
446
     * Disable global constraints on this repository.
447
     * Only for the current query
448
     * 
449
     * @return $this
450
     */
451 4
    public function withoutConstraints()
452
    {
453 4
        $this->withoutConstraints = true;
454
455 4
        return $this;
456
    }
457
458
    /**
459
     * Check whether the current query has global constraints
460
     *
461
     * @return bool
0 ignored issues
show
Coding Style introduced by
Expected "boolean" but found "bool" for function return type
Loading history...
462
     */
463 4
    public function isWithoutConstraints(): bool
464
    {
465 4
        return $this->withoutConstraints;
466
    }
467
468
    /**
469
     * Get query builder
470
     * 
471
     * @return QueryInterface<ConnectionInterface, E>
472
     */
473 281
    public function builder()
474
    {
475 281
        return $this->queries->builder();
476
    }
477
478
    /**
479
     * {@inheritdoc}
480
     */
481 226
    public function queries(): RepositoryQueryFactory
482
    {
483 226
        return $this->queries;
484
    }
485
486
    /**
487
     * {@inheritdoc}
488
     */
489 4
    public function writer(): WriterInterface
490
    {
491 4
        return $this->writer;
492
    }
493
494
    /**
495
     * Count entity
496
     * 
497
     * @param array $criteria
498
     * @param string|array|null $attributes
499
     * 
500
     * @return int
501
     * @throws PrimeException
502
     */
503
    #[ReadOperation]
504 42
    public function count(array $criteria = [], $attributes = null): int
505
    {
506
        /** @psalm-suppress UndefinedInterfaceMethod */
0 ignored issues
show
Coding Style introduced by
The close comment tag must be the only content on the line
Loading history...
Coding Style introduced by
The open comment tag must be the only content on the line
Loading history...
Coding Style introduced by
Block comments must be started with /*
Loading history...
Coding Style introduced by
Inline doc block comments are not allowed; use "/* Comment */" or "// Comment" instead
Loading history...
507 42
        return $this->builder()->where($criteria)->count($attributes);
0 ignored issues
show
Bug introduced by
The method count() does not exist on Bdf\Prime\Query\QueryInterface. Since it exists in all sub-types, consider adding an abstract or default implementation to Bdf\Prime\Query\QueryInterface. ( Ignorable by Annotation )

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

507
        return $this->builder()->where($criteria)->/** @scrutinizer ignore-call */ count($attributes);
Loading history...
508
    }
509
510
    /**
511
     * {@inheritdoc}
512
     */
513
    #[ReadOperation]
514 45
    public function exists($entity): bool
515
    {
516 45
        return $this->queries->countKeyValue($this->mapper()->primaryCriteria($entity)) > 0;
517
    }
518
519
    /**
520
     * {@inheritdoc}
521
     */
522
    #[ReadOperation]
523 40
    public function refresh($entity, array $criteria = [])
524
    {
525 40
        if (empty($criteria)) {
526 40
            return $this->queries->findById($this->mapper()->primaryCriteria($entity));
527
        }
528
529 1
        $criteria += $this->mapper()->primaryCriteria($entity);
530
531 1
        return $this->builder()->where($criteria)->first();
532
    }
533
534
    /**
535
     * Check if the entity is new
536
     *
537
     * @param E $entity
538
     *
539
     * @return bool|null Returns null if entity is composite primary
0 ignored issues
show
Coding Style introduced by
Expected "boolean|null" but found "bool|null" for function return type
Loading history...
540
     */
541 72
    public function isNew($entity): ?bool
0 ignored issues
show
introduced by
Type hint "E" missing for $entity
Loading history...
542
    {
543 72
        $metadata = $this->mapper->metadata();
544
545 72
        if ($metadata->isCompositePrimaryKey()) {
546 6
            return null;
547
        }
548
549 68
        $primaryValue = $this->mapper->getId($entity);
550
551 68
        if (empty($primaryValue)) {
552 28
            return true;
553
        }
554
555 46
        return $metadata->isForeignPrimaryKey() ? null : false;
556
    }
557
558
    /**
559
     * {@inheritdoc}
560
     */
561
    #[WriteOperation]
562 34
    public function save($entity): int
563
    {
564 34
        $isNew = $this->isNew($entity);
565
566 34
        if ($this->notify(Events::PRE_SAVE, [$entity, $this, $isNew]) === false) {
567
            return 0;
568
        }
569
570
        // composite primary
0 ignored issues
show
Coding Style Documentation introduced by
Inline comments must start with a capital letter
Loading history...
571 34
        if ($isNew === null) {
572 14
            $count = $this->replace($entity);
573 26
        } elseif ($isNew) {
0 ignored issues
show
Coding Style introduced by
Usage of ELSEIF not allowed; use ELSE IF instead
Loading history...
574 18
            $count = $this->insert($entity);
575
        } else {
576 11
            $count = $this->update($entity);
577
        }
578
579 32
        $this->notify(Events::POST_SAVE, [$entity, $this, $count, $isNew]);
580
        
581 32
        return $count;
582
    }
583
584
    /**
585
     * Replace an entity
586
     *
587
     * @param E $entity
588
     *
589
     * @return int  Returns 2 if updated and 1 if inserting
590
     * @throws PrimeException
591
     */
592
    #[WriteOperation]
593 52
    public function replace($entity): int
594
    {
595 52
        $isNew = $this->isNew($entity);
596
597 52
        if ($isNew !== true && $this->exists($entity)) {
598 10
            return $this->update($entity) + 1;
0 ignored issues
show
Coding Style introduced by
Operation must be bracketed
Loading history...
599
        }
600
601 44
        return $this->insert($entity);
602
    }
603
604
    /**
605
     * Duplicate an entity
606
     * remove primary key and launch insertion
607
     * 
608
     * @param E $entity
609
     * 
610
     * @return int
611
     * @throws PrimeException
612
     */
613
    #[WriteOperation]
614 2
    public function duplicate($entity): int
615
    {
616 2
        $this->mapper()->setId($entity, null);
617
        
618 2
        return $this->insert($entity);
619
    }
620
    
621
    /**
622
     * Insert an entity
623
     * 
624
     * @param E $entity
625
     * @param bool $ignore
626
     * 
627
     * @return int
628
     * @throws PrimeException
629
     */
630
    #[WriteOperation]
631 406
    public function insert($entity, bool $ignore = false): int
632
    {
633 406
        return $this->writer->insert($entity, ['ignore' => $ignore]);
634
    }
635
636
    /**
637
     * Insert ignore
638
     * 
639
     * @param E $entity
640
     * 
641
     * @return int
642
     * @throws PrimeException
643
     */
644
    #[WriteOperation]
645 2
    public function insertIgnore($entity): int
646
    {
647 2
        return $this->insert($entity, true);
648
    }
649
    
650
    /**
651
     * {@inheritdoc}
652
     */
653
    #[WriteOperation]
654 45
    public function update($entity, array $attributes = null): int
655
    {
656 45
        return $this->writer->update($entity, ['attributes' => $attributes]);
657
    }
658
659
    /**
660
     * Update collection of entities
661
     * 
662
     * @param array $attributes
663
     * @param array $criteria
664
     * 
665
     * @return int
666
     * @throws PrimeException
667
     */
668
    #[WriteOperation]
669
    public function updateBy(array $attributes, array $criteria = []): int
670
    {
671
        return $this->builder()->where($criteria)->update($attributes);
672
    }
673
    
674
    /**
675
     * {@inheritdoc}
676
     */
677
    #[WriteOperation]
678 26
    public function delete($entity): int
679
    {
680 26
        return $this->writer->delete($entity);
681
    }
682
    
683
    /**
684
     * Remove a collection of entities
685
     * 
686
     * @param array $criteria
687
     * 
688
     * @return int
689
     * @throws PrimeException
690
     */
691
    #[WriteOperation]
692
    public function deleteBy(array $criteria): int
693
    {
694
        return $this->builder()->where($criteria)->delete();
695
    }
696
697
    /**
0 ignored issues
show
Coding Style introduced by
Parameter $force should have a doc-comment as per coding-style.
Loading history...
698
     * {@inheritdoc}
699
     */
700 634
    public function schema(bool $force = false): ResolverInterface
701
    {
702 634
        if (!$this->mapper->hasSchemaManager() && !$force) {
703
            return new NullResolver();
704
        }
705
        
706 634
        return new Resolver($this->serviceLocator, $this->mapper->metadata());
707
    }
708
    
709
    /**
710
     * Gets custom filters
711
     *
712
     * @return array
713
     */
714
    public function filters()
715
    {
716
        return $this->mapper->filters();
717
    }
718
    
719
    /**
720
     * Repository extensions
721
     *
722
     * @return array
723
     */
724 1
    public function scopes()
725
    {
726 1
        return $this->mapper->scopes();
727
    }
728
    
729
    //----- events
0 ignored issues
show
Coding Style introduced by
No space found before comment text; expected "// ----- events" but found "//----- events"
Loading history...
730
731
    /**
0 ignored issues
show
Coding Style introduced by
Parameter $once should have a doc-comment as per coding-style.
Loading history...
Coding Style introduced by
Parameter $listener should have a doc-comment as per coding-style.
Loading history...
732
     * {@inheritdoc}
733
     */
734 1
    public function loaded(callable $listener, bool $once = false)
735
    {
736 1
        if ($once) {
737 1
            $this->once(Events::POST_LOAD, $listener);
738
        } else {
739
            $this->listen(Events::POST_LOAD, $listener);
740
        }
741
742 1
        return $this;
743
    }
744
745
    /**
0 ignored issues
show
Coding Style introduced by
Parameter $once should have a doc-comment as per coding-style.
Loading history...
Coding Style introduced by
Parameter $listener should have a doc-comment as per coding-style.
Loading history...
746
     * {@inheritdoc}
747
     */
748 1
    public function saving(callable $listener, bool $once = false)
749
    {
750 1
        if ($once) {
751 1
            $this->once(Events::PRE_SAVE, $listener);
752
        } else {
753
            $this->listen(Events::PRE_SAVE, $listener);
754
        }
755
756 1
        return $this;
757
    }
758
759
    /**
0 ignored issues
show
Coding Style introduced by
Parameter $listener should have a doc-comment as per coding-style.
Loading history...
Coding Style introduced by
Parameter $once should have a doc-comment as per coding-style.
Loading history...
760
     * {@inheritdoc}
761
     */
762 1
    public function saved(callable $listener, bool $once = false)
763
    {
764 1
        if ($once) {
765 1
            $this->once(Events::POST_SAVE, $listener);
766
        } else {
767
            $this->listen(Events::POST_SAVE, $listener);
768
        }
769
770 1
        return $this;
771
    }
772
773
    /**
0 ignored issues
show
Coding Style introduced by
Parameter $listener should have a doc-comment as per coding-style.
Loading history...
Coding Style introduced by
Parameter $once should have a doc-comment as per coding-style.
Loading history...
774
     * {@inheritdoc}
775
     */
776 5
    public function inserting(callable $listener, bool $once = false)
777
    {
778 5
        if ($once) {
779 1
            $this->once(Events::PRE_INSERT, $listener);
780
        } else {
781 4
            $this->listen(Events::PRE_INSERT, $listener);
782
        }
783
784 5
        return $this;
785
    }
786
787
    /**
0 ignored issues
show
Coding Style introduced by
Parameter $listener should have a doc-comment as per coding-style.
Loading history...
Coding Style introduced by
Parameter $once should have a doc-comment as per coding-style.
Loading history...
788
     * {@inheritdoc}
789
     */
790 2
    public function inserted(callable $listener, bool $once = false)
791
    {
792 2
        if ($once) {
793 1
            $this->once(Events::POST_INSERT, $listener);
794
        } else {
795 1
            $this->listen(Events::POST_INSERT, $listener);
796
        }
797
798 2
        return $this;
799
    }
800
801
    /**
0 ignored issues
show
Coding Style introduced by
Parameter $listener should have a doc-comment as per coding-style.
Loading history...
Coding Style introduced by
Parameter $once should have a doc-comment as per coding-style.
Loading history...
802
     * {@inheritdoc}
803
     */
804 6
    public function updating(callable $listener, bool $once = false)
805
    {
806 6
        if ($once) {
807 2
            $this->once(Events::PRE_UPDATE, $listener);
808
        } else {
809 4
            $this->listen(Events::PRE_UPDATE, $listener);
810
        }
811
812 6
        return $this;
813
    }
814
815
    /**
0 ignored issues
show
Coding Style introduced by
Parameter $once should have a doc-comment as per coding-style.
Loading history...
Coding Style introduced by
Parameter $listener should have a doc-comment as per coding-style.
Loading history...
816
     * {@inheritdoc}
817
     */
818 2
    public function updated(callable $listener, bool $once = false)
819
    {
820 2
        if ($once) {
821 1
            $this->once(Events::POST_UPDATE, $listener);
822
        } else {
823 1
            $this->listen(Events::POST_UPDATE, $listener);
824
        }
825
826 2
        return $this;
827
    }
828
829
    /**
0 ignored issues
show
Coding Style introduced by
Parameter $listener should have a doc-comment as per coding-style.
Loading history...
Coding Style introduced by
Parameter $once should have a doc-comment as per coding-style.
Loading history...
830
     * {@inheritdoc}
831
     */
832 6
    public function deleting(callable $listener, bool $once = false)
833
    {
834 6
        if ($once) {
835 1
            $this->once(Events::PRE_DELETE, $listener);
836
        } else {
837 5
            $this->listen(Events::PRE_DELETE, $listener);
838
        }
839
840 6
        return $this;
841
    }
842
843
    /**
0 ignored issues
show
Coding Style introduced by
Parameter $listener should have a doc-comment as per coding-style.
Loading history...
Coding Style introduced by
Parameter $once should have a doc-comment as per coding-style.
Loading history...
844
     * {@inheritdoc}
845
     */
846 2
    public function deleted(callable $listener, bool $once = false)
847
    {
848 2
        if ($once) {
849 1
            $this->once(Events::POST_DELETE, $listener);
850
        } else {
851 1
            $this->listen(Events::POST_DELETE, $listener);
852
        }
853
854 2
        return $this;
855
    }
856
857
    /**
858
     * Query method
859
     * redirect method to the query builder
860
     * 
861
     * @param string $name         Query builder method
862
     * @param array  $arguments
863
     * 
864
     * @return int|QueryInterface
0 ignored issues
show
Coding Style introduced by
Expected "integer|QueryInterface" but found "int|QueryInterface" for function return type
Loading history...
865
     */
866 108
    public function __call($name, $arguments)
0 ignored issues
show
introduced by
Type hint "array" missing for $arguments
Loading history...
867
    {
868 108
        return $this->queries->$name(...$arguments);
869
    }
870
    
871
    //--- Methodes for optimisation: alias of query methods
0 ignored issues
show
Coding Style introduced by
No space found before comment text; expected "// --- Methodes for optimisation: alias of query methods" but found "//--- Methodes for optimisation: alias of query methods"
Loading history...
872
    
873
    /**
874
     * @see QueryRepositoryExtension::with
875
     * 
876
     * @param string|array $relations
0 ignored issues
show
Coding Style introduced by
Parameter tags must be defined first in a doc comment
Loading history...
877
     * 
878
     * @return QueryInterface<ConnectionInterface, E>
879
     */
880 35
    public function with($relations)
881
    {
882 35
        return $this->builder()->with($relations);
0 ignored issues
show
Bug introduced by
The method with() does not exist on Bdf\Prime\Query\QueryInterface. It seems like you code against a sub-type of said class. However, the method does not exist in Bdf\Prime\Query\SqlQueryInterface. Are you sure you never get one of those? ( Ignorable by Annotation )

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

882
        return $this->builder()->/** @scrutinizer ignore-call */ with($relations);
Loading history...
883
    }
884
885
    /**
886
     * @see QueryRepositoryExtension::without
887
     *
888
     * @param string|array $relations
0 ignored issues
show
Coding Style introduced by
Parameter tags must be defined first in a doc comment
Loading history...
889
     *
890
     * @return QueryInterface<ConnectionInterface, E>
891
     */
892 11
    public function without($relations)
893
    {
894 11
        return $this->builder()->without($relations);
0 ignored issues
show
Bug introduced by
The method without() does not exist on Bdf\Prime\Query\QueryInterface. It seems like you code against a sub-type of said class. However, the method does not exist in Bdf\Prime\Query\SqlQueryInterface. Are you sure you never get one of those? ( Ignorable by Annotation )

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

894
        return $this->builder()->/** @scrutinizer ignore-call */ without($relations);
Loading history...
895
    }
896
    
897
    /**
898
     * @see QueryRepositoryExtension::by
899
     * 
900
     * @param string|array $attribute
0 ignored issues
show
Coding Style introduced by
Parameter tags must be defined first in a doc comment
Loading history...
901
     * @param boolean      $combine
0 ignored issues
show
introduced by
Expected "bool" but found "boolean" for parameter type
Loading history...
902
     * 
903
     * @return QueryInterface<ConnectionInterface, E>
904
     */
905 6
    public function by($attribute, $combine = false)
906
    {
907 6
        return $this->builder()->by($attribute, $combine);
0 ignored issues
show
Bug introduced by
The method by() does not exist on Bdf\Prime\Query\QueryInterface. It seems like you code against a sub-type of said class. However, the method does not exist in Bdf\Prime\Query\SqlQueryInterface. Are you sure you never get one of those? ( Ignorable by Annotation )

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

907
        return $this->builder()->/** @scrutinizer ignore-call */ by($attribute, $combine);
Loading history...
908
    }
909
    
910
    /**
911
     * @see QueryInterface::wrapAs
912
     * 
913
     * @param string $wrapperClass
0 ignored issues
show
Coding Style introduced by
Parameter tags must be defined first in a doc comment
Loading history...
914
     * 
915
     * @return QueryInterface<ConnectionInterface, E>
916
     */
917 7
    public function wrapAs($wrapperClass)
918
    {
919 7
        return $this->builder()->wrapAs($wrapperClass);
920
    }
921
    
922
    /**
923
     * @param array $criteria
924
     * @param array $attributes
925
     * 
926
     * @return array|CollectionInterface<E>
927
     * @throws PrimeException
928
     */
929
    #[ReadOperation]
930 5
    public function find(array $criteria, $attributes = null)
931
    {
932 5
        return $this->builder()->find($criteria, $attributes);
933
    }
934
935
    /**
936
     * @param array $criteria
937
     * @param array $attributes
938
     * 
939
     * @return E|null
940
     * @throws PrimeException
941
     */
942
    #[ReadOperation]
943 11
    public function findOne(array $criteria, $attributes = null)
944
    {
945 11
        return $this->builder()->findOne($criteria, $attributes);
946
    }
947
948
    /**
0 ignored issues
show
introduced by
Parameter $column is not described in comment
Loading history...
introduced by
Parameter $value is not described in comment
Loading history...
Coding Style introduced by
Parameter $value should have a doc-comment as per coding-style.
Loading history...
introduced by
Parameter $operator is not described in comment
Loading history...
Coding Style introduced by
Parameter $column should have a doc-comment as per coding-style.
Loading history...
Coding Style introduced by
Parameter $operator should have a doc-comment as per coding-style.
Loading history...
949
     * @see QueryInterface::where
950
     * 
951
     * @param string|array $relations
0 ignored issues
show
introduced by
Doc comment for parameter $relations does not match actual variable name $value
Loading history...
Coding Style introduced by
Doc comment for parameter $relations does not match actual variable name $column
Loading history...
Coding Style introduced by
Parameter tags must be defined first in a doc comment
Loading history...
952
     * 
953
     * @return QueryInterface<ConnectionInterface, E>
954
     */
955 49
    public function where($column, $operator = null, $value = null)
956
    {
957 49
        return $this->builder()->where($column, $operator, $value);
958
    }
959
960
    /**
961
     * {@inheritdoc}
962
     */
963 3
    public function onConnectionClosed()
964
    {
965 3
        $this->reset();
966 3
    }
967
968
    /**
969
     * {@inheritdoc}
970
     */
971 143
    public function getSubscribedEvents()
972
    {
973 143
        return [ConnectionClosedListenerInterface::EVENT_NAME];
974
    }
975
976
    /**
977
     * Free metadata information on the given entity
978
     * Note: This method is called by the model destructor
979
     *
980
     * @param E $entity
981
     *
982
     * @return void
0 ignored issues
show
introduced by
If there is no return value for a function, there must not be a @return tag.
Loading history...
983
     */
984 530
    public function free($entity)
0 ignored issues
show
introduced by
Type hint "E" missing for $entity
Loading history...
985
    {
986 530
        foreach ($this->relations as $relation) {
987 253
            $relation->clearInfo($entity);
988
        }
989 530
    }
990
991
    /**
992
     * Clear dependencies for break cyclic references
993
     * After this call, the repository will be unusable
994
     *
995
     * @internal
996
     */
997 1
    public function destroy()
998
    {
999 1
        $this->connection()->getEventManager()->removeEventSubscriber($this);
1000
1001 1
        $this->serviceLocator = null;
1002 1
        $this->queries = null;
1003 1
        $this->writer = null;
1004 1
        $this->relations = [];
1005 1
        $this->collectionFactory = null;
1006
1007 1
        $this->mapper->destroy();
1008 1
        $this->mapper = null;
1009
1010 1
        if ($this->resultCache) {
1011
            $this->resultCache->clear();
1012
            $this->resultCache = null;
1013
        }
1014 1
    }
1015
1016
    /**
1017
     * Change the active connection on the repository
1018
     * All queries will be reseted
1019
     *
1020
     * /!\ The method will not check if the connection exists nor the new connection is same as the active
0 ignored issues
show
introduced by
Doc comment long description must end with a full stop
Loading history...
1021
     *
1022
     * @param string $connectionName The new connection name
1023
     *
1024
     * @return string The last active connection name
1025
     */
1026 6
    private function changeActiveConnection($connectionName)
0 ignored issues
show
Coding Style introduced by
Private method name "EntityRepository::changeActiveConnection" must be prefixed with an underscore
Loading history...
1027
    {
1028 6
        $this->connection()->getEventManager()->removeEventSubscriber($this);
1029
1030
        /** @var string $original */
0 ignored issues
show
Coding Style introduced by
The close comment tag must be the only content on the line
Loading history...
Coding Style introduced by
The open comment tag must be the only content on the line
Loading history...
Coding Style introduced by
Block comments must be started with /*
Loading history...
Coding Style introduced by
Inline doc block comments are not allowed; use "/* Comment */" or "// Comment" instead
Loading history...
1031 6
        $original = $this->mapper->metadata()->connection;
1032 6
        $this->mapper->metadata()->connection = $connectionName;
1033
1034 6
        $this->connection()->getEventManager()->addEventSubscriber($this);
1035 6
        $this->reset();
1036
1037 6
        return $original;
1038
    }
1039
1040
    /**
1041
     * Reset the inner queries
1042
     * Use for invalidate prepared queries, or when connection changed
1043
     */
1044 8
    private function reset()
0 ignored issues
show
Coding Style introduced by
Private method name "EntityRepository::reset" must be prefixed with an underscore
Loading history...
1045
    {
1046
        // Reset queries
1047 8
        $this->queries = new RepositoryQueryFactory($this, $this->resultCache);
1048 8
        $this->writer = new Writer($this, $this->serviceLocator);
1049 8
        $this->relations = []; // Relation may contains inner query : it must be reseted
0 ignored issues
show
Coding Style introduced by
Comments may not appear after statements
Loading history...
1050
1051 8
        if ($this->resultCache) {
1052
            $this->resultCache->clear();
1053
        }
1054 8
    }
1055
}
1056