Passed
Pull Request — 1.x (#13)
by
unknown
41:33 queued 26:35
created

MermaidRenderer::render()   F

Complexity

Conditions 17
Paths 678

Size

Total Lines 104
Code Lines 57

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 57
c 1
b 0
f 0
dl 0
loc 104
rs 1.4972
cc 17
nc 678
nop 1

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
declare(strict_types=1);
4
5
namespace Cycle\Schema\Renderer\MermaidRenderer;
6
7
use Cycle\ORM\Relation;
8
use Cycle\ORM\SchemaInterface;
9
use Cycle\Schema\Renderer\MermaidRenderer\Entity\EntityArrow;
10
use Cycle\Schema\Renderer\MermaidRenderer\Entity\EntityTable;
11
use Cycle\Schema\Renderer\MermaidRenderer\Schema\ClassDiagram;
12
use Cycle\Schema\Renderer\SchemaRenderer;
13
use ReflectionException;
14
15
final class MermaidRenderer implements SchemaRenderer
16
{
17
    private const METHOD_FORMAT = '%s: %s';
18
19
    public function render(array $schema): string
20
    {
21
        $class = new ClassDiagram();
22
        $relationMapper = new RelationMapper();
23
24
        $aliases = [];
25
26
        foreach ($schema as $key => $value) {
27
            $role = $key;
28
29
            if (\class_exists($role)) {
30
                $role = $value[SchemaInterface::ROLE];
31
            }
32
33
            $aliases[$key] = $role;
34
        }
35
36
        $aliasCollection = new AliasCollection($aliases);
37
38
        foreach ($schema as $key => $value) {
39
            if (!isset($value[SchemaInterface::COLUMNS])) {
40
                continue;
41
            }
42
43
            $role = $aliasCollection->getAlias($key);
44
45
            $table = new EntityTable($role);
46
            $arrow = new EntityArrow();
47
48
            if (\strpos($key, ':') !== false) {
49
                $table->addAnnotation($key);
50
            }
51
52
            foreach ($value[SchemaInterface::COLUMNS] as $column) {
53
                $typecast = $this->formatTypecast($value[SchemaInterface::TYPECAST][$column] ?? 'string');
54
55
                $table->addRow($typecast, $column);
56
            }
57
58
            foreach ($value[SchemaInterface::RELATIONS] ?? [] as $relationKey => $relation) {
59
                if (!isset($relation[Relation::TARGET], $relation[Relation::SCHEMA])) {
60
                    continue;
61
                }
62
63
                $target = $aliasCollection->getAlias($relation[Relation::TARGET]);
64
65
                if (\class_exists($target)) {
66
                    $target = \lcfirst($this->getClassShortName($target));
67
                }
68
69
                $isNullable = $relation[Relation::SCHEMA][Relation::NULLABLE] ?? false;
70
71
                $mappedRelation = $relationMapper->mapWithNode($relation[Relation::TYPE], $isNullable);
72
73
                switch ($relation[Relation::TYPE]) {
74
                    case Relation::MANY_TO_MANY:
75
                        $throughEntity = $relation[Relation::SCHEMA][Relation::THROUGH_ENTITY] ?? null;
76
77
                        if ($throughEntity) {
78
                            if (\class_exists($throughEntity)) {
79
                                $throughEntity = \lcfirst($this->getClassShortName($throughEntity));
80
                            }
81
82
                            $table->addMethod($relationKey, \sprintf(self::METHOD_FORMAT, $mappedRelation[0], $target));
83
84
                            // tag --* post : posts
85
                            $arrow->addArrow($role, $target, $relationKey, $mappedRelation[1]);
86
                            // postTag ..> tag : tag.posts
87
                            $arrow->addArrow($throughEntity, $role, "$role.$relationKey", '..>');
88
                            // postTag ..> post : tag.posts
89
                            $arrow->addArrow($throughEntity, $target, "$role.$relationKey", '..>');
90
                        }
91
                        break;
92
                    case Relation::EMBEDDED:
93
                        $methodTarget = explode('_', $target);
94
95
                        $prop = isset($methodTarget[2]) ? $methodTarget[2] . '_prop' : $target;
96
97
                        $table->addMethod(
98
                            $prop,
99
                            \sprintf(self::METHOD_FORMAT, $mappedRelation[0], $relation[Relation::TARGET])
100
                        );
101
102
                        $arrow->addArrow($role, $target, $prop, $mappedRelation[1]);
103
                        break;
104
                    default:
105
                        $table->addMethod($relationKey, \sprintf(self::METHOD_FORMAT, $mappedRelation[0], $target));
106
                        $arrow->addArrow($role, $target, $relationKey, $mappedRelation[1]);
107
                }
108
            }
109
110
            foreach ($value[SchemaInterface::CHILDREN] ?? [] as $children) {
111
                $arrow->addArrow($role, $children, 'STI', '--|>');
112
            }
113
114
            if (isset($value[SchemaInterface::PARENT])) {
115
                $arrow->addArrow($value[SchemaInterface::PARENT], $role, 'JTI', '--|>');
116
            }
117
118
            $class->addEntity($table);
119
            $class->addEntity($arrow);
120
        }
121
122
        return (string)$class;
123
    }
124
125
    /**
126
     * @param class-string|object $class
0 ignored issues
show
Documentation Bug introduced by
The doc comment class-string|object at position 0 could not be parsed: Unknown type name 'class-string' at position 0 in class-string|object.
Loading history...
127
     *
128
     * @throws ReflectionException
129
     *
130
     * @return string
131
     */
132
    private function getClassShortName($class): string
133
    {
134
        $className = \is_object($class) ? \get_class($class) : $class;
135
136
        return \substr($className, \strrpos($className, '\\') + 1);
137
    }
138
139
    /**
140
     * @psalm-suppress MissingParamType
141
     */
142
    private function formatTypecast($typecast): string
143
    {
144
        if (\is_array($typecast)) {
145
            $typecast[0] = $this->getClassShortName($typecast[0]);
146
147
            return \sprintf("[%s, '%s']", "$typecast[0]::class", $typecast[1]);
148
        }
149
150
        if ($typecast instanceof \Closure) {
151
            return 'Closure';
152
        }
153
154
        if ($typecast === 'datetime') {
155
            return $typecast;
156
        }
157
158
        if (\class_exists($typecast)) {
159
            return $this->getClassShortName($typecast) . '::class';
160
        }
161
162
        return \htmlentities($typecast);
163
    }
164
}
165