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
Pull Request — master (#154)
by Ross
22:04
created

assertSameEntityManagerInstance()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 0
Metric Value
cc 2
eloc 3
nc 2
nop 1
dl 0
loc 6
ccs 0
cts 6
cp 0
crap 6
rs 10
c 0
b 0
f 0
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 FakerDataFiller
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 FakerDataFiller             $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
        FakerDataFiller $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
        $factory = $this->testEntityGeneratorFactory
255
            ->createForEntityFqn($mappingEntityFqn);
256
        switch (true) {
257
            case $currentlySet === null:
258
            case $currentlySet === []:
259
            case $currentlySet instanceof Collection:
260
                $mappingEntity = $factory->createEntityRelatedToEntity($generated);
261
                break;
262
            default:
263
                return;
264
        }
265
        $generated->$setOrAddMethod($mappingEntity);
266
        $this->entityManager->persist($mappingEntity);
267
    }
268
269
    private function createEntityRelatedToEntity(EntityInterface $entity)
270
    {
271
        $dto = $this->generateDtoRelatedToEntity($entity);
272
273
        return $this->entityFactory->create(
274
            $this->testedEntityDsm->getReflectionClass()->getName(),
275
            $dto
276
        );
277
    }
278
279
    public function generateDtoRelatedToEntity(EntityInterface $entity): DataTransferObjectInterface
280
    {
281
        $dto = $this->dtoFactory->createDtoRelatedToEntityInstance(
282
            $entity,
283
            $this->testedEntityDsm->getReflectionClass()->getName()
284
        );
285
        $this->fakerDataFiller->updateDtoWithFakeData($dto);
286
287
        return $dto;
288
    }
289
290
    /**
291
     * Generate Entities.
292
     *
293
     * Optionally discard the first generated entities up to the value of offset
294
     *
295
     * @param int $num
296
     *
297
     * @return array|EntityInterface[]
298
     */
299
    public function generateEntities(
300
        int $num
301
    ): array {
302
        $entities  = [];
303
        $generator = $this->getGenerator($num);
304
        foreach ($generator as $entity) {
305
            $id = (string)$entity->getId();
306
            if (array_key_exists($id, $entities)) {
307
                throw new \RuntimeException('Entity with ID ' . $id . ' is already generated');
308
            }
309
            $entities[$id] = $entity;
310
        }
311
312
        return $entities;
313
    }
314
315
    public function getGenerator(int $numToGenerate = 100): \Generator
316
    {
317
        $entityFqn = $this->testedEntityDsm->getReflectionClass()->getName();
318
        $generated = 0;
319
        while ($generated < $numToGenerate) {
320
            $dto    = $this->generateDto();
321
            $entity = $this->entityFactory->setEntityManager($this->entityManager)->create($entityFqn, $dto);
322
            yield $entity;
323
            $generated++;
324
        }
325
    }
326
327
    /**
328
     * @return EntityFactoryInterface
329
     */
330
    public function getEntityFactory(): EntityFactoryInterface
331
    {
332
        return $this->entityFactory;
333
    }
334
335
    /**
336
     * @return DtoFactory
337
     */
338
    public function getDtoFactory(): DtoFactory
339
    {
340
        return $this->dtoFactory;
341
    }
342
343
    /**
344
     * @return FakerDataFiller
345
     */
346
    public function getFakerDataFiller(): FakerDataFiller
347
    {
348
        return $this->fakerDataFiller;
349
    }
350
351
    /**
352
     * @return EntityManagerInterface
353
     */
354
    public function getEntityManager(): EntityManagerInterface
355
    {
356
        return $this->entityManager;
357
    }
358
359
    /**
360
     * @return TestEntityGeneratorFactory
361
     */
362
    public function getTestEntityGeneratorFactory(): TestEntityGeneratorFactory
363
    {
364
        return $this->testEntityGeneratorFactory;
365
    }
366
}
367