Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
90.00% covered (success)
90.00%
45 / 50
66.67% covered (warning)
66.67%
6 / 9
CRAP
0.00% covered (danger)
0.00%
0 / 1
InMemoryDatalayer
90.00% covered (success)
90.00%
45 / 50
66.67% covered (warning)
66.67%
6 / 9
20.40
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 restore
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 store
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 all
100.00% covered (success)
100.00%
10 / 10
100.00% covered (success)
100.00%
1 / 1
2
 persistNew
100.00% covered (success)
100.00%
12 / 12
100.00% covered (success)
100.00%
1 / 1
4
 persistExisting
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
3
 find
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
3
 removeExisting
87.50% covered (warning)
87.50%
7 / 8
0.00% covered (danger)
0.00%
0 / 1
3.02
 upsert
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
6
1<?php
2namespace Apie\Core\Datalayers\InMemory;
3
4use Apie\Core\BoundedContext\BoundedContextId;
5use Apie\Core\Datalayers\ApieDatalayer;
6use Apie\Core\Datalayers\Lists\EntityListInterface;
7use Apie\Core\Datalayers\Lists\InMemoryEntityList;
8use Apie\Core\Datalayers\Search\LazyLoadedListFilterer;
9use Apie\Core\Entities\EntityInterface;
10use Apie\Core\Exceptions\EntityAlreadyPersisted;
11use Apie\Core\Exceptions\EntityNotFoundException;
12use Apie\Core\Exceptions\UnknownExistingEntityError;
13use Apie\Core\Identifiers\AutoIncrementInteger;
14use Apie\Core\Identifiers\IdentifierInterface;
15use Faker\Factory;
16use Faker\Generator;
17use ReflectionClass;
18use ReflectionProperty;
19
20class InMemoryDatalayer implements ApieDatalayer
21{
22    /**
23     * @var array<string, array<int, EntityInterface>>
24     */
25    private array $stored = [];
26
27    /**
28     * @var array<class-string<EntityInterface>, EntityListInterface<EntityInterface>>
29     */
30    private array $alreadyLoadedLists = [];
31
32    private Generator $generator;
33
34    public function __construct(private BoundedContextId $boundedContextId, private LazyLoadedListFilterer $filterer)
35    {
36        $this->generator = Factory::create();
37        $this->stored = $this->restore();
38    }
39
40    /**
41     * @return array<string, array<int, EntityInterface>>
42     */
43    protected function restore(): array
44    {
45        return [];
46    }
47
48    /**
49     * @param array<string, array<int, EntityInterface>> $list
50     */
51    protected function store(array $list): void
52    {
53    }
54
55    public function all(ReflectionClass $class, ?BoundedContextId $boundedContextId = null): EntityListInterface
56    {
57        $className = $class->name;
58        $this->stored[$className] ??= [];
59        if (!isset($this->alreadyLoadedLists[$className])) {
60            $this->alreadyLoadedLists[$className] = new InMemoryEntityList(
61                $class,
62                $this->boundedContextId,
63                $this->filterer,
64                $this->stored[$className]
65            );
66        }
67        return $this->alreadyLoadedLists[$className];
68    }
69
70    /**
71     * @template T of EntityInterface
72     * @param T $entity
73     * @return T
74     */
75    public function persistNew(EntityInterface $entity, ?BoundedContextId $boundedContextId = null): EntityInterface
76    {
77        $id = $entity->getId();
78        if ($id instanceof AutoIncrementInteger) {
79            $id = $id::createRandom($this->generator);
80            $reflProperty = new ReflectionProperty($entity, 'id');
81            $reflProperty->setValue($entity, $id);
82        }
83        $className = $id::getReferenceFor()->name;
84        $id = $entity->getId()->toNative();
85        foreach ($this->stored[$className] ?? [] as $entityInList) {
86            if ($entityInList->getId()->toNative() === $id) {
87                throw new EntityAlreadyPersisted($entity);
88            }
89        }
90        $this->stored[$className][] = $entity;
91        return $entity;
92    }
93
94    /**
95     * @template T of EntityInterface
96     * @param T $entity
97     * @return T
98     */
99    public function persistExisting(EntityInterface $entity, ?BoundedContextId $boundedContextId = null): EntityInterface
100    {
101        $id = $entity->getId()->toNative();
102        $className = $entity->getId()::getReferenceFor()->name;
103        foreach ($this->stored[$className] ?? [] as $key => $entityInList) {
104            if ($entityInList->getId()->toNative() === $id) {
105                $this->stored[$className][$key] = $entity;
106                return $entity;
107            }
108        }
109        throw new UnknownExistingEntityError($entity);
110    }
111
112    public function find(IdentifierInterface $identifier, ?BoundedContextId $boundedContextId = null): EntityInterface
113    {
114        $className = $identifier::getReferenceFor()->name;
115        $id = $identifier->toNative();
116        foreach ($this->stored[$className] ?? [] as $entityInList) {
117            if ($entityInList->getId()->toNative() === $id) {
118                return $entityInList;
119            }
120        }
121        throw new EntityNotFoundException($identifier);
122    }
123
124    public function removeExisting(EntityInterface $entity, ?BoundedContextId $boundedContextId = null): void
125    {
126        $identifier = $entity->getId();
127        $className = $identifier::getReferenceFor()->name;
128        $id = $identifier->toNative();
129        $newList = [];
130        foreach ($this->stored[$className] ?? [] as $entityInList) {
131            if ($entityInList->getId()->toNative() !== $id) {
132                $newList[] = $entityInList;
133            }
134        }
135        $this->stored[$className] = $newList;
136    }
137
138    public function upsert(EntityInterface $entity, ?BoundedContextId $boundedContextId): EntityInterface
139    {
140        try {
141            return $this->persistExisting($entity, $boundedContextId);
142        } catch (UnknownExistingEntityError) {
143            return $this->persistNew($entity, $boundedContextId);
144        }
145    }
146}