Completed
Branch master (3b8125)
by
unknown
01:57
created

SimpleEntityGeneratorGenerateCommand   A

Complexity

Total Complexity 29

Size/Duplication

Total Lines 244
Duplicated Lines 0 %

Coupling/Cohesion

Components 2
Dependencies 10

Importance

Changes 0
Metric Value
wmc 29
lcom 2
cbo 10
dl 0
loc 244
rs 10
c 0
b 0
f 0

12 Methods

Rating   Name   Duplication   Size   Complexity  
A configure() 0 11 1
A execute() 0 37 3
A processStructures() 0 15 3
A processInterface() 0 18 3
A processClass() 0 21 4
A processTestClass() 0 20 4
A checkClassesDuplicate() 0 11 3
A validateClasses() 0 9 2
A getFilesManager() 0 4 1
A getStructureGenerator() 0 4 1
A getValidator() 0 4 1
A outputProcessMethods() 0 12 3
1
<?php
2
3
namespace HelloWordPl\SimpleEntityGeneratorBundle\Command;
4
5
use Doctrine\Common\Collections\ArrayCollection;
6
use Exception;
7
use HelloWordPl\SimpleEntityGeneratorBundle\Lib\ClassConfig;
8
use HelloWordPl\SimpleEntityGeneratorBundle\Lib\Interfaces\StructureWithMethodsInterface;
9
use HelloWordPl\SimpleEntityGeneratorBundle\Lib\Items\ClassManager;
10
use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand;
11
use Symfony\Component\Console\Input\InputArgument;
12
use Symfony\Component\Console\Input\InputInterface;
13
use Symfony\Component\Console\Input\InputOption;
14
use Symfony\Component\Console\Output\OutputInterface;
15
16
/**
17
 * @author Sławomir Kania <[email protected]>
18
 */
