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
Push — master ( 8619f1...53d2ad )
by Ross
52s queued 13s
created

FieldGenerator   A

Complexity

Total Complexity 24

Size/Duplication

Total Lines 336
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
dl 0
loc 336
rs 10
c 0
b 0
f 0
wmc 24

13 Methods

Rating   Name   Duplication   Size   Complexity  
B setEntityHasField() 0 24 2
A setGetterToIsForBools() 0 8 2
A postCopy() 0 11 1
B generateField() 0 57 5
A getIsNullable() 0 3 1
A ensurePathExists() 0 6 2
A setIsNullable() 0 5 1
B generateTrait() 0 25 2
A generateInterface() 0 17 2
A interfacePostCopy() 0 4 1
A getPhpTypeForDbalType() 0 11 2
A traitPostCopy() 0 5 1
B getPropertyMetaMethod() 0 28 2
1
<?php declare(strict_types=1);
2
3
namespace EdmondsCommerce\DoctrineStaticMeta\CodeGeneration\Generator;
4
5
use Doctrine\Common\Inflector\Inflector;
6
use Doctrine\ORM\Mapping\Builder\ClassMetadataBuilder;
7
use EdmondsCommerce\DoctrineStaticMeta\Entity\Fields\Traits\Attribute\IpAddressFieldTrait;
8
use EdmondsCommerce\DoctrineStaticMeta\Entity\Fields\Traits\Attribute\LabelFieldTrait;
9
use EdmondsCommerce\DoctrineStaticMeta\Entity\Fields\Traits\Attribute\NameFieldTrait;
10
use EdmondsCommerce\DoctrineStaticMeta\Entity\Fields\Traits\Attribute\QtyFieldTrait;
11
use EdmondsCommerce\DoctrineStaticMeta\Entity\Fields\Traits\Date\ActionedDateFieldTrait;
12
use EdmondsCommerce\DoctrineStaticMeta\Entity\Fields\Traits\Date\ActivatedDateFieldTrait;
13
use EdmondsCommerce\DoctrineStaticMeta\Entity\Fields\Traits\Date\CompletedDateFieldTrait;
14
use EdmondsCommerce\DoctrineStaticMeta\Entity\Fields\Traits\Date\DeactivatedDateFieldTrait;
15
use EdmondsCommerce\DoctrineStaticMeta\Entity\Fields\Traits\Date\TimestampFieldTrait;
16
use EdmondsCommerce\DoctrineStaticMeta\Entity\Fields\Traits\Flag\ApprovedFieldTrait;
17
use EdmondsCommerce\DoctrineStaticMeta\Entity\Fields\Traits\Flag\DefaultFieldTrait;
18
use EdmondsCommerce\DoctrineStaticMeta\Entity\Fields\Traits\Person\EmailFieldTrait;
19
use EdmondsCommerce\DoctrineStaticMeta\Entity\Fields\Traits\Person\YearOfBirthFieldTrait;
20
use EdmondsCommerce\DoctrineStaticMeta\Entity\Interfaces\UsesPHPMetaDataInterface;
21
use EdmondsCommerce\DoctrineStaticMeta\Exception\DoctrineStaticMetaException;
22
use EdmondsCommerce\DoctrineStaticMeta\MappingHelper;
23
use gossi\codegen\model\PhpClass;
24
use gossi\codegen\model\PhpInterface;
25
use gossi\codegen\model\PhpMethod;
26
use gossi\codegen\model\PhpParameter;
27
use gossi\codegen\model\PhpTrait;
28
use gossi\docblock\Docblock;
29
use gossi\docblock\tags\UnknownTag;
30
31
/**
32
 * Class FieldGenerator
33
 *
34
 * @package EdmondsCommerce\DoctrineStaticMeta\CodeGeneration\Generator
35
 * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
36
 */
