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 (#51)
by joseph
102:38 queued 99:24
created

TestEntityGenerator::generateColumnFormatters()   B

Complexity

Conditions 5
Paths 5

Size

Total Lines 23
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 30

Importance

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