Passed
Pull Request — master (#120)
by
unknown
02:28
created

generateStructuredTextRepresentation()   B

Complexity

Conditions 10
Paths 30

Size

Total Lines 54
Code Lines 27

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 1
Metric Value
cc 10
eloc 27
c 2
b 0
f 1
nc 30
nop 1
dl 0
loc 54
rs 7.6666

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
namespace BeyondCode\ErdGenerator;
4
5
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
6
use phpDocumentor\GraphViz\Graph;
7
use Illuminate\Support\Collection;
8
use phpDocumentor\GraphViz\Node;
9
use \Illuminate\Database\Eloquent\Model as EloquentModel;
0 ignored issues
show
Bug introduced by
The type \Illuminate\Database\Eloquent\Model was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
10
11
class GraphBuilder
12
{
13
    /** @var Graph */
14
    private $graph;
15
16
    /**
17
     * @param $models
18
     * @return Graph
19
     */
20
    public function buildGraph(Collection $models) : Graph
21
    {
22
        $this->graph = new Graph();
23
24
        foreach (config('erd-generator.graph') as $key => $value) {
25
            $this->graph->{"set{$key}"}($value);
26
        }
27
28
        $this->addModelsToGraph($models);
29
30
        return $this->graph;
31
    }
32
33
    /**
34
     * Generate a structured text representation of the ER diagram
35
     * 
36
     * @param Collection $models
37
     * @return string
38
     */
39
    public function generateStructuredTextRepresentation(Collection $models) : string
40
    {
41
        $output = "# Entity Relationship Diagram\n\n";
42
        
43
        // First list all models/entities with their attributes
44
        $output .= "## Entities\n\n";
45
        
46
        foreach ($models as $model) {
47
            /** @var Model $model */
48
            $eloquentModel = app($model->getModel());
49
            $output .= "### " . $model->getLabel() . " (`" . $model->getModel() . "`)\n\n";
50
            
51
            // Add table columns if available
52
            if (config('erd-generator.use_db_schema')) {
53
                $columns = $this->getTableColumnsFromModel($eloquentModel);
54
                if (count($columns) > 0) {
55
                    $output .= "#### Attributes:\n\n";
56
                    foreach ($columns as $column) {
57
                        $columnType = config('erd-generator.use_column_types') ? ' (' . $column->getType()->getName() . ')' : '';
58
                        $output .= "- `" . $column->getName() . "`" . $columnType . "\n";
59
                    }
60
                    $output .= "\n";
61
                }
62
            }
63
        }
64
        
65
        // Then list all relationships
66
        $output .= "## Relationships\n\n";
67
        
68
        foreach ($models as $model) {
69
            /** @var Model $model */
70
            if (count($model->getRelations()) > 0) {
71
                $output .= "### " . $model->getLabel() . " Relationships\n\n";
72
                
73
                foreach ($model->getRelations() as $relation) {
74
                    /** @var ModelRelation $relation */
75
                    // Find the related model by comparing model class names
76
                    $relatedModelClass = $relation->getModel();
77
                    $relatedModel = $models->first(function ($m) use ($relatedModelClass) {
78
                        return $m->getModel() === $relatedModelClass;
79
                    });
80
                    
81
                    if ($relatedModel) {
82
                        $output .= "- **" . $relation->getType() . "** `" . $relation->getName() . "` to " . 
83
                                  $relatedModel->getLabel() . " (Local Key: `" . $relation->getLocalKey() . 
0 ignored issues
show
Bug introduced by
Are you sure the usage of $relation->getLocalKey() targeting BeyondCode\ErdGenerator\...Relation::getLocalKey() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
84
                                  "`, Foreign Key: `" . $relation->getForeignKey() . "`)\n";
85
                    }
86
                }
87
                
88
                $output .= "\n";
89
            }
90
        }
91
        
92
        return $output;
93
    }
94
95
    protected function getTableColumnsFromModel(EloquentModel $model)
96
    {
97
        try {
98
99
            $table = $model->getConnection()->getTablePrefix() . $model->getTable();
100
            $schema = $model->getConnection()->getDoctrineSchemaManager($table);
101
            $databasePlatform = $schema->getDatabasePlatform();
102
            $databasePlatform->registerDoctrineTypeMapping('enum', 'string');
103
104
            $database = null;
105
106
            if (strpos($table, '.')) {
107
                list($database, $table) = explode('.', $table);
108
            }
109
110
            return $schema->listTableColumns($table, $database);
111
        } catch (\Throwable $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
112
        }
113
114
        return [];
115
    }
116
117
    protected function getModelLabel(EloquentModel $model, string $label)
118
    {
119
120
        $table = '<<table width="100%" height="100%" border="0" margin="0" cellborder="1" cellspacing="0" cellpadding="10">' . PHP_EOL;
121
        $table .= '<tr width="100%"><td width="100%" bgcolor="'.config('erd-generator.table.header_background_color').'"><font color="'.config('erd-generator.table.header_font_color').'">' . $label . '</font></td></tr>' . PHP_EOL;
122
123
        if (config('erd-generator.use_db_schema')) {
124
            $columns = $this->getTableColumnsFromModel($model);
125
            foreach ($columns as $column) {
126
                $label = $column->getName();
127
                if (config('erd-generator.use_column_types')) {
128
                    $label .= ' ('.$column->getType()->getName().')';
129
                }
130
                $table .= '<tr width="100%"><td port="' . $column->getName() . '" align="left" width="100%"  bgcolor="'.config('erd-generator.table.row_background_color').'"><font color="'.config('erd-generator.table.row_font_color').'" >' . $label . '</font></td></tr>' . PHP_EOL;
131
            }
132
        }
133
134
        $table .= '</table>>';
135
136
        return $table;
137
    }
138
139
    protected function addModelsToGraph(Collection $models)
140
    {
141
        // Add models to graph
142
        $models->map(function (Model $model) {
143
            $eloquentModel = app($model->getModel());
144
            $this->addNodeToGraph($eloquentModel, $model->getNodeName(), $model->getLabel());
145
        });
146
147
        // Create relations
148
        $models->map(function ($model) {
149
            $this->addRelationToGraph($model);
150
        });
151
    }
152
153
    protected function addNodeToGraph(EloquentModel $eloquentModel, string $nodeName, string $label)
154
    {
155
        $node = Node::create($nodeName);
156
        $node->setLabel($this->getModelLabel($eloquentModel, $label));
157
158
        foreach (config('erd-generator.node') as $key => $value) {
159
            $node->{"set{$key}"}($value);
160
        }
161
162
        $this->graph->setNode($node);
163
    }
164
165
    protected function addRelationToGraph(Model $model)
166
    {
167
168
        $modelNode = $this->graph->findNode($model->getNodeName());
169
170
        /** @var ModelRelation $relation */
171
        foreach ($model->getRelations() as $relation) {
172
            $relatedModelNode = $this->graph->findNode($relation->getModelNodeName());
173
174
            if ($relatedModelNode !== null) {
175
                $this->connectByRelation($model, $relation, $modelNode, $relatedModelNode);
176
            }
177
        }
178
    }
179
180
    /**
181
     * @param Node $modelNode
182
     * @param Node $relatedModelNode
183
     * @param ModelRelation $relation
184
     */
185
    protected function connectNodes(Node $modelNode, Node $relatedModelNode, ModelRelation $relation): void
186
    {
187
        $edge = Edge::create($modelNode, $relatedModelNode);
188
        $edge->setFromPort($relation->getLocalKey());
0 ignored issues
show
Bug introduced by
Are you sure the usage of $relation->getLocalKey() targeting BeyondCode\ErdGenerator\...Relation::getLocalKey() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
189
        $edge->setToPort($relation->getForeignKey());
190
        $edge->setLabel(' ');
191
        $edge->setXLabel($relation->getType() . PHP_EOL . $relation->getName());
192
193
        foreach (config('erd-generator.edge') as $key => $value) {
194
            $edge->{"set{$key}"}($value);
195
        }
196
197
        foreach (config('erd-generator.relations.' . $relation->getType(), []) as $key => $value) {
198
            $edge->{"set{$key}"}($value);
199
        }
200
201
        $this->graph->link($edge);
202
    }
203
204
    /**
205
     * @param Model $model
206
     * @param ModelRelation $relation
207
     * @param Node $modelNode
208
     * @param Node $relatedModelNode
209
     * @return void
210
     */
211
    protected function connectBelongsToMany(
212
        Model $model,
213
        ModelRelation $relation,
214
        Node $modelNode,
215
        Node $relatedModelNode
216
    ): void {
217
        $relationName = $relation->getName();
218
        $eloquentModel = app($model->getModel());
219
220
        /** @var BelongsToMany $eloquentRelation */
221
        $eloquentRelation = $eloquentModel->$relationName();
222
223
        if (!$eloquentRelation instanceof BelongsToMany) {
0 ignored issues
show
introduced by
$eloquentRelation is always a sub-type of Illuminate\Database\Eloq...Relations\BelongsToMany.
Loading history...
224
            return;
225
        }
226
227
        $pivotClass = $eloquentRelation->getPivotClass();
228
229
        try {
230
            /** @var EloquentModel $relationModel */
231
            $pivotModel = app($pivotClass);
232
            $pivotModel->setTable($eloquentRelation->getTable());
233
            $label = (new \ReflectionClass($pivotClass))->getShortName();
234
            $pivotTable = $eloquentRelation->getTable();
235
            $this->addNodeToGraph($pivotModel, $pivotTable, $label);
236
237
            $pivotModelNode = $this->graph->findNode($pivotTable);
238
239
            $relation = new ModelRelation(
240
                $relationName,
241
                'BelongsToMany',
242
                $model->getModel(),
243
                $eloquentRelation->getParent()->getKeyName(),
244
                $eloquentRelation->getForeignPivotKeyName()
245
            );
246
247
            $this->connectNodes($modelNode, $pivotModelNode, $relation);
248
249
            $relation = new ModelRelation(
250
                $relationName,
251
                'BelongsToMany',
252
                $model->getModel(),
253
                $eloquentRelation->getRelatedPivotKeyName(),
254
                $eloquentRelation->getRelated()->getKeyName()
255
            );
256
257
            $this->connectNodes($pivotModelNode, $relatedModelNode, $relation);
258
        } catch (\ReflectionException $e){}
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
259
    }
260
261
    /**
262
     * @param Model $model
263
     * @param ModelRelation $relation
264
     * @param Node $modelNode
265
     * @param Node $relatedModelNode
266
     */
267
    protected function connectByRelation(
268
        Model $model,
269
        ModelRelation $relation,
270
        Node $modelNode,
271
        Node $relatedModelNode
272
    ): void {
273
274
        if ($relation->getType() === 'BelongsToMany') {
275
            $this->connectBelongsToMany($model, $relation, $modelNode, $relatedModelNode);
276
            return;
277
        }
278
279
        $this->connectNodes($modelNode, $relatedModelNode, $relation);
280
    }
281
}
282