37
class FieldGenerator extends AbstractGenerator
38
{
39
    protected $fieldsPath;
40
41
    protected $fieldsInterfacePath;
42
43
    protected $classy;
44
45
    protected $consty;
46
47
    protected $phpType;
48
49
    protected $dbalType;
50
51
    protected $isNullable = false;
52
53
    protected $traitNamespace;
54
55
    protected $interfaceNamespace;
56
57
    public const STANDARD_FIELDS = [
58
        // Attribute
59
        IpAddressFieldTrait::class,
60
        LabelFieldTrait::class,
61
        NameFieldTrait::class,
62
        QtyFieldTrait::class,
63
        // Date
64
        ActionedDateFieldTrait::class,
65
        ActivatedDateFieldTrait::class,
66
        CompletedDateFieldTrait::class,
67
        DeactivatedDateFieldTrait::class,
68
        TimestampFieldTrait::class,
69
        // Flag
70
        ApprovedFieldTrait::class,
71
        DefaultFieldTrait::class,
72
        // Person
73
        EmailFieldTrait::class,
74
        YearOfBirthFieldTrait::class,
75
    ];
76
77
    /**
78
     * @param string $fieldFqn
79
     * @param string $entityFqn
80
     *
81
     * @throws DoctrineStaticMetaException
82
     * @SuppressWarnings(PHPMD.StaticAccess)
83
     */
84
    public function setEntityHasField(string $entityFqn, string $fieldFqn): void
85
    {
86
        try {
87
            $entityReflection         = new \ReflectionClass($entityFqn);
88
            $entity                   = PhpClass::fromFile($entityReflection->getFileName());
89
            $fieldReflection          = new \ReflectionClass($fieldFqn);
90
            $field                    = PhpTrait::fromFile($fieldReflection->getFileName());
91
            $fieldInterfaceFqn        = \str_replace(
92
                ['Traits', 'Trait'],
93
                ['Interfaces', 'Interface'],
94
                $fieldFqn
95
            );
96
            $fieldInterfaceReflection = new \ReflectionClass($fieldInterfaceFqn);
97
            $fieldInterface           = PhpInterface::fromFile($fieldInterfaceReflection->getFileName());
98
        } catch (\Exception $e) {
99
            throw new DoctrineStaticMetaException(
100
                'Failed loading the entity or field from FQN: '.$e->getMessage(),
101
                $e->getCode(),
102
                $e
103
            );
104
        }
105
        $entity->addTrait($field);
106
        $entity->addInterface($fieldInterface);
107
        $this->codeHelper->generate($entity, $entityReflection->getFileName());
108
    }
109
110
111
    /**
112
     * Generate a new Field based on a property name and Doctrine Type
113
     *
114
     * @see MappingHelper::ALL_DBAL_TYPES for the full list of Dbal Types
115
     *
116
     * @param string      $fieldFqn
117
     * @param string      $dbalType
118
     * @param null|string $phpType
119
     *
120
     * @return string - The Fully Qualified Name of the generated Field Trait
121
     *
122
     * @throws \Symfony\Component\Filesystem\Exception\IOException
123
     * @throws \InvalidArgumentException
124
     * @throws \RuntimeException
125
     * @throws DoctrineStaticMetaException
126
     * @SuppressWarnings(PHPMD.StaticAccess)
127
     *
128
     */
129
    public function generateField(
130
        string $fieldFqn,
131
        string $dbalType,
132
        ?string $phpType = null
133
    ): string {
134
        if (false === strpos($fieldFqn, AbstractGenerator::ENTITY_FIELD_TRAIT_NAMESPACE)) {
135
            throw new \RuntimeException(
136
                'Fully qualified name [ '.$fieldFqn.' ]'
137
                .' does not include [ '.AbstractGenerator::ENTITY_FIELD_TRAIT_NAMESPACE.' ].'."\n"
138
                .'Please ensure you pass in the full namespace qualified field name'
139
            );
140
        }
141
        if (false === \in_array($dbalType, MappingHelper::ALL_DBAL_TYPES, true)) {
142
            throw new \InvalidArgumentException(
143
                'dbalType must be either null or one of MappingHelper::ALL_DBAL_TYPES'
144
            );
145
        }
146
        if ((null !== $phpType)
147
            && (false === \in_array($phpType, MappingHelper::PHP_TYPES, true))
148
        ) {
149
            throw new \InvalidArgumentException(
150
                'phpType must be either null or one of MappingHelper::PHP_TYPES'
151
            );
152
        }
153
154
        $this->dbalType = $dbalType;
155
        $this->phpType  = $phpType ?? $this->getPhpTypeForDbalType();
156
157
        list($className, $traitNamespace, $traitSubDirectories) = $this->parseFullyQualifiedName(
158
            $fieldFqn,
159
            $this->srcSubFolderName
160
        );
161
162
        list(, $interfaceNamespace, $interfaceSubDirectories) = $this->parseFullyQualifiedName(
163
            str_replace('Traits', 'Interfaces', $fieldFqn),
164
            $this->srcSubFolderName
165
        );
166
167
        $this->fieldsPath = $this->codeHelper->resolvePath(
168
            $this->pathToProjectRoot.'/'.implode('/', $traitSubDirectories)
169
        );
170
171
        $this->fieldsInterfacePath = $this->codeHelper->resolvePath(
172
            $this->pathToProjectRoot.'/'.implode('/', $interfaceSubDirectories)
173
        );
174
175
        $this->classy             = Inflector::classify($className);
176
        $this->consty             = strtoupper(Inflector::tableize($className));
177
        $this->traitNamespace     = $traitNamespace;
178
        $this->interfaceNamespace = $interfaceNamespace;
179
180
        $this->ensurePathExists($this->fieldsPath);
181
        $this->ensurePathExists($this->fieldsInterfacePath);
182
183
        $this->generateInterface();
184
185
        return $this->generateTrait();
186
    }
187
188
    /**
189
     * @param string $path
190
     */
191
    protected function ensurePathExists(string $path): void
192
    {
193
        if ($this->fileSystem->exists($path)) {
194
            return;
195
        }
196
        $this->fileSystem->mkdir($path);
197
    }
198
199
200
    /**
201
     * @return string
202
     * @throws DoctrineStaticMetaException
203
     *
204
     */
205
    protected function getPhpTypeForDbalType(): string
206
    {
207
        if (!\in_array($this->dbalType, MappingHelper::COMMON_TYPES, true)) {
208
            throw new DoctrineStaticMetaException(
209
                'Field type of '.$this->dbalType.' is not one of MappingHelper::COMMON_TYPES'
210
                ."\n\nYou can only use this dbal type if you pass in the explicit phpType as well "
211
                ."\n\nAlternatively, suggest you set the type as string and then edit the generated code as you see fit"
212
            );
213
        }
214
215
        return MappingHelper::COMMON_TYPES_TO_PHP_TYPES[$this->dbalType];
216
    }
217
218
    /**
219
     * @throws DoctrineStaticMetaException
220
     */
221
    protected function generateInterface(): void
222
    {
223
        $filePath = $this->fieldsInterfacePath.'/'.$this->classy.'FieldInterface.php';
224
        try {
225
            $this->fileSystem->copy(
226
                $this->codeHelper->resolvePath(static::FIELD_INTERFACE_TEMPLATE_PATH),
227
                $filePath
228
            );
229
            $this->interfacePostCopy($filePath);
230
            $this->codeHelper->replaceTypeHintsInFile(
231
                $filePath,
232
                $this->phpType,
233
                $this->dbalType,
234
                $this->isNullable
235
            );
236
        } catch (\Exception $e) {
237
            throw new DoctrineStaticMetaException('Error in '.__METHOD__.': '.$e->getMessage(), $e->getCode(), $e);
238
        }
239
    }
240
241
    /**
242
     * @param string $filePath
243
     *
244
     * @throws \RuntimeException
245
     * @throws DoctrineStaticMetaException
246
     */
247
    protected function postCopy(string $filePath): void
248
    {
249
        $this->fileCreationTransaction::setPathCreated($filePath);
250
        $this->replaceName(
251
            $this->classy,
252
            $filePath,
253
            static::FIND_ENTITY_FIELD_NAME
254
        );
255
        $this->findReplace('TEMPLATE_FIELD_NAME', $this->consty, $filePath);
256
        $this->codeHelper->tidyNamespacesInFile($filePath);
257
        $this->setGetterToIsForBools($filePath);
258
    }
259
260
    /**
261
     * @param string $filePath
262
     *
263
     * @throws \RuntimeException
264
     * @throws DoctrineStaticMetaException
265
     */
266
    protected function traitPostCopy(string $filePath): void
267
    {
268
        $this->replaceFieldTraitNamespace($this->traitNamespace, $filePath);
269
        $this->replaceFieldInterfaceNamespace($this->interfaceNamespace, $filePath);
270
        $this->postCopy($filePath);
271
    }
272
273
    protected function setGetterToIsForBools(string $filePath): void
274
    {
275
        if ($this->phpType !== 'bool') {
276
            return;
277
        }
278
        $this->findReplace(' get', ' is', $filePath);
279
        $this->findReplace(' isIs', ' is', $filePath);
280
        $this->findReplace(' isis', ' is', $filePath);
281
    }
282
283
    /**
284
     * @param string $filePath
285
     *
286
     * @throws \RuntimeException
287
     * @throws DoctrineStaticMetaException
288
     */
289
    protected function interfacePostCopy(string $filePath): void
290
    {
291
        $this->replaceFieldInterfaceNamespace($this->interfaceNamespace, $filePath);
292
        $this->postCopy($filePath);
293
    }
294
295
    /**
296
     * @return string
297
     * @throws DoctrineStaticMetaException
298
     * @SuppressWarnings(PHPMD.StaticAccess)
299
     */
300
    protected function generateTrait(): string
301
    {
302
        $filePath = $this->fieldsPath.'/'.$this->classy.'FieldTrait.php';
303
        try {
304
            $this->fileSystem->copy(
305
                $this->codeHelper->resolvePath(static::FIELD_TRAIT_TEMPLATE_PATH),
306
                $filePath
307
            );
308
            $this->fileCreationTransaction::setPathCreated($filePath);
309
            $this->traitPostCopy($filePath);
310
            $trait = PhpTrait::fromFile($filePath);
311
            $trait->setMethod($this->getPropertyMetaMethod());
312
            $trait->addUseStatement('\\'.MappingHelper::class);
313
            $trait->addUseStatement('\\'.ClassMetadataBuilder::class);
314
            $this->codeHelper->generate($trait, $filePath);
315
            $this->codeHelper->replaceTypeHintsInFile(
316
                $filePath,
317
                $this->phpType,
318
                $this->dbalType,
319
                $this->isNullable
320
            );
321
322
            return $trait->getQualifiedName();
323
        } catch (\Exception $e) {
324
            throw new DoctrineStaticMetaException('Error in '.__METHOD__.': '.$e->getMessage(), $e->getCode(), $e);
325
        }
326
    }
327
328
    /**
329
     *
330
     * @return PhpMethod
331
     * @SuppressWarnings(PHPMD.StaticAccess)
332
     */
333
    protected function getPropertyMetaMethod(): PhpMethod
334
    {
335
        $name   = UsesPHPMetaDataInterface::METHOD_PREFIX_GET_PROPERTY_DOCTRINE_META.$this->classy;
336
        $method = PhpMethod::create($name);
337
        $method->setStatic(true);
338
        $method->setVisibility('public');
339
        $method->setParameters(
340
            [PhpParameter::create('builder')->setType('ClassMetadataBuilder')]
341
        );
342
        $mappingHelperMethodName = 'setSimple'.ucfirst(strtolower($this->dbalType)).'Fields';
343
        $isNullableString        = $this->isNullable ? 'true' : 'false';
344
        $method->setBody(
345
            "
346
        MappingHelper::$mappingHelperMethodName(
347
            [{$this->classy}FieldInterface::PROP_{$this->consty}],
348
            \$builder,
349
            $isNullableString
350
        );                        
351
"
352
        );
353
        $method->setDocblock(
354
            DocBlock::create()
355
                    ->appendTag(
356
                        UnknownTag::create('SuppressWarnings(PHPMD.StaticAccess)')
357
                    )
358
        );
359
360
        return $method;
361
    }
362
363
    public function setIsNullable(bool $isNullable): FieldGenerator
364
    {
365
        $this->isNullable = $isNullable;
366
367
        return $this;
368
    }
369
370
    public function getIsNullable(): bool
371
    {
372
        return $this->isNullable;
373
    }
374
}
375