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 (#153)
by joseph
11:47
created

Builder::injectTraitInToClass()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 12
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

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