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 (#221)
by joseph
96:29 queued 93:03
created

Builder::setPathToProjectRoot()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 14
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 12
CRAP Score 1

Importance

Changes 4
Bugs 0 Features 0
Metric Value
cc 1
eloc 11
c 4
b 0
f 0
nc 1
nop 1
dl 0
loc 14
ccs 12
cts 12
cp 1
crap 1
rs 9.9
1
<?php declare(strict_types=1);
2
3
namespace EdmondsCommerce\DoctrineStaticMeta\Builder;
4
5
use EdmondsCommerce\DoctrineStaticMeta\CodeGeneration\Action\CreateDtosForAllEntitiesAction;
6
use EdmondsCommerce\DoctrineStaticMeta\CodeGeneration\CodeHelper;
7
use EdmondsCommerce\DoctrineStaticMeta\CodeGeneration\Generator\Embeddable\ArchetypeEmbeddableGenerator;
8
use EdmondsCommerce\DoctrineStaticMeta\CodeGeneration\Generator\Embeddable\EntityEmbeddableSetter;
9
use EdmondsCommerce\DoctrineStaticMeta\CodeGeneration\Generator\EntityGenerator;
10
use EdmondsCommerce\DoctrineStaticMeta\CodeGeneration\Generator\Field\EntityFieldSetter;
11
use EdmondsCommerce\DoctrineStaticMeta\CodeGeneration\Generator\Field\FieldGenerator;
12
use EdmondsCommerce\DoctrineStaticMeta\CodeGeneration\Generator\RelationsGenerator;
13
use EdmondsCommerce\DoctrineStaticMeta\CodeGeneration\NamespaceHelper;
14
use EdmondsCommerce\DoctrineStaticMeta\CodeGeneration\PostProcessor\CopyPhpstormMeta;
15
use EdmondsCommerce\DoctrineStaticMeta\CodeGeneration\PostProcessor\EntityFormatter;
16
use EdmondsCommerce\DoctrineStaticMeta\CodeGeneration\UnusedRelationsRemover;
17
use EdmondsCommerce\DoctrineStaticMeta\Config;
18
use EdmondsCommerce\DoctrineStaticMeta\Exception\DoctrineStaticMetaException;
19
use Exception;
20
use gossi\codegen\model\PhpClass;
21
use gossi\codegen\model\PhpConstant;
22
use gossi\codegen\model\PhpInterface;
23
use gossi\codegen\model\PhpTrait;
24
use ReflectionException;
25
use RuntimeException;
26
use ts\Reflection\ReflectionClass;
27
use function preg_replace;
28
29
/**
30
 * Class Builder
31
 *
32
 * @package EdmondsCommerce\DoctrineStaticMeta\Builder
33
 * @SuppressWarnings(PHPMD)
34
 */
35
class Builder
36
{
37
38
    /**
39
     * @var EntityGenerator
40
     */
41
    protected $entityGenerator;
42
    /**
43
     * @var FieldGenerator
44
     */
45
    protected $fieldGenerator;
46
    /**
47
     * @var EntityFieldSetter
48
     */
49
    protected $fieldSetter;
50
    /**
51
     * @var RelationsGenerator
52
     */
53
    protected $relationsGenerator;
54
    /**
55
     * @var ArchetypeEmbeddableGenerator
56
     */
57
    protected $archetypeEmbeddableGenerator;
58
    /**
59
     * @var EntityEmbeddableSetter
60
     */
61
    protected $embeddableSetter;
62
    /**
63
     * @var CodeHelper
64
     */
65
    protected $codeHelper;
66
    /**
67
     * @var UnusedRelationsRemover
68
     */
69
    protected $unusedRelationsRemover;
70
    /**
71
     * @var CreateDtosForAllEntitiesAction
72
     */
73
    private $dataTransferObjectsForAllEntitiesAction;
74
    /**
75
     * @var EntityFormatter
76
     */
77
    private $entityFormatter;
78
    /**
79
     * @var CopyPhpstormMeta
80
     */
81
    private $copyPhpstormMeta;
82
    /**
83
     * @var NamespaceHelper
84
     */
85
    private $namespaceHelper;
86
87 1
    public function __construct(
88
        EntityGenerator $entityGenerator,
89
        FieldGenerator $fieldGenerator,
90
        EntityFieldSetter $fieldSetter,
91
        RelationsGenerator $relationsGenerator,
92
        ArchetypeEmbeddableGenerator $archetypeEmbeddableGenerator,
93
        EntityEmbeddableSetter $embeddableSetter,
94
        CodeHelper $codeHelper,
95
        UnusedRelationsRemover $unusedRelationsRemover,
96
        CreateDtosForAllEntitiesAction $dataTransferObjectsForAllEntitiesAction,
97
        EntityFormatter $entityFormatter,
98
        Config $config,
99
        CopyPhpstormMeta $copyPhpstormMeta,
100
        NamespaceHelper $namespaceHelper
101
    ) {
102 1
        $this->entityGenerator                         = $entityGenerator;
103 1
        $this->fieldGenerator                          = $fieldGenerator;
104 1
        $this->fieldSetter                             = $fieldSetter;
105 1
        $this->relationsGenerator                      = $relationsGenerator;
106 1
        $this->archetypeEmbeddableGenerator            = $archetypeEmbeddableGenerator;
107 1
        $this->embeddableSetter                        = $embeddableSetter;
108 1
        $this->codeHelper                              = $codeHelper;
109 1
        $this->unusedRelationsRemover                  = $unusedRelationsRemover;
110 1
        $this->dataTransferObjectsForAllEntitiesAction = $dataTransferObjectsForAllEntitiesAction;
111 1
        $this->entityFormatter                         = $entityFormatter;
112 1
        $this->copyPhpstormMeta                        = $copyPhpstormMeta;
113 1
        $this->namespaceHelper                         = $namespaceHelper;
114
115 1
        $this->setPathToProjectRoot($config::getProjectRootDirectory());
116 1
    }
117
118 1
    public function setPathToProjectRoot(string $pathToProjectRoot): self
119
    {
120 1
        $this->entityGenerator->setPathToProjectRoot($pathToProjectRoot);
121 1
        $this->fieldGenerator->setPathToProjectRoot($pathToProjectRoot);
122 1
        $this->fieldSetter->setPathToProjectRoot($pathToProjectRoot);
123 1
        $this->relationsGenerator->setPathToProjectRoot($pathToProjectRoot);
124 1
        $this->archetypeEmbeddableGenerator->setPathToProjectRoot($pathToProjectRoot);
125 1
        $this->unusedRelationsRemover->setPathToProjectRoot($pathToProjectRoot);
126 1
        $this->dataTransferObjectsForAllEntitiesAction->setProjectRootDirectory($pathToProjectRoot);
127 1
        $this->embeddableSetter->setPathToProjectRoot($pathToProjectRoot);
128 1
        $this->entityFormatter->setPathToProjectRoot($pathToProjectRoot);
129 1
        $this->copyPhpstormMeta->setPathToProjectRoot($pathToProjectRoot);
130
131 1
        return $this;
132
    }
133
134
    /**
135
     * @return EntityGenerator
136
     */
137
    public function getEntityGenerator(): EntityGenerator
138
    {
139
        return $this->entityGenerator;
140
    }
141
142
    /**
143
     * @return FieldGenerator
144
     */
145
    public function getFieldGenerator(): FieldGenerator
146
    {
147
        return $this->fieldGenerator;
148
    }
149
150
    /**
151
     * @return EntityFieldSetter
152
     */
153
    public function getFieldSetter(): EntityFieldSetter
154
    {
155
        return $this->fieldSetter;
156
    }
157
158
    /**
159
     * @return RelationsGenerator
160
     */
161
    public function getRelationsGenerator(): RelationsGenerator
162
    {
163
        return $this->relationsGenerator;
164
    }
165
166
    /**
167
     * @return ArchetypeEmbeddableGenerator
168
     */
169
    public function getArchetypeEmbeddableGenerator(): ArchetypeEmbeddableGenerator
170
    {
171
        return $this->archetypeEmbeddableGenerator;
172
    }
173
174
    /**
175
     * @return EntityEmbeddableSetter
176
     */
177
    public function getEmbeddableSetter(): EntityEmbeddableSetter
178
    {
179
        return $this->embeddableSetter;
180
    }
181
182
    /**
183
     * Finalise build - run various steps to wrap up the build and tidy up the codebase
184
     *
185
     * @return Builder
186
     */
187
    public function finaliseBuild(): self
188
    {
189
        $this->dataTransferObjectsForAllEntitiesAction->run();
190
        $this->entityFormatter->run();
191
        $this->copyPhpstormMeta->run();
192
193
        return $this;
194
    }
195
196
    /**
197
     * This step will remove any relations code that is not being used
198
     *
199
     * Generally it needs to be run in a separate PHP process to ensure PHP loads the final versions of code
200
     */
201
    public function removeUnusedRelations(): void
202
    {
203
        $this->unusedRelationsRemover->run();
204
    }
205
206
    /**
207
     * @param array $entityFqns
208
     *
209
     * @return Builder
210
     * @throws DoctrineStaticMetaException
211
     */
212
    public function generateEntities(array $entityFqns): self
213
    {
214
        $this->setProjectRootNamespace(
215
            $this->namespaceHelper->getProjectNamespaceRootFromEntityFqn(
216
                current($entityFqns)
217
            )
218
        );
219
        foreach ($entityFqns as $entityFqn) {
220
            $this->entityGenerator->generateEntity($entityFqn);
221
        }
222
223
        return $this;
224
    }
225
226
    public function setProjectRootNamespace(string $projectRootNamespace): self
227
    {
228
        $this->entityGenerator->setProjectRootNamespace($projectRootNamespace);
229
        $this->fieldGenerator->setProjectRootNamespace($projectRootNamespace);
230
        $this->fieldSetter->setProjectRootNamespace($projectRootNamespace);
231
        $this->relationsGenerator->setProjectRootNamespace($projectRootNamespace);
232
        $this->archetypeEmbeddableGenerator->setProjectRootNamespace($projectRootNamespace);
233
        $this->dataTransferObjectsForAllEntitiesAction->setProjectRootNamespace($projectRootNamespace);
234
        $this->embeddableSetter->setProjectRootNamespace($projectRootNamespace);
235
        $this->unusedRelationsRemover->setProjectRootNamespace($projectRootNamespace);
236
237
        return $this;
238
    }
239
240
    /**
241
     * @param array $entityRelationEntity
242
     *
243
     * @return Builder
244
     * @throws DoctrineStaticMetaException
245
     */
246
    public function setEntityRelations(array $entityRelationEntity): self
247
    {
248
        foreach ($entityRelationEntity as list($owningEntityFqn, $hasType, $ownedEntityFqn)) {
249
            $this->relationsGenerator->setEntityHasRelationToEntity($owningEntityFqn, $hasType, $ownedEntityFqn);
250
        }
251
252
        return $this;
253
    }
254
255
    /**
256
     * @param array $fields
257
     *
258
     * @return array $traitFqns
259
     */
260
    public function generateFields(array $fields): array
261
    {
262
        $traitFqns = [];
263
        foreach ($fields as list($fieldFqn, $fieldType)) {
264
            try {
265
                $traitFqns[] = $this->fieldGenerator->generateField($fieldFqn, $fieldType);
266
            } catch (Exception $e) {
267
                throw new RuntimeException(
268
                    'Failed building field with $fieldFqn: ' . $fieldFqn . ' and $fieldType ' . $fieldType,
269
                    $e->getCode(),
270
                    $e
271
                );
272
            }
273
        }
274
275
        return $traitFqns;
276
    }
277
278
    public function generateKeyedFields(array $fields): array
279
    {
280
        $traitFqns = [];
281
282
        $defaults = [
283
            FieldGenerator::FIELD_PHP_TYPE_KEY      => null,
284
            FieldGenerator::FIELD_DEFAULT_VAULE_KEY => null,
285
            FieldGenerator::FIELD_IS_UNIQUE_KEY     => false,
286
        ];
287
288
        foreach ($fields as $field) {
289
            /* Can not use list here as it breaks PHPMD */
290
            $combinedDefaults = $field + $defaults;
291
            $fieldFqn         = $combinedDefaults[FieldGenerator::FIELD_FQN_KEY];
292
            $fieldType        = $combinedDefaults[FieldGenerator::FIELD_TYPE_KEY];
293
            $phpType          = $combinedDefaults[FieldGenerator::FIELD_PHP_TYPE_KEY];
294
            $defaultValue     = $combinedDefaults[FieldGenerator::FIELD_DEFAULT_VAULE_KEY];
295
            $isUnique         = $combinedDefaults[FieldGenerator::FIELD_IS_UNIQUE_KEY];
296
            try {
297
                $traitFqns[] =
298
                    $this->fieldGenerator->generateField($fieldFqn, $fieldType, $phpType, $defaultValue, $isUnique);
299
            } catch (Exception $e) {
300
                throw new RuntimeException(
301
                    'Failed building field with $fieldFqn: ' . $fieldFqn . ' and $fieldType ' . $fieldType,
302
                    $e->getCode(),
303
                    $e
304
                );
305
            }
306
        }
307
308
        return $traitFqns;
309
    }
310
311
    /**
312
     * @param string $entityFqn
313
     * @param array  $fieldFqns
314
     *
315
     * @return Builder
316
     * @throws DoctrineStaticMetaException
317
     */
318
    public function setFieldsToEntity(string $entityFqn, array $fieldFqns): self
319
    {
320
        foreach ($fieldFqns as $fieldFqn) {
321
            $this->fieldSetter->setEntityHasField($entityFqn, $fieldFqn);
322
        }
323
324
        return $this;
325
    }
326
327
    /**
328
     * @param array $embeddables
329
     *
330
     * @return array $traitFqns
331
     * @throws DoctrineStaticMetaException
332
     * @throws ReflectionException
333
     */
334
    public function generateEmbeddables(array $embeddables): array
335
    {
336
        $traitFqns = [];
337
        foreach ($embeddables as $embeddable) {
338
            [$archetypeEmbeddableObjectFqn, $newEmbeddableObjectClassName] = array_values($embeddable);
339
            $traitFqns[] = $this->archetypeEmbeddableGenerator->createFromArchetype(
340
                $archetypeEmbeddableObjectFqn,
341
                $newEmbeddableObjectClassName
342
            );
343
        }
344
345
        return $traitFqns;
346
    }
347
348
    /**
349
     * @param string $entityFqn
350
     * @param array  $embeddableTraitFqns
351
     *
352
     * @return Builder
353
     */
354
    public function setEmbeddablesToEntity(string $entityFqn, array $embeddableTraitFqns): self
355
    {
356
        foreach ($embeddableTraitFqns as $embeddableTraitFqn) {
357
            $this->embeddableSetter->setEntityHasEmbeddable($entityFqn, $embeddableTraitFqn);
358
        }
359
360
        return $this;
361
    }
362
363 1
    public function setEnumOptionsOnInterface(string $interfaceFqn, array $options): void
364
    {
365 1
        $pathToInterface = (new ReflectionClass($interfaceFqn))->getFileName();
366 1
        $basename        = basename($pathToInterface);
367 1
        $classy          = substr($basename, 0, strpos($basename, 'FieldInterface'));
368 1
        $consty          = $this->codeHelper->consty($classy);
369 1
        $interface       = PhpInterface::fromFile($pathToInterface);
370 1
        $constants       = $interface->getConstants();
371
        $constants->map(static function (PhpConstant $constant) use ($interface, $consty) {
372 1
            if (0 === strpos($constant->getName(), $consty . '_OPTION')) {
373 1
                $interface->removeConstant($constant);
374
            }
375 1
            if (0 === strpos($constant->getName(), 'DEFAULT')) {
376 1
                $interface->removeConstant($constant);
377
            }
378 1
        });
379 1
        $optionConsts = [];
380 1
        foreach ($options as $option) {
381 1
            $name           = preg_replace(
382 1
                '%_{2,}%',
383 1
                '_',
384 1
                $consty . '_OPTION_' . $this->codeHelper->consty(
385 1
                    preg_replace('%[^a-z0-9]%i', '_', $option)
386
                )
387
            );
388 1
            $optionConsts[] = 'self::' . $name;
389 1
            $constant       = new PhpConstant($name, $option);
390 1
            $interface->setConstant($constant);
391
        }
392 1
        $interface->setConstant(
393 1
            new PhpConstant(
394 1
                $consty . '_OPTIONS',
395 1
                '[' . implode(",\n", $optionConsts) . ']',
396 1
                true
397
            )
398
        );
399 1
        $interface->setConstant(
400 1
            new PhpConstant(
401 1
                'DEFAULT_' . $consty,
402 1
                current($optionConsts),
403 1
                true
404
            )
405
        );
406 1
        $this->codeHelper->generate($interface, $pathToInterface);
407 1
    }
408
409
    public function injectTraitInToClass(string $traitFqn, string $classFqn): void
410
    {
411
        $classFilePath = $this->getFileName($classFqn);
412
        $class         = PhpClass::fromFile($classFilePath);
413
        $trait         = PhpTrait::fromFile($this->getFileName($traitFqn));
414
        $traits        = $class->getTraits();
415
        $exists        = array_search($traitFqn, $traits, true);
416
        if ($exists !== false) {
417
            return;
418
        }
419
        $class->addTrait($trait);
420
        $this->codeHelper->generate($class, $classFilePath);
421
    }
422
423
    private function getFileName(string $typeFqn): string
424
    {
425
        $reflectionClass = new ReflectionClass($typeFqn);
426
427
        return $reflectionClass->getFileName();
428
    }
429
430
    public function extendInterfaceWithInterface(string $interfaceToExtendFqn, string $interfaceToAddFqn): void
431
    {
432
        $toExtendFilePath = $this->getFileName($interfaceToExtendFqn);
433
        $toExtend         = PhpInterface::fromFile($toExtendFilePath);
434
        $toAdd            = PhpInterface::fromFile($this->getFileName($interfaceToAddFqn));
435
        $exists           = $toExtend->getInterfaces()->contains($interfaceToAddFqn);
436
        if ($exists !== false) {
437
            return;
438
        }
439
        $toExtend->addInterface($toAdd);
440
        $this->codeHelper->generate($toExtend, $toExtendFilePath);
441
    }
442
443
    public function removeIdTraitFromClass(string $classFqn): void
444
    {
445
        $traitFqn = "DSM\\Fields\\Traits\\PrimaryKey\\IdFieldTrait";
446
        $this->removeTraitFromClass($classFqn, $traitFqn);
447
    }
448
449
    public function removeTraitFromClass(string $classFqn, string $traitFqn): void
450
    {
451
        $classPath = $this->getFileName($classFqn);
452
        $class     = PhpClass::fromFile($classPath);
453
        $traits    = $class->getTraits();
454
        if ($class->getUseStatements()->contains($traitFqn) === true) {
455
            $class->removeUseStatement($traitFqn);
456
        }
457
        $index = array_search($traitFqn, $traits, true);
458
        if ($index === false) {
459
            $shortNameParts = explode('\\', $traitFqn);
460
            $shortName      = (string)array_pop($shortNameParts);
461
            $index          = array_search($shortName, $traits, true);
462
        }
463
        if ($index === false) {
464
            return;
465
        }
466
        unset($traits[$index]);
467
        $reflectionClass = new ReflectionClass(PhpClass::class);
468
        $property        = $reflectionClass->getProperty('traits');
469
        $property->setAccessible(true);
470
        $property->setValue($class, $traits);
471
        $this->codeHelper->generate($class, $classPath);
472
    }
473
}
474