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 (#152)
by joseph
17:58
created

TestEntityGenerator::fillColumns()   A

Complexity

Conditions 5
Paths 6

Size

Total Lines 17
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 9.9614

Importance

Changes 0
Metric Value
eloc 12
dl 0
loc 17
rs 9.5555
c 0
b 0
f 0
ccs 5
cts 12
cp 0.4167
cc 5
nc 6
nop 3
crap 9.9614
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
33
    /**
34
     * @var EntityManagerInterface
35
     */
36
    protected $entityManager;
37
38
39
    /**
40
     * @var DoctrineStaticMeta
41
     */
42
    protected $testedEntityDsm;
43
44
    /**
45
     * @var EntityFactoryInterface
46
     */
47
    protected $entityFactory;
48
    /**
49
     * @var DtoFactory
50
     */
51
    private $dtoFactory;
52
    /**
53
     * @var TestEntityGeneratorFactory
54
     */
55
    private $testEntityGeneratorFactory;
56
    /**
57
     * @var FakerDataFiller
58
     */
59
    private $fakerDataFiller;
60
61
62
    /**
63
     * TestEntityGenerator constructor.
64
     *
65
     * @param DoctrineStaticMeta          $testedEntityDsm
66
     * @param EntityFactoryInterface|null $entityFactory
67
     * @param DtoFactory                  $dtoFactory
68
     * @param TestEntityGeneratorFactory  $testEntityGeneratorFactory
69
     * @param FakerDataFiller             $fakerDataFiller
70
     * @SuppressWarnings(PHPMD.StaticAccess)
71
     */
72
    public function __construct(
73
        DoctrineStaticMeta $testedEntityDsm,
74
        EntityFactoryInterface $entityFactory,
75
        DtoFactory $dtoFactory,
76
        TestEntityGeneratorFactory $testEntityGeneratorFactory,
77
        FakerDataFiller $fakerDataFiller,
78
        EntityManagerInterface $entityManager
79
    ) {
80
        $this->testedEntityDsm            = $testedEntityDsm;
81
        $this->entityFactory              = $entityFactory;
82
        $this->dtoFactory                 = $dtoFactory;
83
        $this->testEntityGeneratorFactory = $testEntityGeneratorFactory;
84
        $this->fakerDataFiller            = $fakerDataFiller;
85
        $this->entityManager              = $entityManager;
86
    }
87
88
89
    public function assertSameEntityManagerInstance(EntityManagerInterface $entityManager): void
90
    {
91
        if ($entityManager === $this->entityManager) {
92
            return;
93
        }
94
        throw new \RuntimeException('EntityManager instance is not the same as the one loaded in this factory');
95
    }
96
97
    /**
98
     * Use the factory to generate a new Entity, possibly with values set as well
99
     *
100
     * @param array $values
101
     *
102
     * @return EntityInterface
103
     */
104
    public function create(array $values = []): EntityInterface
105
    {
106
        $dto = $this->dtoFactory->createEmptyDtoFromEntityFqn($this->testedEntityDsm->getReflectionClass()->getName());
107
        if ([] !== $values) {
108
            foreach ($values as $property => $value) {
109
                $setter = 'set' . $property;
110
                $dto->$setter($value);
111
            }
112
        }
113
114
        return $this->entityFactory->create(
115
            $this->testedEntityDsm->getReflectionClass()->getName(),
116
            $dto
117
        );
118
    }
119
120
    /**
121
     * Generate an Entity. Optionally provide an offset from the first entity
122
     *
123
     * @return EntityInterface
124
     * @throws \EdmondsCommerce\DoctrineStaticMeta\Exception\DoctrineStaticMetaException
125
     * @throws \ErrorException
126
     * @throws \ReflectionException
127
     * @SuppressWarnings(PHPMD.StaticAccess)
128
     */
129
    public function generateEntity(): EntityInterface
130
    {
131
        return $this->createEntityWithData();
132
    }
133
134
    private function createEntityWithData(): EntityInterface
135
    {
136
        $dto = $this->generateDto();
137
138
        return $this->entityFactory->create($this->testedEntityDsm->getReflectionClass()->getName(), $dto);
139
    }
140
141
    public function generateDto(): DataTransferObjectInterface
142
    {
143
        $dto = $this->dtoFactory->createEmptyDtoFromEntityFqn(
144
            $this->testedEntityDsm->getReflectionClass()->getName()
145
        );
146
        $this->fakerUpdateDto($dto);
147
148
        return $dto;
149
    }
150
151
    public function fakerUpdateDto(DataTransferObjectInterface $dto): void
152
    {
153
        $this->fakerDataFiller->fillDtoFieldsWithData($dto);
154
    }
155
156
    /**
157
     * @param EntityInterface $generated
158
     *
159
     * @throws \ErrorException
160
     * @SuppressWarnings(PHPMD.ElseExpression)
161
     * @SuppressWarnings(PHPMD.CyclomaticComplexity)
162
     */
163
    public function addAssociationEntities(
164
        EntityInterface $generated
165
    ): void {
166
        $testedEntityReflection = $this->testedEntityDsm->getReflectionClass();
167
        $class                  = $testedEntityReflection->getName();
168
        $meta                   = $this->testedEntityDsm->getMetaData();
169
        $mappings               = $meta->getAssociationMappings();
170
        if (empty($mappings)) {
171
            return;
172
        }
173
        $namespaceHelper = new NamespaceHelper();
174
        $methods         = array_map('strtolower', get_class_methods($generated));
175
        foreach ($mappings as $mapping) {
176
            $mappingEntityFqn                     = $mapping['targetEntity'];
177
            $errorMessage                         = "Error adding association entity $mappingEntityFqn to $class: %s";
178
            $mappingEntityPluralInterface         =
179
                $namespaceHelper->getHasPluralInterfaceFqnForEntity($mappingEntityFqn);
180
            $mappingEntityPluralInterfaceRequired =
181
                str_replace('\\Has', '\\HasRequired', $mappingEntityPluralInterface);
182
            if ((\interface_exists($mappingEntityPluralInterface) &&
183
                 $testedEntityReflection->implementsInterface($mappingEntityPluralInterface))
184
                ||
185
                (\interface_exists($mappingEntityPluralInterfaceRequired)
186
                 && $testedEntityReflection->implementsInterface($mappingEntityPluralInterfaceRequired))
187
            ) {
188
                $this->assertSame(
189
                    $mappingEntityFqn::getDoctrineStaticMeta()->getPlural(),
190
                    $mapping['fieldName'],
191
                    sprintf($errorMessage, ' mapping should be plural')
192
                );
193
                $getter = 'get' . $mappingEntityFqn::getDoctrineStaticMeta()->getPlural();
194
                $method = 'add' . $mappingEntityFqn::getDoctrineStaticMeta()->getSingular();
195
            } else {
196
                $this->assertSame(
197
                    $mappingEntityFqn::getDoctrineStaticMeta()->getSingular(),
198
                    $mapping['fieldName'],
199
                    sprintf($errorMessage, ' mapping should be singular')
200
                );
201
                $getter = 'get' . $mappingEntityFqn::getDoctrineStaticMeta()->getSingular();
202
                $method = 'set' . $mappingEntityFqn::getDoctrineStaticMeta()->getSingular();
203
            }
204
            $this->assertInArray(
205
                strtolower($method),
206
                $methods,
207
                sprintf($errorMessage, $method . ' method is not defined')
208
            );
209
            try {
210
                $currentlySet = $generated->$getter();
211
            } catch (\TypeError $e) {
212
                $currentlySet = null;
213
            }
214
            switch (true) {
215
                case $currentlySet === null:
216
                case $currentlySet === []:
217
                case $currentlySet instanceof Collection:
218
                    $mappingEntity = $this->testEntityGeneratorFactory
219
                        ->createForEntityFqn($mappingEntityFqn)
220
                        ->createEntityRelatedToEntity($generated);
221
                    $generated->$method($mappingEntity);
222
                    $this->entityManager->persist($mappingEntity);
223
                    break;
224
            }
225
        }
226
    }
227
228
    /**
229
     * Stub of PHPUnit Assertion method
230
     *
231
     * @param mixed  $expected
232
     * @param mixed  $actual
233
     * @param string $error
234
     *
235
     * @throws \ErrorException
236
     */
237
    protected function assertSame($expected, $actual, string $error): void
238
    {
239
        if ($expected !== $actual) {
240
            throw new \ErrorException($error);
241
        }
242
    }
243
244
    /**
245
     * Stub of PHPUnit Assertion method
246
     *
247
     * @param mixed  $needle
248
     * @param array  $haystack
249
     * @param string $error
250
     *
251
     * @throws \ErrorException
252
     */
253
    protected function assertInArray($needle, array $haystack, string $error): void
254
    {
255
        if (false === \in_array($needle, $haystack, true)) {
256
            throw new \ErrorException($error);
257
        }
258
    }
259
260
    private function createEntityRelatedToEntity(EntityInterface $entity)
261
    {
262
        $dto = $this->generateDtoRelatedToEntity($entity);
263
264
        return $this->entityFactory->create(
265
            $this->testedEntityDsm->getReflectionClass()->getName(),
266
            $dto
267
        );
268
    }
269
270
    public function generateDtoRelatedToEntity(EntityInterface $entity): DataTransferObjectInterface
271
    {
272
        $dto = $this->dtoFactory->createDtoRelatedToEntityInstance(
273
            $entity,
274
            $this->testedEntityDsm->getReflectionClass()->getName()
275
        );
276
        $this->fakerDataFiller->fillDtoFieldsWithData($dto);
277
278
        return $dto;
279
    }
280
281
    /**
282
     * Generate Entities.
283
     *
284
     * Optionally discard the first generated entities up to the value of offset
285
     *
286
     * @param int $num
287
     *
288
     * @param int $offset
289
     *
290
     * @return array|EntityInterface[]
291
     * @throws \Doctrine\ORM\Mapping\MappingException
292
     * @throws \ReflectionException
293
     */
294
    public function generateEntities(
295
        int $num,
296
        int $offset = 0
297
    ): array {
298
299
        return $this->generateUnsavedEntities($num, $offset);
300
    }
301
302
    /**
303
     * Generate Entities but do not save them
304
     *
305
     * @param int $num
306
     * @param int $offset
307
     *
308
     * @return array
309
     */
310
    public function generateUnsavedEntities(
311
        int $num,
312
        int $offset = 0
313
    ): array {
314
        $entities  = [];
315
        $generator = $this->getGenerator();
316
        $count     = 0;
317
        foreach ($generator as $entity) {
318
            $count++;
319
            if ($count + $offset > $num) {
320
                $this->entityManager->getUnitOfWork()->detach($entity);
321
                break;
322
            }
323
            $id = (string)$entity->getId();
324
            if (array_key_exists($id, $entities)) {
325
                throw new \RuntimeException('Entity with ID ' . $id . ' is already generated');
326
            }
327
            $entities[$id] = $entity;
328
        }
329
330
        return $entities;
331
    }
332
333
    public function getGenerator(): \Generator
334
    {
335
        $entityFqn = $this->testedEntityDsm->getReflectionClass()->getName();
336
        while (true) {
337
            $dto    = $this->generateDto();
338
            $entity = $this->entityFactory->setEntityManager($this->entityManager)->create($entityFqn, $dto);
339
            yield $entity;
340
        }
341
    }
342
}
343