Issues (590)

src/Test/TestPack.php (5 issues)

1
<?php
2
3
namespace Bdf\Prime\Test;
4
5
use Bdf\Event\EventNotifier;
6
use Bdf\Prime\Connection\ConnectionInterface;
7
use Bdf\Prime\Connection\SimpleConnection;
8
use Bdf\Prime\Prime;
9
use Bdf\Prime\Repository\EntityRepository;
10
11
/**
12
 * TestPack
13
 *
14
 * @author  Seb
15
 * @author  Gilles Gautier
16
 * @author  Virginie
17
 */
18
class TestPack
19
{
20
    use EventNotifier;
21
22
    /**
23
     * Singleton
24
     *
25
     * @var static|null
26
     */
27
    protected static $pack;
28
29
    /**
30
     * Test data by group
31
     *
32
     * @var array{persistent:object[], non-persistent: object[]}
33
     */
34
    protected $testPacks = [
35
        'persistent'        => [],  // Entités créées non modifiables
36
        'non-persistent'    => []   // Entités créées et détruites aprés un test
37
    ];
38
39
    /**
40
     * @var bool
41
     */
42
    private $initialized = false;
43
44
    /**
45
     * @var class-string[]
0 ignored issues
show
Documentation Bug introduced by
The doc comment class-string[] at position 0 could not be parsed: Unknown type name 'class-string' at position 0 in class-string[].
Loading history...
46
     */
47
    private $entityClasses = [];
48
49
    /**
50
     * @var array<string, object>
51
     */
52
    private $entities = [];
53
54
55
    /**
56
     * Singleton
57
     *
58
     * @return self
59
     */
60 1435
    public static function pack()
61
    {
62 1435
        if (static::$pack === null) {
63 1
            static::$pack = new static();
64
        }
65
66 1435
        return static::$pack;
67
    }
68
69
    /**
70
     * Check whether the pack is initialized
71
     *
72
     * @return bool Return true if testPack is initialized
73
     */
74 39
    public function isInitialized()
75
    {
76 39
        return $this->initialized;
77
    }
78
79
    /**
80
     * Declare persistent data
81
     *
82
     * @param object|array $entities
83
     *
84
     * @return $this
85
     */
86 499
    public function persist($entities)
87
    {
88 499
        if ($this->initialized) {
89 2
            return $this;
90
        }
91
92 498
        return $this->storeEntities($entities, 'persistent');
93
    }
94
95
    /**
96
     * Set non persitent test pack entities
97
     *
98
     * Entities could be:
99
     *  - an entity object
100
     *  - a collection of entities
101
     *  - a collection of entities with alias as key
102
     *
103
     * @param object|array $entities
104
     *
105
     * @return $this
106
     * @psalm-assert true $this->initialized
107
     */
108 116
    public function nonPersist($entities)
109
    {
110 116
        if (!$this->initialized) {
111 1
            throw new \LogicException('Non persistent data cannot be declared before initialization');
112
        }
113
114 115
        return $this->storeEntities($entities, 'non-persistent');
115
    }
116
117
    /**
118
     * Store entities
119
     *
120
     * @param object|object[] $entities
121
     *
122
     * @return $this
123
     */
124 557
    protected function storeEntities($entities, string $mode)
125
    {
126 557
        if (!is_array($entities)) {
127 35
            $entities = [$entities];
128
        }
129
130 557
        foreach ($entities as $alias => $entity) {
131 557
            $this->declareEntity(get_class($entity));
132
133 557
            if (is_string($alias)) {
134 452
                $this->testPacks[$mode][$alias] = $entity;
135 452
                $this->entities[$alias] = $entity;
136
            } else {
137 168
                $this->testPacks[$mode][] = $entity;
138
            }
139
140 557
            if ($this->initialized) {
141 115
                $this->pushEntity($entity);
142
            }
143
        }
144
145 557
        return $this;
146
    }
147
148
    /**
149
     * Declare a entity class for schema managing
150
     *
151
     * @param class-string|list<class-string> $entityClasses
0 ignored issues
show
Documentation Bug introduced by
The doc comment class-string|list<class-string> at position 0 could not be parsed: Unknown type name 'class-string' at position 0 in class-string|list<class-string>.
Loading history...
152
     *
153
     * @return self
154
     */
155 874
    public function declareEntity($entityClasses)
156
    {
157 874
        if (!is_array($entityClasses)) {
158 616
            $entityClasses = [$entityClasses];
159
        }
160
161
        // On ajoute la classe de l'entité uniquement si elle n'est pas déjà dans le tableau et si le test pack n'a pas démarré de savepoint
162 874
        foreach ($entityClasses as $entityClassName) {
163 874
            if (! in_array($entityClassName, $this->entityClasses)) {
164 853
                if ($this->initialized) {
165 96
                    $this->create([$entityClassName]);
166
                } else {
167 807
                    $this->entityClasses[] = $entityClassName;
168
                }
169
            }
170
        }
171
172 874
        return $this;
173
    }
174
175
    /**
176
     * Initialize schema and data
177
     *
178
     * @return self
179
     * @psalm-assert true $this->initialized
180
     */
181 1437
    public function initialize()
182
    {
183 1437
        if ($this->initialized === false) {
184 1126
            $this->create($this->entityClasses);
185
186 1126
            foreach ($this->testPacks['persistent'] as $entity) {
187 497
                $this->pushEntity($entity);
188
            }
189
190 1126
            $this->initialized = true;
191
192 1126
            $this->notify('testpack.initialized');
193
        }
194
195 1437
        $this->createSavePoint();
196
197 1437
        return $this;
198
    }
199
200
    /**
201
     * Create schema
202
     *
203
     * @param list<class-string> $entityClasses
0 ignored issues
show
The type Bdf\Prime\Test\list was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
204
     *
205
     * @return $this
206
     */
207 1147
    protected function create(array $entityClasses)
208
    {
209 1147
        Prime::create($entityClasses, true);
210
211 1147
        return $this;
212
    }
213
214
    /**
215
     * Register callback on initialized event
216
     *
217
     * @param callable $callback
218
     *
219
     * @return $this
220
     */
221
    public function onInit(callable $callback)
222
    {
223
        if (! $this->initialized) {
224
            $this->once('testpack.initialized', $callback);
225
        }
226
227
        return $this;
228
    }
229
230
    /**
231
     * Drop schema and reset test pack
232
     *
233
     * @return $this
234
     */
235 1132
    public function destroy()
236
    {
237 1132
        $this->rollbackToSavePoint();
238
239 1132
        Prime::drop($this->entityClasses, true);
240
241 1132
        $this->initialized = false;
242 1132
        $this->entities = [];
243 1132
        $this->entityClasses = [];
244 1132
        $this->testPacks = [
245 1132
            'persistent'        => [],
246 1132
            'non-persistent'    => [],
247 1132
        ];
248
249 1132
        $this->notify('testpack.destroyed');
250
251 1132
        return $this;
252
    }
253
254
    /**
255
     * Register callback on destroy event
256
     *
257
     * @param callable $callback
258
     *
259
     * @return $this
260
     */
261
    public function onDestroy(callable $callback)
262
    {
263
        $this->once('testpack.destroyed', $callback);
264
265
        return $this;
266
    }
267
268
    /**
269
     * Clear non-persistent user definition
270
     *
271
     * @return $this
272
     */
273 64
    public function clear()
274
    {
275 64
        foreach ($this->testPacks['non-persistent'] as $alias => $entity) {
276 23
            unset($this->entities[$alias]);
277
        }
278
279 64
        $this->testPacks['non-persistent'] = [];
280
281 64
        $this->rollbackToSavePoint();
282
283 64
        return $this;
284
    }
285
286
    /**
287
     * Get an entity by alias from test pack
288
     *
289
     * @param string $name        The entity alias name
290
     *
291
     * @return object|null
292
     */
293 265
    public function get($name)
294
    {
295 265
        if (isset($this->entities[$name])) {
296 264
            return $this->entities[$name];
297
        }
298
299 1
        return null;
300
    }
301
302
    /**
303
     * Push entity to repository
304
     *
305
     * @param object $entity
306
     *
307
     * @return void
308
     */
309 556
    public function pushEntity($entity): void
310
    {
311
        /** @var EntityRepository $repository */
312 556
        $repository = Prime::repository($entity);
313 556
        $mapper = $repository->mapper();
314
315 556
        $isReadOnly = $mapper->isReadOnly();
316 556
        $mapper->setReadOnly(false);
317
318 556
        $repository->disableEventNotifier();
319 556
        $repository->insert($entity);
320 556
        $repository->enableEventNotifier();
321
322 556
        $mapper->setReadOnly($isReadOnly);
323
    }
324
325
    /**
326
     * Delete entity from repository
327
     *
328
     * @param object $entity
329
     *
330
     * @return void
331
     */
332
    public function deleteEntity($entity): void
333
    {
334
        /** @var EntityRepository $repository */
335
        $repository = Prime::repository($entity);
336
        $mapper = $repository->mapper();
337
338
        $isReadOnly = $mapper->isReadOnly();
339
        $mapper->setReadOnly(false);
340
341
        $repository->disableEventNotifier();
342
        $repository->delete($entity);
343
        $repository->enableEventNotifier();
344
345
        $mapper->setReadOnly($isReadOnly);
346
    }
347
348
    /**
349
     * Create a save point on each active connections
350
     *
351
     * @return void
352
     */
353 1437
    protected function createSavePoint(): void
354
    {
355 1437
        foreach ($this->getActiveConnections() as $connection) {
356
            try {
357 1327
                $connection->setNestTransactionsWithSavepoints(true);
358 1099
                $connection->beginTransaction();
359 236
            } catch (\Exception $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
360
            }
361
        }
362
    }
363
364
    /**
365
     * Remove a save point on each active connections
366
     *
367
     * @return void
368
     */
369 1193
    protected function rollbackToSavePoint(): void
370
    {
371 1193
        foreach ($this->getActiveConnections() as $connection) {
372
            try {
373 1131
                while ($connection->isTransactionActive()) {
374 1075
                    $connection->rollBack();
375
                }
376
            } catch (\Exception $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
377
            }
378
        }
379
    }
380
381
    /**
382
     * Get Prime connections
383
     *
384
     * @return SimpleConnection[]
385
     */
386 1444
    private function getActiveConnections()
387
    {
388
        /** @var SimpleConnection[] */
389 1444
        return Prime::service()->connections()->connections();
390
    }
391
}
392