GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Completed
Push — master ( d68f0e...658fac )
by Ross
14s queued 12s
created

TestEntityGenerator   A

Complexity

Total Complexity 38

Size/Duplication

Total Lines 342
Duplicated Lines 0 %

Test Coverage

Coverage 0%

Importance

Changes 0
Metric Value
eloc 119
dl 0
loc 342
ccs 0
cts 204
cp 0
rs 9.36
c 0
b 0
f 0
wmc 38

20 Methods

Rating   Name   Duplication   Size   Complexity  
A addAssociation() 0 19 4
A create() 0 13 3
A fakerUpdateDto() 0 3 1
A assertInArray() 0 4 2
A generateDto() 0 8 1
A assertSame() 0 4 2
A __construct() 0 14 1
A createEntityWithData() 0 5 1
A assertSameEntityManagerInstance() 0 6 2
B addAssociationEntities() 0 52 8
A generateEntity() 0 3 1
A getTestEntityGeneratorFactory() 0 3 1
A getDtoFactory() 0 3 1
A getFakerDataFiller() 0 3 1
A getEntityFactory() 0 3 1
A getEntityManager() 0 3 1
A generateDtoRelatedToEntity() 0 9 1
A generateEntities() 0 14 3
A createEntityRelatedToEntity() 0 7 1
A getGenerator() 0 9 2
1
<?php declare(strict_types=1);
2
3
namespace EdmondsCommerce\DoctrineStaticMeta\Entity\Testing\EntityGenerator;
4
5
use Doctrine\Common\Collections\Collection;
6
use Doctrine\ORM\EntityManagerInterface;
7
use EdmondsCommerce\DoctrineStaticMeta\CodeGeneration\NamespaceHelper;
8
use EdmondsCommerce\DoctrineStaticMeta\DoctrineStaticMeta;
9
use EdmondsCommerce\DoctrineStaticMeta\Entity\DataTransferObjects\DtoFactory;
10
use EdmondsCommerce\DoctrineStaticMeta\Entity\Factory\EntityFactoryInterface;
11
use EdmondsCommerce\DoctrineStaticMeta\Entity\Interfaces\DataTransferObjectInterface;
12
use EdmondsCommerce\DoctrineStaticMeta\Entity\Interfaces\EntityInterface;
13
14
/**
15
 * Class TestEntityGenerator
16
 *
17
 * This class handles utilising Faker to build up an Entity and then also possible build associated entities and handle
18
 * the association
19
 *
20
 * Unique columns are guaranteed to have a totally unique value in this particular process, but not between processes
21
 *
22
 * This Class provides you a few ways to generate test Entities, either in bulk or one at a time
23
 *ExcessiveClassComplexity
24
 *
25
 * @package EdmondsCommerce\DoctrineStaticMeta\Entity\Testing
26
 * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
27
 * @SuppressWarnings(PHPMD.ExcessiveClassComplexity)
28
 */
