Storage::getState()   A
last analyzed

Complexity

Conditions 6
Paths 6

Size

Total Lines 22
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 11
CRAP Score 6.1308

Importance

Changes 0
Metric Value
cc 6
eloc 13
nc 6
nop 1
dl 0
loc 22
ccs 11
cts 13
cp 0.8462
crap 6.1308
rs 9.2222
c 0
b 0
f 0
1
<?php declare(strict_types=1);
2
3
namespace Igni\Storage;
4
5
use Igni\Storage\Driver\ConnectionManager;
6
use Igni\Storage\Exception\UnitOfWorkException;
7
use SplObjectStorage;
8
9
class Storage implements UnitOfWork
10
{
11
    private const STATE_NEW = 1;
12
    private const STATE_MANAGED = 2;
13
    private const STATE_REMOVED = 3;
14
    private const STATE_DETACHED = 4;
15
    private const ACTION_CREATE = 'create';
16
    private const ACTION_REMOVE = 'remove';
17
    private const ACTION_UPDATE = 'update';
18
19
    /**
20
     * Contains calculated states for entities.
21
     * @var array<string, int>
22
     */
23
    private $states = [];
24
25
    /**
26
     * Contains grouped by class name entities that should be saved.
27
     * @var SplObjectStorage[]
28
     */
29
    private $create = [];
30
31
    /**
32
     * Contains grouped by class name entities that should be removed.
33
     * @var SplObjectStorage[]
34
     */
35
    private $remove = [];
36
37
    /**
38
     * Contains grouped by class name entities that should be removed.
39
     * @var SplObjectStorage[]
40
     */
41
    private $update = [];
42
43
    /**
44
     * @var EntityManager
45
     */
46
    private $entityManager;
47
48 49
    public function __construct(EntityManager $manager = null)
49
    {
50 49
        $this->entityManager = $manager ?? new EntityManager();
51 49
    }
52
53 1
    public function getRepository(string $entity): Repository
54
    {
55 1
        return $this->entityManager->getRepository($entity);
56
    }
57
58 1
    public function hasRepository(string $entity): bool
59
    {
60 1
        return $this->entityManager->hasRepository($entity);
61
    }
62
63 1
    public function addRepository(Repository ...$repositories): void
64
    {
65 1
        $this->entityManager->addRepository(...$repositories);
66 1
    }
67
68 1
    public function getEntityManager(): EntityManager
69
    {
70 1
        return $this->entityManager;
71
    }
72
73 4
    public function get(string $entity, $id): Storable
74
    {
75 4
        $entity = $this->entityManager->get($entity, $id);
76 4
        $this->states[spl_object_hash($entity)] = self::STATE_MANAGED;
77
78 4
        return $entity;
79
    }
80
81
    /**
82
     * @param Storable[] ...$entities
83
     */
84 5
    public function persist(Storable ...$entities): void
85
    {
86 5
        foreach ($entities as $entity) {
87 5
            $this->persistOne($entity);
88
        }
89 4
    }
90
91
    /**
92
     * @param Storable[] ...$entities
93
     */
94 3
    public function remove(Storable ...$entities): void
95
    {
96 3
        foreach ($entities as $entity) {
97 3
            $this->removeOne($entity);
98
        }
99 3
    }
100
101 3
    public function commit(): void
102
    {
103 3
        $this->commitAction(self::ACTION_CREATE);
104 3
        $this->commitAction(self::ACTION_UPDATE);
105 3
        $this->commitAction(self::ACTION_REMOVE);
106 3
    }
107
108 3
    private function commitAction(string $action): void
109
    {
110 3
        foreach ($this->{$action} as $namespace => $entities) {
111 3
            foreach ($entities as $entity) {
112 3
                call_user_func([$this->entityManager->getRepository($namespace), $action], $entity);
113
            }
114
        }
115 3
    }
116
117 1
    public function rollback(): void
118
    {
119 1
        $this->create = [];
120 1
        $this->update = [];
121 1
        $this->remove = [];
122 1
    }
123
124 5
    private function persistOne(Storable $entity): void
125
    {
126 5
        $namespace = get_class($entity);
127
128 5
        switch ($this->getState($entity)) {
129 5
            case self::STATE_MANAGED:
130 2
                if (!isset($this->update[$namespace])) {
131 2
                    $this->update[$namespace] = new SplObjectStorage();
132
                }
133 2
                $this->update[$namespace]->attach($entity);
134 2
                break;
135 3
            case self::STATE_NEW:
136 2
                if (!isset($this->create[$namespace])) {
137 2
                    $this->create[$namespace] = new SplObjectStorage();
138
                }
139 2
                $this->create[$namespace]->attach($entity);
140 2
                break;
141 1
            case self::STATE_REMOVED:
142
            case self::STATE_DETACHED:
143
            default:
144 1
                throw UnitOfWorkException::forPersistingEntityInInvalidState($entity);
145
        }
146 4
    }
147
148 5
    public function getState(Storable $entity): int
149
    {
150 5
        $oid = spl_object_hash($entity);
151 5
        if (isset($this->states[$oid])) {
152 3
            return $this->states[$oid];
153
        }
154
155 3
        $namespace = get_class($entity);
156
157 3
        if (isset($this->remove[$namespace]) && $this->remove[$namespace]->contains($entity)) {
158 1
            return $this->states[$oid] = self::STATE_REMOVED;
159
        }
160
161 2
        if ($this->entityManager->contains($entity)) {
162
            return $this->states[$oid] = self::STATE_MANAGED;
163
        }
164
165
        try {
166 2
            $this->entityManager->getRepository($namespace)->get($entity->getId());
167
            return $this->states[$oid] = self::STATE_DETACHED;
168 2
        } catch (\Exception $exception) {
169 2
            return $this->states[$oid] = self::STATE_NEW;
170
        }
171
    }
172
173 3
    private function removeOne(Storable $entity): void
174
    {
175 3
        $namespace = get_class($entity);
176 3
        $oid = spl_object_hash($entity);
177
178 3
        if (isset($this->states[$oid]) && $this->states[$oid] === self::STATE_MANAGED) {
179 2
            $this->states[$oid] = self::STATE_REMOVED;
180
        }
181
182 3
        if (!isset($this->remove[$namespace])) {
183 3
            $this->remove[$namespace] = new SplObjectStorage();
184
        }
185
186 3
        if (isset($this->create[$namespace])) {
187
            $this->create[$namespace]->detach($entity);
188
        }
189
190 3
        if (isset($this->update[$namespace])) {
191 1
            $this->update[$namespace]->detach($entity);
192
        }
193
194 3
        $this->remove[$namespace]->attach($entity);
195 3
    }
196
197 1
    public function attach(Storable ...$entities): void
198
    {
199 1
        foreach ($entities as $entity) {
200 1
            $this->entityManager->attach($entity);
201
        }
202 1
    }
203
204 1
    public function contains(Storable $entity): bool
205
    {
206 1
        $contains = $this->entityManager->contains($entity);
207 1
        $oid = spl_object_hash($entity);
208 1
        $this->states[$oid] = self::STATE_MANAGED;
209
210 1
        return $contains;
211
    }
212
213 1
    public function detach(Storable ...$entities): void
214
    {
215 1
        foreach ($entities as $entity) {
216 1
            $this->detachOne($entity);
217
        }
218 1
    }
219
220 1
    private function detachOne(Storable $entity): void
221
    {
222 1
        $this->states[spl_object_hash($entity)] = self::STATE_DETACHED;
223 1
        $namespace = get_class($entity);
224
225 1
        if (isset($this->update[$namespace])) {
226
            $this->update[$namespace]->detach($entity);
227
        }
228
229 1
        if (isset($this->create[$namespace])) {
230
            $this->create[$namespace]->detach($entity);
231
        }
232
233 1
        if (isset($this->remove[$namespace])) {
234
            $this->remove[$namespace]->detach($entity);
235
        }
236
237 1
        $this->entityManager->detach($entity);
238 1
    }
239
}
240