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 (#214)
by joseph
21:10
created

MappingHelper   B

Complexity

Total Complexity 49

Size/Duplication

Total Lines 579
Duplicated Lines 0 %

Test Coverage

Coverage 97.19%

Importance

Changes 2
Bugs 0 Features 0
Metric Value
eloc 236
dl 0
loc 579
ccs 173
cts 178
cp 0.9719
rs 8.48
c 2
b 0
f 0
wmc 49

19 Methods

Rating   Name   Duplication   Size   Complexity  
A getSingularForFqn() 0 6 1
A backticks() 0 3 1
A getTableNameForEntityFqn() 0 14 2
A getPluralForFqn() 0 10 2
A getColumnNameForField() 0 3 1
A pluralize() 0 8 2
A setSimpleObjectFields() 0 18 2
A getShortNameForFqn() 0 3 1
A setSimpleFields() 0 7 2
A singularize() 0 8 2
A setSimpleArrayFields() 0 18 2
A setSimpleTextFields() 0 23 4
A setSimpleIntegerFields() 0 26 4
A setSimpleStringFields() 0 27 4
A setSimpleBooleanFields() 0 24 4
A setSimpleFloatFields() 0 24 4
B setSimpleDecimalFields() 0 32 6
A setSimpleDatetimeFields() 0 21 3
A getType() 0 8 2

How to fix   Complexity   

Complex Class

Complex classes like MappingHelper often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use MappingHelper, and based on these observations, apply Extract Interface, too.

1
<?php declare(strict_types=1);
2
3
namespace EdmondsCommerce\DoctrineStaticMeta;
4
5
use DateTimeImmutable;
6
use Doctrine\Common\Inflector\Inflector;
7
use Doctrine\DBAL\Types\Type;
8
use Doctrine\ORM\Mapping\Builder\ClassMetadataBuilder;
9
use Doctrine\ORM\Mapping\Builder\FieldBuilder;
10
use EdmondsCommerce\DoctrineStaticMeta\CodeGeneration\NamespaceHelper;
11
use EdmondsCommerce\DoctrineStaticMeta\CodeGeneration\TypeHelper;
12
use EdmondsCommerce\DoctrineStaticMeta\Schema\Database;
13
use InvalidArgumentException;
14
use Ramsey\Uuid\Doctrine\UuidBinaryOrderedTimeType;
15
use Ramsey\Uuid\Doctrine\UuidBinaryType;
16
use Ramsey\Uuid\Doctrine\UuidType;
17
use function is_bool;
18
use function is_float;
19
use function is_int;
20
use function is_string;
21
use function str_replace;
22
use function strlen;
23
24
/**
25
 * Class MappingHelper
26
 *
27
 * Helper functions to assist with setting up Doctrine mapping meta data
28
 *
29
 * @package EdmondsCommerce\DoctrineStaticMeta
30
 *
31
 * @SuppressWarnings(PHPMD.StaticAccess)
32
 * @SuppressWarnings(PHPMD.BooleanArgumentFlag)
33
 */
34
class MappingHelper
35
{
36
37
    /**
38
     * Primary Key types (beyond the common types)
39
     */
40
    public const TYPE_UUID                    = UuidBinaryOrderedTimeType::NAME;
41
    public const TYPE_NON_BINARY_UUID         = UuidType::NAME;
42
    public const TYPE_NON_ORDERED_BINARY_UUID = UuidBinaryType::NAME;
43
44
    /**
45
     * Quick accessors for common types that are supported by methods in this helper
46
     *
47
     * Note this is not all of the types supported by Doctrine
48
     *
49
     * Each of these methods has a corresponding `setSimple{Type}Fields` method
50
     */
51
    public const TYPE_STRING   = 'string';
52
    public const TYPE_DATETIME = 'datetime';// actually datetime is implemented as datetime_immutable
53
    public const TYPE_FLOAT    = 'float';
54
    public const TYPE_DECIMAL  = 'decimal';
55
    public const TYPE_INTEGER  = 'integer';
56
    public const TYPE_TEXT     = 'text';
57
    public const TYPE_BOOLEAN  = 'boolean';
58
    public const TYPE_ARRAY    = 'array';
59
    public const TYPE_OBJECT   = 'object';
60
61
62
    /**
63
     * This is the list of common types that mapping helper fully supports with a `setSimple{Type}Fields` method
64
     */
65
    public const COMMON_TYPES = [
66
        self::TYPE_STRING,
67
        self::TYPE_DATETIME,
68
        self::TYPE_FLOAT,
69
        self::TYPE_DECIMAL,
70
        self::TYPE_INTEGER,
71
        self::TYPE_TEXT,
72
        self::TYPE_BOOLEAN,
73
        self::TYPE_ARRAY,
74
        self::TYPE_OBJECT,
75
    ];
76
77
    /**
78
     * Which types do we support marking as unique
79
     */
80
    public const UNIQUEABLE_TYPES = [
81
        self::TYPE_STRING,
82
        self::TYPE_INTEGER,
83
    ];
84
85
    public const PHP_TYPE_STRING   = 'string';
86
    public const PHP_TYPE_DATETIME = '\\' . DateTimeImmutable::class;
87
    public const PHP_TYPE_FLOAT    = 'float';
88
    public const PHP_TYPE_INTEGER  = 'int';
89
    public const PHP_TYPE_BOOLEAN  = 'bool';
90
    public const PHP_TYPE_ARRAY    = 'array';
91
    public const PHP_TYPE_OBJECT   = 'object';
92
93
    public const PHP_TYPES = [
94
        self::PHP_TYPE_STRING,
95
        self::PHP_TYPE_DATETIME,
96
        self::PHP_TYPE_FLOAT,
97
        self::PHP_TYPE_INTEGER,
98
        self::PHP_TYPE_BOOLEAN,
99
        self::PHP_TYPE_ARRAY,
100
        self::PHP_TYPE_OBJECT,
101
    ];
102
103
    /**
104
     * The PHP type associated with the mapping type
105
     */
106
    public const COMMON_TYPES_TO_PHP_TYPES = [
107
        self::TYPE_STRING   => self::PHP_TYPE_STRING,
108
        self::TYPE_DATETIME => self::PHP_TYPE_DATETIME,
109
        self::TYPE_FLOAT    => self::PHP_TYPE_FLOAT,
110
        self::TYPE_DECIMAL  => self::PHP_TYPE_STRING,
111
        self::TYPE_INTEGER  => self::PHP_TYPE_INTEGER,
112
        self::TYPE_TEXT     => self::PHP_TYPE_STRING,
113
        self::TYPE_BOOLEAN  => self::PHP_TYPE_BOOLEAN,
114
        self::TYPE_ARRAY    => self::PHP_TYPE_ARRAY,
115
        self::TYPE_OBJECT   => self::PHP_TYPE_OBJECT,
116
    ];
117
118
    /**
119
     * This is the full list of mapping types
120
     *
121
     * @see \Doctrine\DBAL\Types\Type
122
     */
123
    public const ALL_DBAL_TYPES = [
124
        Type::TARRAY,
125
        Type::SIMPLE_ARRAY,
126
        Type::JSON,
127
        Type::JSON_ARRAY,
128
        Type::BIGINT,
129
        Type::BOOLEAN,
130
        Type::DATETIME,
131
        Type::DATETIME_IMMUTABLE,
132
        Type::DATETIMETZ,
133
        Type::DATETIMETZ_IMMUTABLE,
134
        Type::DATE,
135
        Type::DATE_IMMUTABLE,
136
        Type::TIME,
137
        Type::TIME_IMMUTABLE,
138
        Type::DECIMAL,
139
        Type::INTEGER,
140
        Type::OBJECT,
141
        Type::SMALLINT,
142
        Type::STRING,
143
        Type::TEXT,
144
        Type::BINARY,
145
        Type::BLOB,
146
        Type::FLOAT,
147
        Type::GUID,
148
        Type::DATEINTERVAL,
149
    ];
150
151
    public const MIXED_TYPES = [
152
        // Doctrine hydrates decimal values as strings.
153
        // However, setting these using an int or float is also valid.
154
        self::TYPE_DECIMAL,
155
    ];
156
157
    /**
158
     * @param string $entityFqn
159
     *
160
     * @return string
161
     */
162 1
    public static function getPluralForFqn(string $entityFqn): string
163
    {
164 1
        $singular = self::getSingularForFqn($entityFqn);
165
166 1
        $plural = self::pluralize($singular);
167 1
        if ($plural === $singular) {
168
            $plural = $singular . 's';
169
        }
170
171 1
        return $plural;
172
    }
173
174
    /**
175
     * @param string $entityFqn
176
     *
177
     * @return string
178
     */
179 14
    public static function getSingularForFqn(string $entityFqn): string
180
    {
181 14
        $shortName = self::getShortNameForFqn($entityFqn);
182 14
        $singular  = self::singularize($shortName);
183
184 14
        return lcfirst($singular);
185
    }
186
187
    /**
188
     * @param string $entityFqn
189
     *
190
     * @return string
191
     */
192 14
    public static function getShortNameForFqn(string $entityFqn): string
193
    {
194 14
        return substr($entityFqn, strrpos($entityFqn, '\\') + 1);
195
    }
196
197 14
    public static function singularize(string $item): string
198
    {
199 14
        $singular = Inflector::singularize($item);
200 14
        if ('datum' === strtolower(substr($singular, -5))) {
201 1
            $singular = $item;
202
        }
203
204 14
        return $singular;
205
    }
206
207 1
    public static function pluralize(string $item): string
208
    {
209 1
        $plural = Inflector::pluralize($item);
210 1
        if ($plural === $item) {
211 1
            $plural = $item . 's';
212
        }
213
214 1
        return $plural;
215
    }
216
217
    /**
218
     * @param string $entityFqn
219
     *
220
     * @return string
221
     */
222 2
    public static function getTableNameForEntityFqn(
223
        string $entityFqn
224
    ): string {
225 2
        $namespaceHelper = new NamespaceHelper();
226 2
        $subFqn          = $namespaceHelper->getEntitySubNamespace(
227 2
            $entityFqn
228
        );
229 2
        $tableName       = str_replace('\\', '', $subFqn);
230 2
        $tableName       = self::backticks(Inflector::tableize($tableName));
231 2
        if (strlen($tableName) > Database::MAX_IDENTIFIER_LENGTH) {
232
            $tableName = substr($tableName, -Database::MAX_IDENTIFIER_LENGTH);
233
        }
234
235 2
        return $tableName;
236
    }
237
238
    /**
239
     * Wrap the name in backticks
240
     *
241
     * @param string $name
242
     *
243
     * @return string
244
     */
245 3
    public static function backticks(string $name): string
246
    {
247 3
        return '`' . $name . '`';
248
    }
249
250
    /**
251
     * Set bog standard string fields quickly in bulk
252
     *
253
     * @param array                $fields
254
     * @param ClassMetadataBuilder $builder
255
     * @param mixed                $default
256
     * @param bool                 $isUnique
257
     * In this case the boolean argument is simply data
258
     * @SuppressWarnings(PHPMD.BooleanArgumentFlag)
259
     */
260 2
    public static function setSimpleStringFields(
261
        array $fields,
262
        ClassMetadataBuilder $builder,
263
        $default = null,
264
        bool $isUnique = false
265
    ): void {
266 2
        if (null !== $default && !is_string($default)) {
267 1
            throw new InvalidArgumentException(
268 1
                'Invalid default value ' . $default
269 1
                . ' with type ' . self::getType($default)
270
            );
271
        }
272 1
        foreach ($fields as $field) {
273 1
            $fieldBuilder = new FieldBuilder(
274 1
                $builder,
275
                [
276 1
                    'fieldName' => $field,
277 1
                    'type'      => Type::STRING,
278 1
                    'default'   => $default,
279
                ]
280
            );
281
            $fieldBuilder
282 1
                ->columnName(self::getColumnNameForField($field))
283 1
                ->nullable(null === $default)
284 1
                ->unique($isUnique)
285 1
                ->length(Database::MAX_VARCHAR_LENGTH)
286 1
                ->build();
287
        }
288 1
    }
289
290 5
    private static function getType($var): string
291
    {
292 5
        static $typeHelper;
293 5
        if (null === $typeHelper) {
294 1
            $typeHelper = new TypeHelper();
295
        }
296
297 5
        return $typeHelper->getType($var);
298
    }
299
300
    /**
301
     * Get the properly backticked and formatted column name for a field
302
     *
303
     * @param string $field
304
     *
305
     * @return string
306
     */
307 2
    public static function getColumnNameForField(string $field): string
308
    {
309 2
        return self::backticks(Inflector::tableize($field));
310
    }
311
312
    /**
313
     * Set bog standard text fields quickly in bulk
314
     *
315
     * @param array                $fields
316
     * @param ClassMetadataBuilder $builder
317
     * @param mixed                $default
318
     * In this case the boolean argument is simply data
319
     * @SuppressWarnings(PHPMD.BooleanArgumentFlag)
320
     */
321 2
    public static function setSimpleTextFields(
322
        array $fields,
323
        ClassMetadataBuilder $builder,
324
        $default = null
325
    ): void {
326 2
        if (null !== $default && !is_string($default)) {
327 1
            throw new InvalidArgumentException(
328 1
                'Invalid default value ' . $default
329 1
                . ' with type ' . self::getType($default)
330
            );
331
        }
332 1
        foreach ($fields as $field) {
333 1
            $fieldBuilder = new FieldBuilder(
334 1
                $builder,
335
                [
336 1
                    'fieldName' => $field,
337 1
                    'type'      => Type::TEXT,
338 1
                    'default'   => $default,
339
                ]
340
            );
341 1
            $fieldBuilder->columnName(self::getColumnNameForField($field))
342 1
                         ->nullable(null === $default)
343 1
                         ->build();
344
        }
345 1
    }
346
347
348
    /**
349
     * Set bog standard float fields quickly in bulk
350
     *
351
     * @param array                $fields
352
     * @param ClassMetadataBuilder $builder
353
     * @param mixed                $default
354
     * In this case the boolean argument is simply data
355
     * @SuppressWarnings(PHPMD.BooleanArgumentFlag)
356
     */
357 2
    public static function setSimpleFloatFields(
358
        array $fields,
359
        ClassMetadataBuilder $builder,
360
        $default = null
361
    ): void {
362 2
        if (null !== $default && !is_float($default)) {
363 1
            throw new InvalidArgumentException(
364 1
                'Invalid default value ' . $default
365 1
                . ' with type ' . self::getType($default)
366
            );
367
        }
368 1
        foreach ($fields as $field) {
369 1
            $fieldBuilder = new FieldBuilder(
370 1
                $builder,
371
                [
372 1
                    'fieldName' => $field,
373 1
                    'type'      => Type::FLOAT,
374 1
                    'default'   => $default,
375
                ]
376
            );
377
            $fieldBuilder
378 1
                ->columnName(self::getColumnNameForField($field))
379 1
                ->nullable(null === $default)
380 1
                ->build();
381
        }
382 1
    }
383
384
    /**
385
     * Set bog standard decimal fields quickly in bulk
386
     *
387
     * @param array                $fields
388
     * @param ClassMetadataBuilder $builder
389
     * @param mixed                $default
390
     * In this case the boolean argument is simply data
391
     * @SuppressWarnings(PHPMD.BooleanArgumentFlag)
392
     */
393 2
    public static function setSimpleDecimalFields(
394
        array $fields,
395
        ClassMetadataBuilder $builder,
396
        $default = null
397
    ): void {
398 2
        if (null !== $default && !is_string($default)) {
399
            throw new InvalidArgumentException(
400
                'Invalid default value ' . $default
401
                . ' with type ' . self::getType($default)
402
            );
403
        }
404 2
        if (null !== $default && !is_numeric($default)) {
405 1
            throw new InvalidArgumentException(
406 1
                'Invalid default value ' . $default
407 1
                . ', even though it is a string, it must be numeric '
408
            );
409
        }
410 1
        foreach ($fields as $field) {
411 1
            $fieldBuilder = new FieldBuilder(
412 1
                $builder,
413
                [
414 1
                    'fieldName' => $field,
415 1
                    'type'      => Type::DECIMAL,
416 1
                    'default'   => (string)(float)$default,
417
                ]
418
            );
419
            $fieldBuilder
420 1
                ->columnName(self::getColumnNameForField($field))
421 1
                ->nullable(null === $default)
422 1
                ->precision(Database::MAX_DECIMAL_PRECISION)
423 1
                ->scale(Database::MAX_DECIMAL_SCALE)
424 1
                ->build();
425
        }
426 1
    }
427
428
    /**
429
     * Set bog standard dateTime fields quickly in bulk
430
     *
431
     * @param array                $fields
432
     * @param ClassMetadataBuilder $builder
433
     * @param mixed                $default
434
     * In this case the boolean argument is simply data
435
     * @SuppressWarnings(PHPMD.BooleanArgumentFlag)
436
     */
437 2
    public static function setSimpleDatetimeFields(
438
        array $fields,
439
        ClassMetadataBuilder $builder,
440
        $default = null
441
    ): void {
442 2
        if (null !== $default) {
443 1
            throw new InvalidArgumentException('DateTime currently only support null as a default value');
444
        }
445 1
        foreach ($fields as $field) {
446 1
            $fieldBuilder = new FieldBuilder(
447 1
                $builder,
448
                [
449 1
                    'fieldName' => $field,
450 1
                    'type'      => Type::DATETIME_IMMUTABLE,
451 1
                    'default'   => $default,
452
                ]
453
            );
454
            $fieldBuilder
455 1
                ->columnName(self::getColumnNameForField($field))
456 1
                ->nullable(null === $default)
457 1
                ->build();
458
        }
459 1
    }
460
461
    /**
462
     * @param array                $fields
463
     * @param ClassMetadataBuilder $builder
464
     * @param null                 $default
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $default is correct as it would always require null to be passed?
Loading history...
465
     * @param bool                 $isUnique
466
     */
467 2
    public static function setSimpleIntegerFields(
468
        array $fields,
469
        ClassMetadataBuilder $builder,
470
        $default = null,
471
        bool $isUnique = false
472
    ): void {
473 2
        if (null !== $default && !is_int($default)) {
0 ignored issues
show
introduced by
The condition null !== $default is always false.
Loading history...
474 1
            throw new InvalidArgumentException(
475 1
                'Invalid default value ' . $default
476 1
                . ' with type ' . self::getType($default)
477
            );
478
        }
479 1
        foreach ($fields as $field) {
480 1
            $fieldBuilder = new FieldBuilder(
481 1
                $builder,
482
                [
483 1
                    'fieldName' => $field,
484 1
                    'type'      => Type::INTEGER,
485 1
                    'default'   => $default,
486
                ]
487
            );
488
            $fieldBuilder
489 1
                ->columnName(self::getColumnNameForField($field))
490 1
                ->nullable(null === $default)
491 1
                ->unique($isUnique)
492 1
                ->build();
493
        }
494 1
    }
495
496
    /**
497
     * Set bog standard boolean fields quickly in bulk
498
     *
499
     * @param array                $fields
500
     * @param ClassMetadataBuilder $builder
501
     * @param mixed                $default
502
     * In this case the boolean argument is simply data
503
     * @SuppressWarnings(PHPMD.BooleanArgumentFlag)
504
     */
505 2
    public static function setSimpleBooleanFields(
506
        array $fields,
507
        ClassMetadataBuilder $builder,
508
        $default = null
509
    ): void {
510 2
        if (null !== $default && !is_bool($default)) {
511 1
            throw new InvalidArgumentException(
512 1
                'Invalid default value ' . $default
513 1
                . ' with type ' . self::getType($default)
514
            );
515
        }
516 1
        foreach ($fields as $field) {
517 1
            $fieldBuilder = new FieldBuilder(
518 1
                $builder,
519
                [
520 1
                    'fieldName' => $field,
521 1
                    'type'      => Type::BOOLEAN,
522 1
                    'default'   => $default,
523
                ]
524
            );
525
            $fieldBuilder
526 1
                ->columnName(self::getColumnNameForField($field))
527 1
                ->nullable(null === $default)
528 1
                ->build();
529
        }
530 1
    }
531
532
    /**
533
     * Create JSON Array fields
534
     *
535
     * Will use real JSON in the DB engine if it is supported
536
     *
537
     * This should be used for any structured data, arrays, lists, simple objects
538
     *
539
     * @param array                $fields
540
     * @param ClassMetadataBuilder $builder
541
     * @param array|null           $default
542
     */
543 1
    public static function setSimpleArrayFields(
544
        array $fields,
545
        ClassMetadataBuilder $builder,
546
        ?array $default = null
547
    ): void {
548 1
        foreach ($fields as $field) {
549 1
            $fieldBuilder = new FieldBuilder(
550 1
                $builder,
551
                [
552 1
                    'fieldName' => $field,
553 1
                    'type'      => Type::JSON_ARRAY,
554 1
                    'default'   => $default,
555
                ]
556
            );
557
            $fieldBuilder
558 1
                ->columnName(self::getColumnNameForField($field))
559 1
                ->nullable(null === $default)
560 1
                ->build();
561
        }
562 1
    }
563
564
    /**
565
     * Create JSON Object fields
566
     *
567
     * Will use real JSON in the DB engine if it is supported
568
     *
569
     * This should be used for any structured data, arrays, lists, simple objects
570
     *
571
     * @param array                $fields
572
     * @param ClassMetadataBuilder $builder
573
     * @param object|null          $default
574
     */
575 1
    public static function setSimpleObjectFields(
576
        array $fields,
577
        ClassMetadataBuilder $builder,
578
        ?object $default = null
579
    ): void {
580 1
        foreach ($fields as $field) {
581 1
            $fieldBuilder = new FieldBuilder(
582 1
                $builder,
583
                [
584 1
                    'fieldName' => $field,
585 1
                    'type'      => Type::JSON,
586 1
                    'default'   => $default,
587
                ]
588
            );
589
            $fieldBuilder
590 1
                ->columnName(self::getColumnNameForField($field))
591 1
                ->nullable(null === $default)
592 1
                ->build();
593
        }
594 1
    }
595
596
    /**
597
     * Bulk create multiple fields of different simple types
598
     *
599
     * Always creates nullable fields, if you want to set a default, you must call the type based method
600
     *
601
     * @param array                $fieldToType [
602
     *                                          'fieldName'=>'fieldSimpleType'
603
     *                                          ]
604
     * @param ClassMetadataBuilder $builder
605
     */
606 1
    public static function setSimpleFields(
607
        array $fieldToType,
608
        ClassMetadataBuilder $builder
609
    ): void {
610 1
        foreach ($fieldToType as $field => $type) {
611 1
            $method = "setSimple$type" . 'fields';
612 1
            static::$method([$field], $builder);
613
        }
614 1
    }
615
}
616