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 (#58)
by joseph
18:31
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
dl 0
loc 6
ccs 3
cts 3
cp 1
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 2
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 $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
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 \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              $fakerDataProviderClasses
80
     * @param \ReflectionClass   $testedEntityReflectionClass
81
     * @param EntitySaverFactory $entitySaverFactory
82
     * @SuppressWarnings(PHPMD.StaticAccess)
83
     */
84 17
    public function __construct(
85
        ?float $seed,
86
        array $fakerDataProviderClasses,
87
        \ReflectionClass $testedEntityReflectionClass,
88
        EntitySaverFactory $entitySaverFactory
89
    ) {
90 17
        $this->generator = Faker\Factory::create();
91 17
        if (null !== $seed) {
92 17
            $this->generator->seed($seed);
93
        }
94 17
        $this->fakerDataProviderClasses    = $fakerDataProviderClasses;
95 17
        $this->testedEntityReflectionClass = $testedEntityReflectionClass;
96 17
        $this->entitySaverFactory          = $entitySaverFactory;
97 17
    }
98
99
    /**
100
     * @param EntityManager $entityManager
101
     * @param string        $class
102
     *
103
     * @return EntityInterface
104
     * @throws \Doctrine\ORM\Mapping\MappingException
105
     * @SuppressWarnings(PHPMD.StaticAccess)
106
     */
107 2
    public function generateEntity(EntityManager $entityManager, string $class): EntityInterface
108
    {
109
110 2
        $result = $this->generateEntities($entityManager, $class, 1);
111
112 2
        return $result[0];
113
    }
114
115
    /**
116
     * @param EntityManager $entityManager
117
     * @param string        $class
118
     * @param int           $num
119
     *
120
     * @return array|EntityInterface[]
121
     * @throws \Doctrine\ORM\Mapping\MappingException
122
     */
123 17
    public function generateEntities(EntityManager $entityManager, string $class, int $num): array
124
    {
125 17
        $customColumnFormatters = $this->generateColumnFormatters($entityManager, $class);
126 17
        $populator              = new Populator($this->generator, $entityManager);
127 17
        $populator->addEntity($class, $num, $customColumnFormatters);
128
129 17
        $result = $populator->execute($entityManager, false);
130
131 17
        return $result[ltrim($class, '\\')];
132
    }
133
134
    /**
135
     * @param EntityManager   $entityManager
136
     * @param EntityInterface $generated
137
     *
138
     * @throws \Doctrine\ORM\Mapping\MappingException
139
     * @throws \EdmondsCommerce\DoctrineStaticMeta\Exception\DoctrineStaticMetaException
140
     * @throws \ErrorException
141
     * @throws \ReflectionException
142
     * @SuppressWarnings(PHPMD.ElseExpression)
143
     */
144 1
    public function addAssociationEntities(
145
        EntityManager $entityManager,
146
        EntityInterface $generated
147
    ): void {
148 1
        $class    = $this->testedEntityReflectionClass->getName();
149 1
        $meta     = $entityManager->getClassMetadata($class);
150 1
        $mappings = $meta->getAssociationMappings();
151 1
        if (empty($mappings)) {
152
            return;
153
        }
154 1
        $namespaceHelper = new NamespaceHelper();
155 1
        $methods         = array_map('strtolower', get_class_methods($generated));
156 1
        foreach ($mappings as $mapping) {
157 1
            $mappingEntityClass = $mapping['targetEntity'];
158 1
            $mappingEntity      = $this->generateEntity($entityManager, $mappingEntityClass);
159 1
            $errorMessage       = "Error adding association entity $mappingEntityClass to $class: %s";
160 1
            $this->entitySaverFactory->getSaverForEntity($mappingEntity)->save($mappingEntity);
161 1
            $mappingEntityPluralInterface = $namespaceHelper->getHasPluralInterfaceFqnForEntity($mappingEntityClass);
162 1
            if ($this->testedEntityReflectionClass->implementsInterface($mappingEntityPluralInterface)) {
163 1
                $this->assertSame(
164 1
                    $mappingEntityClass::getPlural(),
165 1
                    $mapping['fieldName'],
166 1
                    sprintf($errorMessage, ' mapping should be plural')
167
                );
168 1
                $method = 'add'.$mappingEntityClass::getSingular();
169
            } else {
170 1
                $this->assertSame(
171 1
                    $mappingEntityClass::getSingular(),
172 1
                    $mapping['fieldName'],
173 1
                    sprintf($errorMessage, ' mapping should be singular')
174
                );
175 1
                $method = 'set'.$mappingEntityClass::getSingular();
176
            }
177 1
            $this->assertInArray(
178 1
                strtolower($method),
179 1
                $methods,
180 1
                sprintf($errorMessage, $method.' method is not defined')
181
            );
182 1
            $generated->$method($mappingEntity);
183
        }
184 1
    }
185
186
    /**
187
     * Stub of PHPUnit Assertion method
188
     *
189
     * @param mixed  $expected
190
     * @param mixed  $actual
191
     * @param string $error
192
     *
193
     * @throws \ErrorException
194
     */
195 1
    protected function assertSame($expected, $actual, string $error): void
196
    {
197 1
        if ($expected !== $actual) {
198
            throw new \ErrorException($error);
199
        }
200 1
    }
201
202
    /**
203
     * Stub of PHPUnit Assertion method
204
     *
205
     * @param mixed  $needle
206
     * @param array  $haystack
207
     * @param string $error
208
     *
209
     * @throws \ErrorException
210
     */
211 1
    protected function assertInArray($needle, array $haystack, string $error): void
212
    {
213 1
        if (false === \in_array($needle, $haystack, true)) {
214
            throw new \ErrorException($error);
215
        }
216 1
    }
217
218
    /**
219
     * @param EntityManager $entityManager
220
     * @param string        $class
221
     *
222
     * @return array
223
     * @throws \Doctrine\ORM\Mapping\MappingException
224
     */
225 17
    protected function generateColumnFormatters(EntityManager $entityManager, string $class): array
226
    {
227 17
        $columnFormatters = [];
228 17
        $meta             = $entityManager->getClassMetadata($class);
229 17
        $mappings         = $meta->getAssociationMappings();
230 17
        $this->initialiseColumnFormatters($meta, $mappings, $columnFormatters);
231 17
        $fieldNames = $meta->getFieldNames();
232
233 17
        foreach ($fieldNames as $fieldName) {
234 17
            if (isset($columnFormatters[$fieldName])) {
235
                continue;
236
            }
237 17
            if (true === $this->setFakerDataProvider($columnFormatters, $fieldName)) {
238
                continue;
239
            }
240 17
            $fieldMapping = $meta->getFieldMapping($fieldName);
241 17
            if (true === ($fieldMapping['unique'] ?? false)) {
242
                $this->addUniqueColumnFormatter($fieldMapping, $columnFormatters, $fieldName);
243 17
                continue;
244
            }
245
        }
246
247 17
        return $columnFormatters;
248
    }
249
250
    protected function addUniqueColumnFormatter(array &$fieldMapping, array &$columnFormatters, string $fieldName)
251
    {
252
        switch ($fieldMapping['type']) {
253
            case 'string':
254
                $columnFormatters[$fieldName] = $this->getUniqueString();
255
                break;
256
            case 'integer':
257
            case 'bigint':
258
                $columnFormatters[$fieldName] = $this->getUniqueInt();
259
                break;
260
            default:
261
                throw new \InvalidArgumentException('unique field has an unsupported type: '
262
                                                    .print_r($fieldMapping, true));
263
        }
264
    }
265
266
    /**
267
     * Loop through mappings and initialise empty array collections for colection valued mappings, or null if not
268
     *
269
     * @param ClassMetadataInfo $meta
270
     * @param array             $mappings
271
     * @param array             $columnFormatters
272
     */
273 17
    protected function initialiseColumnFormatters(
274
        ClassMetadataInfo $meta,
275
        array &$mappings,
276
        array &$columnFormatters
277
    ) {
278 17
        foreach ($mappings as $mapping) {
279 2
            if ($meta->isCollectionValuedAssociation($mapping['fieldName'])) {
280 2
                $columnFormatters[$mapping['fieldName']] = new ArrayCollection();
281 2
                continue;
282
            }
283 2
            $columnFormatters[$mapping['fieldName']] = null;
284
        }
285 17
    }
286
287
    protected function getUniqueString(): string
288
    {
289
        $string = 'unique string: '.$this->getUniqueInt().md5((string)time());
290
        while (isset(self::$uniqueStrings[$string])) {
291
            $string                       = md5((string)time());
292
            self::$uniqueStrings[$string] = true;
293
        }
294
295
        return $string;
296
    }
297
298
    protected function getUniqueInt(): int
299
    {
300
        return ++self::$uniqueInt;
301
    }
302
303
    /**
304
     * Add a faker data provider to the columnFormatters array (by reference) if there is one available
305
     *
306
     * Handles instantiating and caching of the data providers
307
     *
308
     * @param array  $columnFormatters
309
     * @param string $fieldName
310
     *
311
     * @return bool
312
     */
313 17
    protected function setFakerDataProvider(array &$columnFormatters, string $fieldName): bool
314
    {
315 17
        if (!isset($this->fakerDataProviderClasses[$fieldName])) {
316 17
            return false;
317
        }
318
        if (!isset($this->fakerDataProviderObjects[$fieldName])) {
319
            $class                                      = $this->fakerDataProviderClasses[$fieldName];
320
            $this->fakerDataProviderObjects[$fieldName] = new $class($this->generator);
321
        }
322
        $columnFormatters[$fieldName] = $this->fakerDataProviderObjects[$fieldName];
323
324
        return true;
325
    }
326
}
327