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.

MappingHelper   B
last analyzed

Complexity

Total Complexity 49

Size/Duplication

Total Lines 579
Duplicated Lines 0 %

Test Coverage

Coverage 97.19%

Importance

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

19 Methods

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