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 ( cc354f...2af0ad )
by joseph
12s
created

TestEntityGenerator::generateColumnFormatters()   A

Complexity

Conditions 5
Paths 5

Size

Total Lines 23
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 13
CRAP Score 5.1647

Importance

Changes 0
Metric Value
eloc 15
dl 0
loc 23
rs 9.4555
c 0
b 0
f 0
ccs 13
cts 16
cp 0.8125
cc 5
nc 5
nop 2
crap 5.1647
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 (\interface_exists($mappingEntityPluralInterface)
174 1
                && $this->testedEntityReflectionClass->implementsInterface($mappingEntityPluralInterface)
175
            ) {
176 1
                $this->assertSame(
177 1
                    $mappingEntityClass::getPlural(),
178 1
                    $mapping['fieldName'],
179 1
                    sprintf($errorMessage, ' mapping should be plural')
180
                );
181 1
                $method = 'add'.$mappingEntityClass::getSingular();
182
            } else {
183 1
                $this->assertSame(
184 1
                    $mappingEntityClass::getSingular(),
185 1
                    $mapping['fieldName'],
186 1
                    sprintf($errorMessage, ' mapping should be singular')
187
                );
188 1
                $method = 'set'.$mappingEntityClass::getSingular();
189
            }
190 1
            $this->assertInArray(
191 1
                strtolower($method),
192 1
                $methods,
193 1
                sprintf($errorMessage, $method.' method is not defined')
194
            );
195 1
            $generated->$method($mappingEntity);
196
        }
197 1
    }
198
199
    /**
200
     * Stub of PHPUnit Assertion method
201
     *
202
     * @param mixed  $expected
203
     * @param mixed  $actual
204
     * @param string $error
205
     *
206
     * @throws \ErrorException
207
     */
208 1
    protected function assertSame($expected, $actual, string $error): void
209
    {
210 1
        if ($expected !== $actual) {
211
            throw new \ErrorException($error);
212
        }
213 1
    }
214
215
    /**
216
     * Stub of PHPUnit Assertion method
217
     *
218
     * @param mixed  $needle
219
     * @param array  $haystack
220
     * @param string $error
221
     *
222
     * @throws \ErrorException
223
     */
224 1
    protected function assertInArray($needle, array $haystack, string $error): void
225
    {
226 1
        if (false === \in_array($needle, $haystack, true)) {
227
            throw new \ErrorException($error);
228
        }
229 1
    }
230
231
    /**
232
     * @param EntityManager $entityManager
233
     * @param string        $class
234
     *
235
     * @return array
236
     * @throws \Doctrine\ORM\Mapping\MappingException
237
     */
238 17
    protected function generateColumnFormatters(EntityManager $entityManager, string $class): array
239
    {
240 17
        $columnFormatters = [];
241 17
        $meta             = $entityManager->getClassMetadata($class);
242 17
        $mappings         = $meta->getAssociationMappings();
243 17
        $this->initialiseColumnFormatters($meta, $mappings, $columnFormatters);
244 17
        $fieldNames = $meta->getFieldNames();
245
246 17
        foreach ($fieldNames as $fieldName) {
247 17
            if (isset($columnFormatters[$fieldName])) {
248
                continue;
249
            }
250 17
            if (true === $this->setFakerDataProvider($columnFormatters, $fieldName)) {
251
                continue;
252
            }
253 17
            $fieldMapping = $meta->getFieldMapping($fieldName);
254 17
            if (true === ($fieldMapping['unique'] ?? false)) {
255
                $this->addUniqueColumnFormatter($fieldMapping, $columnFormatters, $fieldName);
256 17
                continue;
257
            }
258
        }
259
260 17
        return $columnFormatters;
261
    }
262
263
    protected function addUniqueColumnFormatter(array &$fieldMapping, array &$columnFormatters, string $fieldName): void
264
    {
265
        switch ($fieldMapping['type']) {
266
            case 'string':
267
                $columnFormatters[$fieldName] = $this->getUniqueString();
268
                break;
269
            case 'integer':
270
            case 'bigint':
271
                $columnFormatters[$fieldName] = $this->getUniqueInt();
272
                break;
273
            default:
274
                throw new \InvalidArgumentException('unique field has an unsupported type: '
275
                                                    .print_r($fieldMapping, true));
276
        }
277
    }
278
279
    /**
280
     * Loop through mappings and initialise empty array collections for colection valued mappings, or null if not
281
     *
282
     * @param ClassMetadataInfo $meta
283
     * @param array             $mappings
284
     * @param array             $columnFormatters
285
     */
286 17
    protected function initialiseColumnFormatters(
287
        ClassMetadataInfo $meta,
288
        array &$mappings,
289
        array &$columnFormatters
290
    ): void {
291 17
        foreach ($mappings as $mapping) {
292 2
            if ($meta->isCollectionValuedAssociation($mapping['fieldName'])) {
293 2
                $columnFormatters[$mapping['fieldName']] = new ArrayCollection();
294 2
                continue;
295
            }
296 2
            $columnFormatters[$mapping['fieldName']] = null;
297
        }
298 17
    }
299
300
    protected function getUniqueString(): string
301
    {
302
        $string = 'unique string: '.$this->getUniqueInt().md5((string)time());
303
        while (isset(self::$uniqueStrings[$string])) {
304
            $string                       = md5((string)time());
305
            self::$uniqueStrings[$string] = true;
306
        }
307
308
        return $string;
309
    }
310
311
    protected function getUniqueInt(): int
312
    {
313
        return ++self::$uniqueInt;
314
    }
315
316
    /**
317
     * Add a faker data provider to the columnFormatters array (by reference) if there is one available
318
     *
319
     * Handles instantiating and caching of the data providers
320
     *
321
     * @param array  $columnFormatters
322
     * @param string $fieldName
323
     *
324
     * @return bool
325
     */
326 17
    protected function setFakerDataProvider(array &$columnFormatters, string $fieldName): bool
327
    {
328 17
        if (!isset($this->fakerDataProviderClasses[$fieldName])) {
329 17
            return false;
330
        }
331
        if (!isset($this->fakerDataProviderObjects[$fieldName])) {
332
            $class                                      = $this->fakerDataProviderClasses[$fieldName];
333
            $this->fakerDataProviderObjects[$fieldName] = new $class(self::$generator);
334
        }
335
        $columnFormatters[$fieldName] = $this->fakerDataProviderObjects[$fieldName];
336
337
        return true;
338
    }
339
}
340