29
class TestEntityGenerator
30
{
31
    /**
32
     * @var EntityManagerInterface
33
     */
34
    protected $entityManager;
35
36
    /**
37
     * @var DoctrineStaticMeta
38
     */
39
    protected $testedEntityDsm;
40
41
    /**
42
     * @var EntityFactoryInterface
43
     */
44
    protected $entityFactory;
45
    /**
46
     * @var DtoFactory
47
     */
48
    private $dtoFactory;
49
    /**
50
     * @var TestEntityGeneratorFactory
51
     */
52
    private $testEntityGeneratorFactory;
53
    /**
54
     * @var FakerDataFillerInterface
55
     */
56
    private $fakerDataFiller;
57
58
59
    /**
60
     * TestEntityGenerator constructor.
61
     *
62
     * @param DoctrineStaticMeta          $testedEntityDsm
63
     * @param EntityFactoryInterface|null $entityFactory
64
     * @param DtoFactory                  $dtoFactory
65
     * @param TestEntityGeneratorFactory  $testEntityGeneratorFactory
66
     * @param FakerDataFillerInterface    $fakerDataFiller
67
     * @param EntityManagerInterface      $entityManager
68
     * @SuppressWarnings(PHPMD.StaticAccess)
69
     */
70
    public function __construct(
71
        DoctrineStaticMeta $testedEntityDsm,
72
        EntityFactoryInterface $entityFactory,
73
        DtoFactory $dtoFactory,
74
        TestEntityGeneratorFactory $testEntityGeneratorFactory,
75
        FakerDataFillerInterface $fakerDataFiller,
76
        EntityManagerInterface $entityManager
77
    ) {
78
        $this->testedEntityDsm            = $testedEntityDsm;
79
        $this->entityFactory              = $entityFactory;
80
        $this->dtoFactory                 = $dtoFactory;
81
        $this->testEntityGeneratorFactory = $testEntityGeneratorFactory;
82
        $this->fakerDataFiller            = $fakerDataFiller;
83
        $this->entityManager              = $entityManager;
84
    }
85
86
87
    public function assertSameEntityManagerInstance(EntityManagerInterface $entityManager): void
88
    {
89
        if ($entityManager === $this->entityManager) {
90
            return;
91
        }
92
        throw new \RuntimeException('EntityManager instance is not the same as the one loaded in this factory');
93
    }
94
95
    /**
96
     * Use the factory to generate a new Entity, possibly with values set as well
97
     *
98
     * @param array $values
99
     *
100
     * @return EntityInterface
101
     */
102
    public function create(array $values = []): EntityInterface
103
    {
104
        $dto = $this->dtoFactory->createEmptyDtoFromEntityFqn($this->testedEntityDsm->getReflectionClass()->getName());
105
        if ([] !== $values) {
106
            foreach ($values as $property => $value) {
107
                $setter = 'set' . $property;
108
                $dto->$setter($value);
109
            }
110
        }
111
112
        return $this->entityFactory->create(
113
            $this->testedEntityDsm->getReflectionClass()->getName(),
114
            $dto
115
        );
116
    }
117
118
    /**
119
     * Generate an Entity. Optionally provide an offset from the first entity
120
     *
121
     * @return EntityInterface
122
     * @throws \EdmondsCommerce\DoctrineStaticMeta\Exception\DoctrineStaticMetaException
123
     * @throws \ErrorException
124
     * @throws \ReflectionException
125
     * @SuppressWarnings(PHPMD.StaticAccess)
126
     */
127
    public function generateEntity(): EntityInterface
128
    {
129
        return $this->createEntityWithData();
130
    }
131
132
    private function createEntityWithData(): EntityInterface
133
    {
134
        $dto = $this->generateDto();
135
136
        return $this->entityFactory->create($this->testedEntityDsm->getReflectionClass()->getName(), $dto);
137
    }
138
139
    public function generateDto(): DataTransferObjectInterface
140
    {
141
        $dto = $this->dtoFactory->createEmptyDtoFromEntityFqn(
142
            $this->testedEntityDsm->getReflectionClass()->getName()
143
        );
144
        $this->fakerUpdateDto($dto);
145
146
        return $dto;
147
    }
148
149
    public function fakerUpdateDto(DataTransferObjectInterface $dto): void
150
    {
151
        $this->fakerDataFiller->updateDtoWithFakeData($dto);
152
    }
153
154
    /**
155
     * @param EntityInterface $generated
156
     *
157
     * @throws \ErrorException
158
     * @SuppressWarnings(PHPMD.ElseExpression)
159
     * @SuppressWarnings(PHPMD.CyclomaticComplexity)
160
     */
161
    public function addAssociationEntities(
162
        EntityInterface $generated
163
    ): void {
164
        $testedEntityReflection = $this->testedEntityDsm->getReflectionClass();
165
        $class                  = $testedEntityReflection->getName();
166
        $meta                   = $this->testedEntityDsm->getMetaData();
167
        $mappings               = $meta->getAssociationMappings();
168
        if (empty($mappings)) {
169
            return;
170
        }
171
        $namespaceHelper = new NamespaceHelper();
172
        $methods         = array_map('strtolower', get_class_methods($generated));
173
        foreach ($mappings as $mapping) {
174
            $mappingEntityFqn                     = $mapping['targetEntity'];
175
            $errorMessage                         = "Error adding association entity $mappingEntityFqn to $class: %s";
176
            $mappingEntityPluralInterface         =
177
                $namespaceHelper->getHasPluralInterfaceFqnForEntity($mappingEntityFqn);
178
            $mappingEntityPluralInterfaceRequired =
179
                str_replace('\\Has', '\\HasRequired', $mappingEntityPluralInterface);
180
            if ((\interface_exists($mappingEntityPluralInterface) &&
181
                 $testedEntityReflection->implementsInterface($mappingEntityPluralInterface))
182
                ||
183
                (\interface_exists($mappingEntityPluralInterfaceRequired)
184
                 && $testedEntityReflection->implementsInterface($mappingEntityPluralInterfaceRequired))
185
            ) {
186
                $this->assertSame(
187
                    $mappingEntityFqn::getDoctrineStaticMeta()->getPlural(),
188
                    $mapping['fieldName'],
189
                    sprintf($errorMessage, ' mapping should be plural')
190
                );
191
                $getter = 'get' . $mappingEntityFqn::getDoctrineStaticMeta()->getPlural();
192
                $method = 'add' . $mappingEntityFqn::getDoctrineStaticMeta()->getSingular();
193
            } else {
194
                $this->assertSame(
195
                    $mappingEntityFqn::getDoctrineStaticMeta()->getSingular(),
196
                    $mapping['fieldName'],
197
                    sprintf($errorMessage, ' mapping should be singular')
198
                );
199
                $getter = 'get' . $mappingEntityFqn::getDoctrineStaticMeta()->getSingular();
200
                $method = 'set' . $mappingEntityFqn::getDoctrineStaticMeta()->getSingular();
201
            }
202
            $this->assertInArray(
203
                strtolower($method),
204
                $methods,
205
                sprintf($errorMessage, $method . ' method is not defined')
206
            );
207
            try {
208
                $currentlySet = $generated->$getter();
209
            } catch (\TypeError $e) {
210
                $currentlySet = null;
211
            }
212
            $this->addAssociation($generated, $method, $mappingEntityFqn, $currentlySet);
213
        }
214
    }
215
216
    /**
217
     * Stub of PHPUnit Assertion method
218
     *
219
     * @param mixed  $expected
220
     * @param mixed  $actual
221
     * @param string $error
222
     *
223
     * @throws \ErrorException
224
     */
225
    protected function assertSame($expected, $actual, string $error): void
226
    {
227
        if ($expected !== $actual) {
228
            throw new \ErrorException($error);
229
        }
230
    }
231
232
    /**
233
     * Stub of PHPUnit Assertion method
234
     *
235
     * @param mixed  $needle
236
     * @param array  $haystack
237
     * @param string $error
238
     *
239
     * @throws \ErrorException
240
     */
241
    protected function assertInArray($needle, array $haystack, string $error): void
242
    {
243
        if (false === \in_array($needle, $haystack, true)) {
244
            throw new \ErrorException($error);
245
        }
246
    }
247
248
    private function addAssociation(
249
        EntityInterface $generated,
250
        string $setOrAddMethod,
251
        string $mappingEntityFqn,
252
        $currentlySet
253
    ): void {
254
        $testEntityGenerator = $this->testEntityGeneratorFactory
255
            ->createForEntityFqn($mappingEntityFqn);
256
        switch (true) {
257
            case $currentlySet === null:
258
            case $currentlySet === []:
259
            case $currentlySet instanceof Collection:
260
                $mappingEntity = $testEntityGenerator->createEntityRelatedToEntity($generated);
261
                break;
262
            default:
263
                return;
264
        }
265
        $generated->$setOrAddMethod($mappingEntity);
266
        $this->entityManager->persist($mappingEntity);
267
    }
268
269
    /**
270
     * @param EntityInterface $entity
271
     *
272
     * @return mixed
273
     * @SuppressWarnings(PHPMD.UnusedPrivateMethod - it is being used)
274
     */
275
    private function createEntityRelatedToEntity(EntityInterface $entity)
276
    {
277
        $dto = $this->generateDtoRelatedToEntity($entity);
278
279
        return $this->entityFactory->create(
280
            $this->testedEntityDsm->getReflectionClass()->getName(),
281
            $dto
282
        );
283
    }
284
285
    public function generateDtoRelatedToEntity(EntityInterface $entity): DataTransferObjectInterface
286
    {
287
        $dto = $this->dtoFactory->createDtoRelatedToEntityInstance(
288
            $entity,
289
            $this->testedEntityDsm->getReflectionClass()->getName()
290
        );
291
        $this->fakerDataFiller->updateDtoWithFakeData($dto);
292
293
        return $dto;
294
    }
295
296
    /**
297
     * Generate Entities.
298
     *
299
     * Optionally discard the first generated entities up to the value of offset
300
     *
301
     * @param int $num
302
     *
303
     * @return array|EntityInterface[]
304
     */
305
    public function generateEntities(
306
        int $num
307
    ): array {
308
        $entities  = [];
309
        $generator = $this->getGenerator($num);
310
        foreach ($generator as $entity) {
311
            $id = (string)$entity->getId();
312
            if (array_key_exists($id, $entities)) {
313
                throw new \RuntimeException('Entity with ID ' . $id . ' is already generated');
314
            }
315
            $entities[$id] = $entity;
316
        }
317
318
        return $entities;
319
    }
320
321
    public function getGenerator(int $numToGenerate = 100): \Generator
322
    {
323
        $entityFqn = $this->testedEntityDsm->getReflectionClass()->getName();
324
        $generated = 0;
325
        while ($generated < $numToGenerate) {
326
            $dto    = $this->generateDto();
327
            $entity = $this->entityFactory->setEntityManager($this->entityManager)->create($entityFqn, $dto);
328
            yield $entity;
329
            $generated++;
330
        }
331
    }
332
333
    /**
334
     * @return EntityFactoryInterface
335
     */
336
    public function getEntityFactory(): EntityFactoryInterface
337
    {
338
        return $this->entityFactory;
339
    }
340
341
    /**
342
     * @return DtoFactory
343
     */
344
    public function getDtoFactory(): DtoFactory
345
    {
346
        return $this->dtoFactory;
347
    }
348
349
    /**
350
     * @return FakerDataFillerInterface
351
     */
352
    public function getFakerDataFiller(): FakerDataFillerInterface
353
    {
354
        return $this->fakerDataFiller;
355
    }
356
357
    /**
358
     * @return EntityManagerInterface
359
     */
360
    public function getEntityManager(): EntityManagerInterface
361
    {
362
        return $this->entityManager;
363
    }
364
365
    /**
366
     * @return TestEntityGeneratorFactory
367
     */
368
    public function getTestEntityGeneratorFactory(): TestEntityGeneratorFactory
369
    {
370
        return $this->testEntityGeneratorFactory;
371
    }
372
}
373