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 (#69)
by joseph
15:46
created

TestEntityGenerator::generateEntity()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

Changes 0
Metric Value
eloc 2
dl 0
loc 6
rs 10
c 0
b 0
f 0
ccs 3
cts 3
cp 1
cc 1
nc 1
nop 2
crap 1
1
<?php declare(strict_types=1);
2
3
namespace EdmondsCommerce\DoctrineStaticMeta\Entity\Testing;
4
5
use Doctrine\Common\Collections\ArrayCollection;
6
use Doctrine\ORM\EntityManager;
7
use Doctrine\ORM\Mapping\ClassMetadataInfo;
8
use EdmondsCommerce\DoctrineStaticMeta\CodeGeneration\NamespaceHelper;
9
use EdmondsCommerce\DoctrineStaticMeta\Entity\Interfaces\EntityInterface;
10
use EdmondsCommerce\DoctrineStaticMeta\Entity\Savers\EntitySaverFactory;
11
use Faker;
12
use Faker\ORM\Doctrine\Populator;
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
 * @package EdmondsCommerce\DoctrineStaticMeta\Entity\Testing
23
 * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
24
 */
25
class TestEntityGenerator
26
{
27
    /**
28
     * @var Faker\Generator
29
     */
30
    protected static $generator;
31
32
33
    /**
34
     * @var EntityManager
35
     */
36
    protected $entityManager;
37
38
    /**
39
     * These two are used to keep track of unique fields and ensure we dont accidently make apply none unique values
40
     *
41
     * @var array
42
     */
43
    private static $uniqueStrings = [];
44
    /**
45
     * @var int
46
     */
47
    private static $uniqueInt;
48
49
50
    /**
51
     * An array of fieldNames to class names that are to be instantiated as column formatters as required
52
     *
53
     * @var array|string[]
54
     */
55
    protected $fakerDataProviderClasses;
56
57
    /**
58
     * A cache of instantiated column data providers
59
     *
60
     * @var array
61
     */
62
    protected $fakerDataProviderObjects = [];
63
64
    /**
65
     * Reflection of the tested entity
66
     *
67
     * @var \ts\Reflection\ReflectionClass
68
     */
69
    protected $testedEntityReflectionClass;
70
    /**
71
     * @var EntitySaverFactory
72
     */
73
    protected $entitySaverFactory;
74
75
    /**
76
     * TestEntityGenerator constructor.
77
     *
78
     * @param float|null                     $seed
79
     * @param array|string[]                 $fakerDataProviderClasses
80
     * @param \ts\Reflection\ReflectionClass $testedEntityReflectionClass
81
     * @param EntitySaverFactory             $entitySaverFactory
82
     * @SuppressWarnings(PHPMD.StaticAccess)
83
     */
84 17
    public function __construct(
85
        ?float $seed,
86
        array $fakerDataProviderClasses,
87
        \ts\Reflection\ReflectionClass $testedEntityReflectionClass,
88
        EntitySaverFactory $entitySaverFactory
89
    ) {
90 17
        $this->initFakerGenerator($seed);
91 17
        $this->fakerDataProviderClasses    = $fakerDataProviderClasses;
92 17
        $this->testedEntityReflectionClass = $testedEntityReflectionClass;
93 17
        $this->entitySaverFactory          = $entitySaverFactory;
94 17
    }
95
96
    /**
97
     * @param float|null $seed
98
     * @SuppressWarnings(PHPMD.StaticAccess)
99
     */
100 17
    protected function initFakerGenerator(?float $seed): void
101
    {
102 17
        if (null === self::$generator) {
103 1
            self::$generator = Faker\Factory::create();
104 1
            if (null !== $seed) {
105 1
                self::$generator->seed($seed);
106
            }
107
        }
108 17
    }
109
110
    /**
111
     * @param EntityManager $entityManager
112
     * @param string        $class
113
     *
114
     * @return EntityInterface
115
     * @throws \Doctrine\ORM\Mapping\MappingException
116
     * @SuppressWarnings(PHPMD.StaticAccess)
117
     */
118 2
    public function generateEntity(EntityManager $entityManager, string $class): EntityInterface
119
    {
120
121 2
        $result = $this->generateEntities($entityManager, $class, 1);
122
123 2
        return $result[0];
124
    }
125
126
    /**
127
     * @param EntityManager $entityManager
128
     * @param string        $class
129
     * @param int           $num
130
     *
131
     * @return array|EntityInterface[]
132
     * @throws \Doctrine\ORM\Mapping\MappingException
133
     */
134 17
    public function generateEntities(EntityManager $entityManager, string $class, int $num): array
135
    {
136 17
        $customColumnFormatters = $this->generateColumnFormatters($entityManager, $class);
137 17
        $populator              = new Populator(self::$generator, $entityManager);
138 17
        $populator->addEntity($class, $num, $customColumnFormatters);
139
140 17
        $result = $populator->execute($entityManager, false);
141
142 17
        return $result[ltrim($class, '\\')];
143
    }
144
145
    /**
146
     * @param EntityManager   $entityManager
147
     * @param EntityInterface $generated
148
     *
149
     * @throws \Doctrine\ORM\Mapping\MappingException
150
     * @throws \EdmondsCommerce\DoctrineStaticMeta\Exception\DoctrineStaticMetaException
151
     * @throws \ErrorException
152
     * @throws \ReflectionException
153
     * @SuppressWarnings(PHPMD.ElseExpression)
154
     */
155 1
    public function addAssociationEntities(
156
        EntityManager $entityManager,
157
        EntityInterface $generated
158
    ): void {
159 1
        $class    = $this->testedEntityReflectionClass->getName();
160 1
        $meta     = $entityManager->getClassMetadata($class);
161 1
        $mappings = $meta->getAssociationMappings();
162 1
        if (empty($mappings)) {
163
            return;
164
        }
165 1
        $namespaceHelper = new NamespaceHelper();
166 1
        $methods         = array_map('strtolower', get_class_methods($generated));
167 1
        foreach ($mappings as $mapping) {
168 1
            $mappingEntityClass = $mapping['targetEntity'];
169 1
            $mappingEntity      = $this->generateEntity($entityManager, $mappingEntityClass);
170 1
            $errorMessage       = "Error adding association entity $mappingEntityClass to $class: %s";
171 1
            $this->entitySaverFactory->getSaverForEntity($mappingEntity)->save($mappingEntity);
172 1
            $mappingEntityPluralInterface = $namespaceHelper->getHasPluralInterfaceFqnForEntity($mappingEntityClass);
173 1
            if ($this->testedEntityReflectionClass->implementsInterface($mappingEntityPluralInterface)) {
174 1
                $this->assertSame(
175 1
                    $mappingEntityClass::getPlural(),
176 1
                    $mapping['fieldName'],
177 1
                    sprintf($errorMessage, ' mapping should be plural')
178
                );
179 1
                $method = 'add'.$mappingEntityClass::getSingular();
180
            } else {
181 1
                $this->assertSame(
182 1
                    $mappingEntityClass::getSingular(),
183 1
                    $mapping['fieldName'],
184 1
                    sprintf($errorMessage, ' mapping should be singular')
185
                );
186 1
                $method = 'set'.$mappingEntityClass::getSingular();
187
            }
188 1
            $this->assertInArray(
189 1
                strtolower($method),
190 1
                $methods,
191 1
                sprintf($errorMessage, $method.' method is not defined')
192
            );
193 1
            $generated->$method($mappingEntity);
194
        }
195 1
    }
196
197
    /**
198
     * Stub of PHPUnit Assertion method
199
     *
200
     * @param mixed  $expected
201
     * @param mixed  $actual
202
     * @param string $error
203
     *
204
     * @throws \ErrorException
205
     */
206 1
    protected function assertSame($expected, $actual, string $error): void
207
    {
208 1
        if ($expected !== $actual) {
209
            throw new \ErrorException($error);
210
        }
211 1
    }
212
213
    /**
214
     * Stub of PHPUnit Assertion method
215
     *
216
     * @param mixed  $needle
217
     * @param array  $haystack
218
     * @param string $error
219
     *
220
     * @throws \ErrorException
221
     */
222 1
    protected function assertInArray($needle, array $haystack, string $error): void
223
    {
224 1
        if (false === \in_array($needle, $haystack, true)) {
225
            throw new \ErrorException($error);
226
        }
227 1
    }
228
229
    /**
230
     * @param EntityManager $entityManager
231
     * @param string        $class
232
     *
233
     * @return array
234
     * @throws \Doctrine\ORM\Mapping\MappingException
235
     */
236 17
    protected function generateColumnFormatters(EntityManager $entityManager, string $class): array
237
    {
238 17
        $columnFormatters = [];
239 17
        $meta             = $entityManager->getClassMetadata($class);
240 17
        $mappings         = $meta->getAssociationMappings();
241 17
        $this->initialiseColumnFormatters($meta, $mappings, $columnFormatters);
242 17
        $fieldNames = $meta->getFieldNames();
243
244 17
        foreach ($fieldNames as $fieldName) {
245 17
            if (isset($columnFormatters[$fieldName])) {
246
                continue;
247
            }
248 17
            if (true === $this->setFakerDataProvider($columnFormatters, $fieldName)) {
249
                continue;
250
            }
251 17
            $fieldMapping = $meta->getFieldMapping($fieldName);
252 17
            if (true === ($fieldMapping['unique'] ?? false)) {
253
                $this->addUniqueColumnFormatter($fieldMapping, $columnFormatters, $fieldName);
254 17
                continue;
255
            }
256
        }
257
258 17
        return $columnFormatters;
259
    }
260
261
    protected function addUniqueColumnFormatter(array &$fieldMapping, array &$columnFormatters, string $fieldName): void
262
    {
263
        switch ($fieldMapping['type']) {
264
            case 'string':
265
                $columnFormatters[$fieldName] = $this->getUniqueString();
266
                break;
267
            case 'integer':
268
            case 'bigint':
269
                $columnFormatters[$fieldName] = $this->getUniqueInt();
270
                break;
271
            default:
272
                throw new \InvalidArgumentException('unique field has an unsupported type: '
273
                                                    .print_r($fieldMapping, true));
274
        }
275
    }
276
277
    /**
278
     * Loop through mappings and initialise empty array collections for colection valued mappings, or null if not
279
     *
280
     * @param ClassMetadataInfo $meta
281
     * @param array             $mappings
282
     * @param array             $columnFormatters
283
     */
284 17
    protected function initialiseColumnFormatters(
285
        ClassMetadataInfo $meta,
286
        array &$mappings,
287
        array &$columnFormatters
288
    ): void {
289 17
        foreach ($mappings as $mapping) {
290 2
            if ($meta->isCollectionValuedAssociation($mapping['fieldName'])) {
291 2
                $columnFormatters[$mapping['fieldName']] = new ArrayCollection();
292 2
                continue;
293
            }
294 2
            $columnFormatters[$mapping['fieldName']] = null;
295
        }
296 17
    }
297
298
    protected function getUniqueString(): string
299
    {
300
        $string = 'unique string: '.$this->getUniqueInt().md5((string)time());
301
        while (isset(self::$uniqueStrings[$string])) {
302
            $string                       = md5((string)time());
303
            self::$uniqueStrings[$string] = true;
304
        }
305
306
        return $string;
307
    }
308
309
    protected function getUniqueInt(): int
310
    {
311
        return ++self::$uniqueInt;
312
    }
313
314
    /**
315
     * Add a faker data provider to the columnFormatters array (by reference) if there is one available
316
     *
317
     * Handles instantiating and caching of the data providers
318
     *
319
     * @param array  $columnFormatters
320
     * @param string $fieldName
321
     *
322
     * @return bool
323
     */
324 17
    protected function setFakerDataProvider(array &$columnFormatters, string $fieldName): bool
325
    {
326 17
        if (!isset($this->fakerDataProviderClasses[$fieldName])) {
327 17
            return false;
328
        }
329
        if (!isset($this->fakerDataProviderObjects[$fieldName])) {
330
            $class                                      = $this->fakerDataProviderClasses[$fieldName];
331
            $this->fakerDataProviderObjects[$fieldName] = new $class(self::$generator);
332
        }
333
        $columnFormatters[$fieldName] = $this->fakerDataProviderObjects[$fieldName];
334
335
        return true;
336
    }
337
}
338