19
class SimpleEntityGeneratorGenerateCommand extends ContainerAwareCommand
20
{
21
22
    const PARAM_BUNDLE_NAME = 'bundle_name';
23
    const PARAM_FILE_NAME = 'file_name';
24
    const OPTION_NO_INTERFACES = 'no-interfaces';
25
    const OPTION_NO_PHPUNIT_CLASSES = 'no-phpunit-classes';
26
    const OPTION_ONLY_SIMULATE_FILE = 'only-simulate-file';
27
28
    protected function configure()
29
    {
30
        $this
31
            ->setName('class_generator:generate')
32
            ->setDescription('Generate entities from yaml bundle config file')
33
            ->addArgument(self::PARAM_BUNDLE_NAME, InputArgument::REQUIRED, 'Name of bundle where config file is placed eg. AppBundle')
34
            ->addArgument(self::PARAM_FILE_NAME, InputArgument::REQUIRED, sprintf('Name of yaml config file eg. entities.yml placed in /{%s}/Resources/config/{%s}', self::PARAM_BUNDLE_NAME, self::PARAM_FILE_NAME))
35
            ->addOption(self::OPTION_NO_INTERFACES, null, InputOption::VALUE_NONE, 'Switches off interfaces generating')
36
            ->addOption(self::OPTION_NO_PHPUNIT_CLASSES, null, InputOption::VALUE_NONE, 'Switches off PHPUnit classes generating')
37
            ->addOption(self::OPTION_ONLY_SIMULATE_FILE, null, InputOption::VALUE_NONE, 'Simulation of generating classes from file and show summary');
38
    }
39
40
    protected function execute(InputInterface $input, OutputInterface $output)
41
    {
42
        try {
43
            $classConfig = new ClassConfig();
44
            $classConfig->setNoInterface($input->getOption(self::OPTION_NO_INTERFACES));
45
            $classConfig->setNoPHPUnitClass($input->getOption(self::OPTION_NO_PHPUNIT_CLASSES));
46
            $onlySimulate = $input->getOption(self::OPTION_ONLY_SIMULATE_FILE);
47
48
            $bundleName = $input->getArgument(self::PARAM_BUNDLE_NAME);
49
            $fileName = $input->getArgument(self::PARAM_FILE_NAME);
50
51
            $filesManager = $this->getFilesManager();
52
            $structureGenerator = $this->getStructureGenerator();
53
54
            $fileContent = $filesManager->loadFileContent($bundleName, $fileName);
0 ignored issues
show
Bug introduced by
It seems like $bundleName defined by $input->getArgument(self::PARAM_BUNDLE_NAME) on line 48 can also be of type array<integer,string> or null; however, HelloWordPl\SimpleEntity...ager::loadFileContent() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
Bug introduced by
It seems like $fileName defined by $input->getArgument(self::PARAM_FILE_NAME) on line 49 can also be of type array<integer,string> or null; however, HelloWordPl\SimpleEntity...ager::loadFileContent() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
55
            $entitiesData = $structureGenerator->parseToArray($fileContent);
56
            $classManagers = $structureGenerator->buildEntitiesClassStructure($entitiesData, $classConfig);
57
58
            $this->checkClassesDuplicate($classManagers);
59
            $this->validateClasses($classManagers);
60
61
            $output->writeln('ClassGenerator 1.1.0 by Slawomir Kania and contributors.');
62
            $output->writeln('');
63
            if ($onlySimulate) {
64
                $output->writeln(sprintf('<comment>[Generating and updating classes is disabled] Simulation for file: %s, in bundle: %s started. Please wait for summary...</comment>', $fileName, $bundleName));
65
            } else {
66
                $output->writeln(sprintf('<info>Start generating classes from config file: %s, in bundle: %s. Please wait...</info>', $fileName, $bundleName));
67
            }
68
            $output->writeln('');
69
70
            $this->processStructures($output, $classManagers, $onlySimulate);
71
72
            $output->writeln('<info>Finished!</info>');
73
        } catch (Exception $ex) {
74
            throw new Exception(sprintf('Error occured, message: %s, trace: %s', $ex->getMessage(), $ex->getTraceAsString()));
75
        }
76
    }
77
78
    /**
79
     * Dump structures into files
80
     *
81
     * @param OutputInterface $output
82
     * @param ArrayCollection $classManagers
83
     * @throws Exception
84
     */
85
    protected function processStructures(OutputInterface $output, ArrayCollection $classManagers, $onlySimulate = false)
86
    {
87
        foreach ($classManagers as $classManager) {
88
            /* @var $classManager \HelloWordPl\SimpleEntityGeneratorBundle\Lib\Items\ClassManager */
89
            if (false === ($classManager instanceof ClassManager)) {
90
                throw new Exception(sprintf("Invalid entity: %s", get_class($classManager)));
91
            }
92
93
            $this->processInterface($output, $classManager, $onlySimulate);
94
            $this->processClass($output, $classManager, $onlySimulate);
95
            $this->processTestClass($output, $classManager, $onlySimulate);
96
97
            $output->writeln('');
98
        }
99
    }
100
101
    /**
102
     * Dump interface to file
103
     *
104
     * @param OutputInterface $output
105
     * @param ClassManager $classManager
106
     * @param boolean $onlySimulate
107
     * @return
108
     */
109
    protected function processInterface(OutputInterface $output, ClassManager $classManager, $onlySimulate = false)
110
    {
111
        if (false === $classManager->hasInterface()) {
112
            return;
113
        }
114
115
        $interfaceManager = $classManager->getInterface();
116
117
        if (false === $onlySimulate) {
118
            $filesManager = $this->getFilesManager();
119
            $filesManager->dump($interfaceManager);
120
        }
121
122
        $output->writeln('<question>Processed: '.$interfaceManager->getNamespace()."</question>");
123
        $this->outputProcessMethods($output, $interfaceManager);
124
125
        $output->writeln('');
126
    }
127
128
    /**
129
     * Dump class to file
130
     *
131
     * @param OutputInterface $output
132
     * @param ClassManager $classManager
133
     * @param boolean $onlySimulate
134
     */
135
    protected function processClass(OutputInterface $output, ClassManager $classManager, $onlySimulate = false)
136
    {
137
        if (false === $onlySimulate) {
138
            $filesManager = $this->getFilesManager();
139
            $filesManager->dump($classManager);
140
        }
141
142
        $output->writeln('<question>Processed: '.$classManager->getNamespace().'</question>');
143
        if ($classManager->getProperties()->isEmpty()) {
144
            $output->writeln('No properties to add');
145
        } else {
146
            $output->writeln('<comment>properties:</comment>');
147
            foreach ($classManager->getProperties() as $classProperty) {
148
                $output->writeln(sprintf(" - %s: %s", $classProperty->getPreparedName(), $classProperty->getType()));
149
            }
150
        }
151
152
        $this->outputProcessMethods($output, $classManager);
153
154
        $output->writeln('');
155
    }
156
157
    /**
158
     * Dump TestClass to file
159
     *
160
     * @param OutputInterface $output
161
     * @param ClassManager $classManager
162
     * @param boolean $onlySimulate
163
     * @return
164
     */
165
    protected function processTestClass(OutputInterface $output, ClassManager $classManager, $onlySimulate = false)
166
    {
167
        if (false === $classManager->hasTestClass()) {
168
            return;
169
        }
170
171
        $testClassManager = $classManager->getTestClass();
172
173
        if (false === $onlySimulate) {
174
            $filesManager = $this->getFilesManager();
175
            $filesManager->dump($testClassManager);
176
        }
177
178
        $output->writeln('<question>Processed: '.$classManager->getTestClass()->getNamespace().'</question>');
179
        $this->outputProcessMethods($output, $testClassManager);
180
        if (false === $testClassManager->getMethods()->isEmpty()) {
181
            $output->writeln(sprintf('<comment>Implement missing assertions in %s</comment>', $testClassManager->getNamespace()));
182
        }
183
        $output->writeln('');
184
    }
185
186
    /**
187
     * Throw Exception when duplicated classes
188
     *
189
     * @param ArrayCollection $classManagers
190
     * @throws Exception
191
     */
192
    protected function checkClassesDuplicate(ArrayCollection $classManagers)
193
    {
194
        $checkDuplicateArray = [];
195
        foreach ($classManagers as $classManager) {
196
            $checkDuplicateArray[] = $classManager->getNamespace();
197
        }
198
199
        if (count(array_unique($checkDuplicateArray)) < count($checkDuplicateArray)) {
200
            throw new Exception("Duplicated classes in bundle!");
201
        }
202
    }
203
204
    /**
205
     * Throw Exception when class entities invalid
206
     *
207
     * @param ArrayCollection $classManagers
208
     * @throws Exception
209
     */
210
    protected function validateClasses(ArrayCollection $classManagers)
211
    {
212
        $validator = $this->getValidator();
213
        $constraintViolationList = $validator->validate($classManagers);
214
215
        if ($constraintViolationList->count() > 0) {
216
            throw new Exception(sprintf("Structure validation errors: %s", $constraintViolationList));
217
        }
218
    }
219
220
    /**
221
     * @return \HelloWordPl\SimpleEntityGeneratorBundle\Lib\FilesManager
222
     */
223
    protected function getFilesManager()
224
    {
225
        return $this->getContainer()->get('seg.files_manager');
226
    }
227
228
    /**
229
     * @return \HelloWordPl\SimpleEntityGeneratorBundle\Lib\StructureGenerator
230
     */
231
    protected function getStructureGenerator()
232
    {
233
        return $this->getContainer()->get('seg.structure_generator');
234
    }
235
236
    /**
237
     * @return \Symfony\Component\Validator\Validator\ValidatorInterface
238
     */
239
    protected function getValidator()
240
    {
241
        return $this->getContainer()->get('validator');
242
    }
243
244
    /**
245
     * Output rendered methods
246
     *
247
     * @param OutputInterface $output
248
     * @param StructureWithMethodsInterface $item
249
     */
250
    private function outputProcessMethods(OutputInterface $output, StructureWithMethodsInterface $item)
251
    {
252
        if ($item->getMethods()->isEmpty()) {
253
            $output->writeln('No methods to add');
254
        } else {
255
            $output->writeln('<comment>methods:</comment>');
256
            foreach ($item->getMethods() as $method) {
257
                $output->writeln(sprintf(" - %s", $method->getPreparedName()));
258
            }
259
            $output->writeln('');
260
        }
261
    }
262
}
263