DoctrinevizCommand::execute()   F
last analyzed

Complexity

Conditions 28
Paths 276

Size

Total Lines 116
Code Lines 88

Duplication

Lines 0
Ratio 0 %

Importance

Changes 6
Bugs 1 Features 1
Metric Value
cc 28
eloc 88
c 6
b 1
f 1
nc 276
nop 2
dl 0
loc 116
rs 2.3833

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
/*
4
 * This file is part of the doctrineviz package
5
 *
6
 * Copyright (c) 2017 Pierre Hennequart
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 *
11
 * Feel free to edit as you please, and have fun.
12
 *
13
 * @author Pierre Hennequart <[email protected]>
14
 */
15
16
declare(strict_types=1);
17
18
namespace Janalis\Doctrineviz\Command;
19
20
use Doctrine\ORM\EntityManager;
21
use Doctrine\ORM\Mapping\ClassMetadataInfo;
22
use Janalis\Doctrineviz\Graphviz\Graph;
23
use Janalis\Doctrineviz\Graphviz\Graphviz;
24
use Janalis\Doctrineviz\Graphviz\Record;
25
use Janalis\Doctrineviz\Graphviz\Vertex;
26
use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand;
27
use Symfony\Component\Console\Input\InputInterface;
28
use Symfony\Component\Console\Input\InputOption;
29
use Symfony\Component\Console\Output\OutputInterface;
30
31
/**
32
 * doctrineviz command.
33
 */
