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

TestEntityGenerator::assertInArray()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 2.1481

Importance

Changes 0
Metric Value
dl 0
loc 4
ccs 2
cts 3
cp 0.6667
rs 10
c 0
b 0
f 0
cc 2
eloc 2
nc 2
nop 3
crap 2.1481
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 14
    public function __construct(
85
        ?float $seed,
86
        array $fakerDataProviderClasses,
87
        \ReflectionClass $testedEntityReflectionClass,
88
        EntitySaverFactory $entitySaverFactory
89
    ) {
90 14
        $this->generator = Faker\Factory::create();
91 14
        if (null !== $seed) {
92 14
            $this->generator->seed($seed);
93
        }
94 14
        $this->fakerDataProviderClasses    = $fakerDataProviderClasses;
95 14
        $this->testedEntityReflectionClass = $testedEntityReflectionClass;
96 14
        $this->entitySaverFactory          = $entitySaverFactory;
97 14
    }
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 14
    public function generateEntities(EntityManager $entityManager, string $class, int $num): array
124
    {
125 14
        $customColumnFormatters = $this->generateColumnFormatters($entityManager, $class);
126 14
        $populator              = new Populator($this->generator, $entityManager);
127 14
        $populator->addEntity($class, $num, $customColumnFormatters);
128
129 14
        $result = $populator->execute($entityManager, false);
130
131 14
        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 14
    protected function generateColumnFormatters(EntityManager $entityManager, string $class): array
226
    {
227 14
        $columnFormatters = [];
228 14
        $meta             = $entityManager->getClassMetadata($class);
229 14
        $mappings         = $meta->getAssociationMappings();
230 14
        $this->initialiseColumnFormatters($meta, $mappings, $columnFormatters);
231 14
        $fieldNames = $meta->getFieldNames();
232
233 14
        foreach ($fieldNames as $fieldName) {
234 14
            if (isset($columnFormatters[$fieldName])) {
235
                continue;
236
            }
237 14
            if (true === $this->setFakerDataProvider($columnFormatters, $fieldName)) {
238
                continue;
239
            }
240 14
            $fieldMapping = $meta->getFieldMapping($fieldName);
241 14
            if (true === ($fieldMapping['unique'] ?? false)) {
242
                $this->addUniqueColumnFormatter($fieldMapping, $columnFormatters, $fieldName);
243 14
                continue;
244
            }
245
        }
246
247 14
        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
                $columnFormatters[$fieldName] = $this->getUniqueInt();
258
                break;
259
            default:
260
                throw new \InvalidArgumentException('unique field has an unsupported type: '
261
                                                    .print_r($fieldMapping, true));
262
        }
263
    }
264
265
    /**
266
     * Loop through mappings and initialise empty array collections for colection valued mappings, or null if not
267
     *
268
     * @param ClassMetadataInfo $meta
269
     * @param array             $mappings
270
     * @param array             $columnFormatters
271
     */
272 14
    protected function initialiseColumnFormatters(
273
        ClassMetadataInfo $meta,
274
        array &$mappings,
275
        array &$columnFormatters
276
    ) {
277 14
        foreach ($mappings as $mapping) {
278 2
            if ($meta->isCollectionValuedAssociation($mapping['fieldName'])) {
279 2
                $columnFormatters[$mapping['fieldName']] = new ArrayCollection();
280 2
                continue;
281
            }
282 2
            $columnFormatters[$mapping['fieldName']] = null;
283
        }
284 14
    }
285
286
    protected function getUniqueString(): string
287
    {
288
        $string = 'unique string: '.$this->getUniqueInt().md5((string)time());
289
        while (isset(self::$uniqueStrings[$string])) {
290
            $string                       = md5((string)time());
291
            self::$uniqueStrings[$string] = true;
292
        }
293
294
        return $string;
295
    }
296
297
    protected function getUniqueInt(): int
298
    {
299
        return ++self::$uniqueInt;
300
    }
301
302
    /**
303
     * Add a faker data provider to the columnFormatters array (by reference) if there is one available
304
     *
305
     * Handles instantiating and caching of the data providers
306
     *
307
     * @param array  $columnFormatters
308
     * @param string $fieldName
309
     *
310
     * @return bool
311
     */
312 14
    protected function setFakerDataProvider(array &$columnFormatters, string $fieldName): bool
313
    {
314 14
        if (!isset($this->fakerDataProviderClasses[$fieldName])) {
315 14
            return false;
316
        }
317
        if (!isset($this->fakerDataProviderObjects[$fieldName])) {
318
            $class                                      = $this->fakerDataProviderClasses[$fieldName];
319
            $this->fakerDataProviderObjects[$fieldName] = new $class($this->generator);
320
        }
321
        $columnFormatters[$fieldName] = $this->fakerDataProviderObjects[$fieldName];
322
323
        return true;
324
    }
325
}
326