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