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 (#214)
by joseph
16:19
created

TestEntityGenerator::generateDto()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 8
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

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