34
class DoctrinevizCommand extends ContainerAwareCommand
35
{
36
    const NAME = 'doctrine:generate:viz';
37
38
    /**
39
     * Configure.
40
     */
41
    protected function configure()
42
    {
43
        $this
44
            ->setName(static::NAME)
45
            ->setHelp('Generates database mapping')
46
            ->addOption('pattern', 'p', InputOption::VALUE_OPTIONAL, 'Filter entities that match that PCRE pattern', '.*')
47
            ->addOption('binary', 'b', InputOption::VALUE_OPTIONAL, 'Path to graphviz dot binary')
48
            ->addOption('format', 'f', InputOption::VALUE_OPTIONAL, 'Output format', 'png')
49
            ->addOption('output-path', 'o', InputOption::VALUE_OPTIONAL, 'Output path');
50
    }
51
52
    /**
53
     * Execute.
54
     *
55
     * @param InputInterface  $input
56
     * @param OutputInterface $output
57
     *
58
     * @return int Status code
59
     */
60
    public function execute(InputInterface $input, OutputInterface $output): int
61
    {
62
        $pattern = '/'.$input->getOption('pattern').'/';
63
        /** @var EntityManager $em */
64
        $em = $this->getContainer()->get('doctrine')->getManager();
65
        $entities = array_filter($em->getConfiguration()->getMetadataDriverImpl()->getAllClassNames(), function ($entity) use ($pattern) {
66
            return preg_match($pattern, $entity);
67
        });
68
        $graph = new Graph();
69
        $graph->createAttribute('rankdir', 'LR');
70
        $graph->createAttribute('ranksep', '3');
71
        /** @var Vertex[] $tables */
72
        $tables = [];
73
        foreach ($entities as $entity) {
74
            $metadata = $em->getClassMetadata($entity);
75
            if ($metadata->getFieldNames()) {
76
                $table = $graph->createVertex($metadata->getTableName());
77
                $table->createAttribute('shape', 'record');
78
                $table->createAttribute('width', '4');
79
                foreach ($metadata->getFieldNames() as $fieldName) {
80
                    $fieldMapping = $metadata->getFieldMapping($fieldName);
81
                    $table->addRecord(new Record($this->getFieldMappingDisplayName($fieldMapping)));
82
                }
83
                $tables[$entity] = $table;
84
            }
85
        }
86
        foreach ($entities as $entity) {
87
            $metadata = $em->getClassMetadata($entity);
88
            foreach ($metadata->getAssociationMappings() as $associationMapping) {
89
                if (array_key_exists('joinTable', $associationMapping) && $associationMapping['joinTable']) {
90
                    $joinTable = $associationMapping['joinTable'];
91
                    $table = $graph->createVertex($joinTable['name']);
92
                    $table->createAttribute('shape', 'record');
93
                    $table->createAttribute('width', '4');
94
                    if (array_key_exists('joinColumns', $joinTable)) {
95
                        $sourceEntity = $associationMapping['sourceEntity'];
96
                        $joinColumns = $joinTable['joinColumns'];
97
                        foreach ($joinColumns as $joinColumn) {
98
                            $record = new Record($this->getFieldMappingDisplayName($joinColumn, 'name'));
99
                            $table->addRecord($record);
100
                            $nullable = false;
101
                            if (array_key_exists($sourceEntity, $tables)) {
102
                                $name = $this->getFieldMappingDisplayName($joinColumn, 'referencedColumnName');
103
                                $nullable = $joinColumn['nullable'];
104
                                $tables[$sourceEntity]->addRecord($to = $graph->getVertex($tables[$sourceEntity]->getId())->getRecord($name) ?: new Record($name));
105
                            }
106
                            $edge = $record->addEdgeTo($to);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $to does not seem to be defined for all execution paths leading up to this point.
Loading history...
107
                            $edge->createAttribute('headlabel', $nullable ? '0..1' : '1');
0 ignored issues
show
Bug introduced by
The method createAttribute() does not exist on Janalis\Doctrineviz\Graphviz\EdgeInterface. Since it exists in all sub-types, consider adding an abstract or default implementation to Janalis\Doctrineviz\Graphviz\EdgeInterface. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

107
                            $edge->/** @scrutinizer ignore-call */ 
108
                                   createAttribute('headlabel', $nullable ? '0..1' : '1');
Loading history...
108
                            $edge->createAttribute('taillabel', '*');
109
                        }
110
                    }
111
                    if (array_key_exists('inverseJoinColumns', $joinTable)) {
112
                        $targetEntity = $associationMapping['targetEntity'];
113
                        $inverseJoinColumns = $joinTable['inverseJoinColumns'];
114
                        foreach ($inverseJoinColumns as $inverseJoinColumn) {
115
                            $record = new Record($this->getFieldMappingDisplayName($inverseJoinColumn, 'name'));
116
                            $table->addRecord($record);
117
                            $nullable = false;
118
                            if (array_key_exists($targetEntity, $tables)) {
119
                                $name = $this->getFieldMappingDisplayName($inverseJoinColumn, 'referencedColumnName');
120
                                $nullable = $inverseJoinColumn['nullable'];
121
                                $tables[$targetEntity]->addRecord($to = $graph->getVertex($tables[$targetEntity]->getId())->getRecord($name) ?: new Record($name));
122
                            }
123
                            $edge = $record->addEdgeTo($to);
124
                            $edge->createAttribute('headlabel', $nullable ? '0..1' : '1');
125
                            $edge->createAttribute('taillabel', '*');
126
                        }
127
                    }
128
                    $tables[$table->getId()] = $table;
129
                } else {
130
                    $targetEntity = $associationMapping['targetEntity'];
131
                    if (!array_key_exists($targetEntity, $tables) || !array_key_exists('sourceToTargetKeyColumns', $associationMapping)) {
132
                        continue;
133
                    }
134
                    $columns = $associationMapping['sourceToTargetKeyColumns'];
135
                    $to = $graph->getVertex($tables[$targetEntity]->getId())->getRecord(array_values($columns)[0]);
136
                    if (!$to) {
137
                        $to = new Record($this->getFieldMappingDisplayName([
138
                            'columnName' => array_values($columns)[0],
139
                        ]));
140
                        $tables[$targetEntity]->addRecord($to);
141
                    }
142
                    $from = $graph->getVertex($tables[$entity]->getId())->getRecord(array_keys($columns)[0]);
143
                    if (!$from) {
144
                        $from = new Record($this->getFieldMappingDisplayName([
145
                            'columnName' => array_keys($columns)[0],
146
                        ]));
147
                        $tables[$entity]->addRecord($from);
148
                    }
149
                    $joinColumn = $associationMapping['joinColumns'][0];
150
                    $nullable = !array_key_exists('nullable', $joinColumn) || $joinColumn['nullable'];
151
                    $edge = $from->addEdgeTo($to);
0 ignored issues
show
Bug introduced by
The method addEdgeTo() does not exist on Janalis\Doctrineviz\Graphviz\RecordInterface. Since it exists in all sub-types, consider adding an abstract or default implementation to Janalis\Doctrineviz\Graphviz\RecordInterface. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

151
                    /** @scrutinizer ignore-call */ 
152
                    $edge = $from->addEdgeTo($to);
Loading history...
152
                    $edge->createAttribute('headlabel', $nullable ? '0..1' : '1');
153
                    if (array_key_exists('type', $associationMapping)) {
154
                        $edge->createAttribute('taillabel', ClassMetadataInfo::ONE_TO_ONE === $associationMapping['type'] ? '1' : '*');
155
                    }
156
                }
157
            }
158
        }
159
        ksort($tables);
160
        $format = $input->getOption('format', 'png');
0 ignored issues
show
Unused Code introduced by
The call to Symfony\Component\Consol...tInterface::getOption() has too many arguments starting with 'png'. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

160
        /** @scrutinizer ignore-call */ 
161
        $format = $input->getOption('format', 'png');

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
161
        $binary = $input->getOption('binary', null);
162
        $path = $input->getOption('output-path', null);
163
        $graphviz = new Graphviz($format, $binary);
164
        if ($path) {
165
            return (int) !file_put_contents($path, $graphviz->createImageData($graph));
166
        }
167
        if ('dot' === $format) {
168
            $output->writeln((string) $graph);
169
170
            return 0;
171
        }
172
        // @codeCoverageIgnoreStart
173
        $graphviz->display($graph);
174
175
        return 0;
176
        // @codeCoverageIgnoreEnd
177
    }
178
179
    /**
180
     * Get field mapping display name.
181
     *
182
     * @param array  $fieldMapping
183
     * @param string $nameKey
184
     *
185
     * @return string
186
     */
187
    protected function getFieldMappingDisplayName(array $fieldMapping, string $nameKey = 'columnName'): string
188
    {
189
        $name = $fieldMapping[$nameKey];
190
        $type = array_key_exists('type', $fieldMapping) ? $fieldMapping['type'] : 'integer';
191
192
        return sprintf('%s : %s', $name, $type);
193
    }
194
}
195