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.
Passed
Pull Request — master (#45)
by joseph
02:32
created

FieldGenerator::getPropertyMetaMethod()   B

Complexity

Conditions 4
Paths 8

Size

Total Lines 38
Code Lines 25

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
dl 0
loc 38
rs 8.5806
c 0
b 0
f 0
eloc 25
nc 8
nop 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 $traitNamespace;
52
53
    protected $interfaceNamespace;
54
55
    public const STANDARD_FIELDS = [
56
        // Attribute
57
        IpAddressFieldTrait::class,
58
        LabelFieldTrait::class,
59
        NameFieldTrait::class,
60
        QtyFieldTrait::class,
61
        // Date
62
        ActionedDateFieldTrait::class,
63
        ActivatedDateFieldTrait::class,
64
        CompletedDateFieldTrait::class,
65
        DeactivatedDateFieldTrait::class,
66
        TimestampFieldTrait::class,
67
        // Flag
68
        ApprovedFieldTrait::class,
69
        DefaultFieldTrait::class,
70
        // Person
71
        EmailFieldTrait::class,
72
        YearOfBirthFieldTrait::class,
73
    ];
74
75
    /**
76
     * @param string $fieldFqn
77
     * @param string $entityFqn
78
     *
79
     * @throws DoctrineStaticMetaException
80
     * @SuppressWarnings(PHPMD.StaticAccess)
81
     */
82
    public function setEntityHasField(string $entityFqn, string $fieldFqn): void
83
    {
84
        try {
85
            $entityReflection         = new \ReflectionClass($entityFqn);
86
            $entity                   = PhpClass::fromFile($entityReflection->getFileName());
87
            $fieldReflection          = new \ReflectionClass($fieldFqn);
88
            $field                    = PhpTrait::fromFile($fieldReflection->getFileName());
89
            $fieldInterfaceFqn        = \str_replace(
90
                ['Traits', 'Trait'],
91
                ['Interfaces', 'Interface'],
92
                $fieldFqn
93
            );
94
            $fieldInterfaceReflection = new \ReflectionClass($fieldInterfaceFqn);
95
            $fieldInterface           = PhpInterface::fromFile($fieldInterfaceReflection->getFileName());
96
        } catch (\Exception $e) {
97
            throw new DoctrineStaticMetaException(
98
                'Failed loading the entity or field from FQN: '.$e->getMessage(),
99
                $e->getCode(),
100
                $e
101
            );
102
        }
103
        $entity->addTrait($field);
104
        $entity->addInterface($fieldInterface);
105
        $this->codeHelper->generate($entity, $entityReflection->getFileName());
106
    }
107
108
109
    /**
110
     * Generate a new Field based on a property name and Doctrine Type
111
     *
112
     * @see MappingHelper::ALL_DBAL_TYPES for the full list of Dbal Types
113
     *
114
     * @param string      $fieldFqn
115
     * @param string      $dbalType
116
     * @param null|string $phpType
117
     *
118
     * @param bool        $isNullable
119
     * @param bool        $isUnique
120
     *
121
     * @return string - The Fully Qualified Name of the generated Field Trait
122
     *
123
     * @throws DoctrineStaticMetaException
124
     * @SuppressWarnings(PHPMD.StaticAccess)
125
     * @SuppressWarnings(PHPMD.BooleanArgumentFlag)
126
     */
127
    public function generateField(
128
        string $fieldFqn,
129
        string $dbalType,
130
        ?string $phpType = null,
131
        bool $isNullable = true,
132
        bool $isUnique = false
133
    ): string {
134
        if (false === strpos($fieldFqn, AbstractGenerator::ENTITY_FIELD_TRAIT_NAMESPACE)) {
135
            throw new \InvalidArgumentException(
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($isNullable);
184
185
        return $this->generateTrait($isNullable, $isUnique);
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
     * @SuppressWarnings(PHPMD.BooleanArgumentFlag)
221
     */
222
    protected function generateInterface(bool $isNullable): void
223
    {
224
        $filePath = $this->fieldsInterfacePath.'/'.$this->classy.'FieldInterface.php';
225
        $this->assertFileDoesNotExist($filePath, 'Interface');
226
        try {
227
            $this->fileSystem->copy(
228
                $this->codeHelper->resolvePath(static::FIELD_INTERFACE_TEMPLATE_PATH),
229
                $filePath
230
            );
231
            $this->interfacePostCopy($filePath);
232
            $this->codeHelper->replaceTypeHintsInFile(
233
                $filePath,
234
                $this->phpType,
235
                $this->dbalType,
236
                $isNullable
237
            );
238
        } catch (\Exception $e) {
239
            throw new DoctrineStaticMetaException('Error in '.__METHOD__.': '.$e->getMessage(), $e->getCode(), $e);
240
        }
241
    }
242
243
    /**
244
     * @param string $filePath
245
     *
246
     * @throws \RuntimeException
247
     * @throws DoctrineStaticMetaException
248
     */
249
    protected function postCopy(string $filePath): void
250
    {
251
        $this->fileCreationTransaction::setPathCreated($filePath);
252
        $this->replaceName(
253
            $this->classy,
254
            $filePath,
255
            static::FIND_ENTITY_FIELD_NAME
256
        );
257
        $this->findReplace('TEMPLATE_FIELD_NAME', $this->consty, $filePath);
258
        $this->codeHelper->tidyNamespacesInFile($filePath);
259
        $this->setGetterToIsForBools($filePath);
260
    }
261
262
    /**
263
     * @param string $filePath
264
     *
265
     * @throws \RuntimeException
266
     * @throws DoctrineStaticMetaException
267
     */
268
    protected function traitPostCopy(string $filePath): void
269
    {
270
        $this->replaceFieldTraitNamespace($this->traitNamespace, $filePath);
271
        $this->replaceFieldInterfaceNamespace($this->interfaceNamespace, $filePath);
272
        $this->postCopy($filePath);
273
    }
274
275
    protected function setGetterToIsForBools(string $filePath): void
276
    {
277
        if ($this->phpType !== 'bool') {
278
            return;
279
        }
280
        $this->findReplace(' get', ' is', $filePath);
281
        $this->findReplace(' isIs', ' is', $filePath);
282
        $this->findReplace(' isis', ' is', $filePath);
283
    }
284
285
    /**
286
     * @param string $filePath
287
     *
288
     * @throws \RuntimeException
289
     * @throws DoctrineStaticMetaException
290
     */
291
    protected function interfacePostCopy(string $filePath): void
292
    {
293
        $this->replaceFieldInterfaceNamespace($this->interfaceNamespace, $filePath);
294
        $this->postCopy($filePath);
295
    }
296
297
    /**
298
     * @param bool $isNullable
299
     * @param bool $isUnique
300
     *
301
     * @return string
302
     * @throws DoctrineStaticMetaException
303
     * @SuppressWarnings(PHPMD.StaticAccess)
304
     * @SuppressWarnings(PHPMD.BooleanArgumentFlag)
305
     */
306
    protected function generateTrait(bool $isNullable, bool $isUnique): string
307
    {
308
        $filePath = $this->fieldsPath.'/'.$this->classy.'FieldTrait.php';
309
        $this->assertFileDoesNotExist($filePath, 'Trait');
310
        try {
311
            $this->fileSystem->copy(
312
                $this->codeHelper->resolvePath(static::FIELD_TRAIT_TEMPLATE_PATH),
313
                $filePath
314
            );
315
            $this->fileCreationTransaction::setPathCreated($filePath);
316
            $this->traitPostCopy($filePath);
317
            $trait = PhpTrait::fromFile($filePath);
318
            $trait->setMethod($this->getPropertyMetaMethod($isNullable, $isUnique));
319
            $trait->addUseStatement('\\'.MappingHelper::class);
320
            $trait->addUseStatement('\\'.ClassMetadataBuilder::class);
321
            $this->codeHelper->generate($trait, $filePath);
322
            $this->codeHelper->replaceTypeHintsInFile(
323
                $filePath,
324
                $this->phpType,
325
                $this->dbalType,
326
                $isNullable
327
            );
328
329
            return $trait->getQualifiedName();
330
        } catch (\Exception $e) {
331
            throw new DoctrineStaticMetaException('Error in '.__METHOD__.': '.$e->getMessage(), $e->getCode(), $e);
332
        }
333
    }
334
335
    /**
336
     *
337
     * @param bool $isNullable
338
     * @param bool $isUnique
339
     *
340
     * @return PhpMethod
341
     * @SuppressWarnings(PHPMD.StaticAccess)
342
     * @SuppressWarnings(PHPMD.BooleanArgumentFlag)
343
     */
344
    protected function getPropertyMetaMethod(bool $isNullable, bool $isUnique): PhpMethod
345
    {
346
        $name   = UsesPHPMetaDataInterface::METHOD_PREFIX_GET_PROPERTY_DOCTRINE_META.$this->classy;
347
        $method = PhpMethod::create($name);
348
        $method->setStatic(true);
349
        $method->setVisibility('public');
350
        $method->setParameters(
351
            [PhpParameter::create('builder')->setType('ClassMetadataBuilder')]
352
        );
353
        $mappingHelperMethodName = 'setSimple'.ucfirst(strtolower($this->dbalType)).'Fields';
354
        $isNullableString        = $isNullable ? 'true' : 'false';
355
        $isUniqueString          = $isUnique ? 'true' : 'false';
356
        $methodBody              = "
357
        MappingHelper::$mappingHelperMethodName(
358
            [{$this->classy}FieldInterface::PROP_{$this->consty}],
359
            \$builder,
360
            $isNullableString
361
        );                        
362
";
363
        if (\in_array($this->dbalType, MappingHelper::UNIQUEABLE_TYPES, true)) {
364
            $methodBody = "
365
        MappingHelper::$mappingHelperMethodName(
366
            [{$this->classy}FieldInterface::PROP_{$this->consty}],
367
            \$builder,
368
            $isNullableString,
369
            $isUniqueString
370
        );                        
371
";
372
        }
373
        $method->setBody($methodBody);
374
        $method->setDocblock(
375
            DocBlock::create()
376
                    ->appendTag(
377
                        UnknownTag::create('SuppressWarnings(PHPMD.StaticAccess)')
378
                    )
379
        );
380
381
        return $method;
382
    }
383
384
    private function assertFileDoesNotExist(string $filePath, string $type): void
385
    {
386
        if (file_exists($filePath)) {
387
            throw new \RuntimeException("Field $type already exists at $filePath");
388
        }
389
    }
390
}
391