Completed
Push — master ( e6c9d7...cc6bd7 )
by Grégoire
12:23
created

GenerateAdminCommand::execute()   D

Complexity

Conditions 10
Paths 540

Size

Total Lines 65

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 65
rs 4.0747
c 0
b 0
f 0
cc 10
nc 540
nop 2

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
/*
6
 * This file is part of the Sonata Project package.
7
 *
8
 * (c) Thomas Rabaix <[email protected]>
9
 *
10
 * For the full copyright and license information, please view the LICENSE
11
 * file that was distributed with this source code.
12
 */
13
14
namespace Sonata\AdminBundle\Command;
15
16
use Sensio\Bundle\GeneratorBundle\SensioGeneratorBundle;
17
use Sonata\AdminBundle\Admin\Pool;
18
use Sonata\AdminBundle\Controller\CRUDController;
19
use Sonata\AdminBundle\Generator\AdminGenerator;
20
use Sonata\AdminBundle\Generator\ControllerGenerator;
21
use Sonata\AdminBundle\Manipulator\ServicesManipulator;
22
use Sonata\AdminBundle\Model\ModelManagerInterface;
23
use Symfony\Bundle\FrameworkBundle\Console\Application;
24
use Symfony\Component\Console\Input\InputArgument;
25
use Symfony\Component\Console\Input\InputInterface;
26
use Symfony\Component\Console\Input\InputOption;
27
use Symfony\Component\Console\Output\OutputInterface;
28
use Symfony\Component\DependencyInjection\Container;
29
use Symfony\Component\HttpKernel\Bundle\BundleInterface;
30
use Symfony\Component\HttpKernel\KernelInterface;
31
32
/**
33
 * @author Marek Stipek <[email protected]>
34
 * @author Simon Cosandey <[email protected]>
35
 */
36
class GenerateAdminCommand extends QuestionableCommand
37
{
38
    /**
39
     * {@inheritdoc}
40
     */
41
    protected static $defaultName = 'sonata:admin:generate';
42
43
    /**
44
     * @var Pool
45
     */
46
    private $pool;
47
48
    /**
49
     * An array of model managers indexed by their service ids.
50
     *
51
     * @var ModelManagerInterface[]
52
     */
53
    private $managerTypes = [];
54
55
    public function __construct(Pool $pool, array $managerTypes)
56
    {
57
        $this->pool = $pool;
58
        $this->managerTypes = $managerTypes;
59
60
        parent::__construct();
61
    }
62
63
    public function configure(): void
64
    {
65
        $this
66
            ->setDescription('Generates an admin class based on the given model class')
67
            ->addArgument('model', InputArgument::REQUIRED, 'The fully qualified model class')
68
            ->addOption('bundle', 'b', InputOption::VALUE_OPTIONAL, 'The bundle name')
69
            ->addOption('admin', 'a', InputOption::VALUE_OPTIONAL, 'The admin class basename')
70
            ->addOption('controller', 'c', InputOption::VALUE_OPTIONAL, 'The controller class basename')
71
            ->addOption('manager', 'm', InputOption::VALUE_OPTIONAL, 'The model manager type')
72
            ->addOption('services', 'y', InputOption::VALUE_OPTIONAL, 'The services YAML file', 'services.yml')
73
            ->addOption('id', 'i', InputOption::VALUE_OPTIONAL, 'The admin service ID')
74
        ;
75
    }
76
77
    public function isEnabled()
78
    {
79
        return class_exists(SensioGeneratorBundle::class);
80
    }
81
82
    /**
83
     * @param string $managerType
84
     *
85
     * @throws \InvalidArgumentException
86
     *
87
     * @return string
88
     */
89
    public function validateManagerType($managerType)
90
    {
91
        $managerTypes = $this->getAvailableManagerTypes();
92
93
        if (!isset($managerTypes[$managerType])) {
94
            throw new \InvalidArgumentException(sprintf(
95
                'Invalid manager type "%s". Available manager types are "%s".',
96
                $managerType,
97
                implode('", "', array_keys($managerTypes))
98
            ));
99
        }
100
101
        return $managerType;
102
    }
103
104
    protected function execute(InputInterface $input, OutputInterface $output)
105
    {
106
        $modelClass = Validators::validateClass($input->getArgument('model'));
0 ignored issues
show
Bug introduced by
It seems like $input->getArgument('model') targeting Symfony\Component\Consol...nterface::getArgument() can also be of type array<integer,string> or null; however, Sonata\AdminBundle\Comma...dators::validateClass() does only seem to accept string, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
107
        $modelClassBasename = current(\array_slice(explode('\\', $modelClass), -1));
108
        $bundle = $this->getBundle($input->getOption('bundle') ?: $this->getBundleNameFromClass($modelClass));
109
        $adminClassBasename = $input->getOption('admin') ?: $modelClassBasename.'Admin';
110
        $adminClassBasename = Validators::validateAdminClassBasename($adminClassBasename);
111
        $managerType = $input->getOption('manager') ?: $this->getDefaultManagerType();
112
        $modelManager = $this->getModelManager($managerType);
113
        $skeletonDirectory = __DIR__.'/../Resources/skeleton';
114
        $adminGenerator = new AdminGenerator($modelManager, $skeletonDirectory);
115
116
        try {
117
            $adminGenerator->generate($bundle, $adminClassBasename, $modelClass);
118
            $output->writeln(sprintf(
119
                '%sThe admin class "<info>%s</info>" has been generated under the file "<info>%s</info>".',
120
                PHP_EOL,
121
                $adminGenerator->getClass(),
122
                realpath($adminGenerator->getFile())
123
            ));
124
        } catch (\Exception $e) {
125
            $this->writeError($output, $e->getMessage());
126
        }
127
128
        $controllerName = CRUDController::class;
129
130
        if ($controllerClassBasename = $input->getOption('controller')) {
131
            $controllerClassBasename = Validators::validateControllerClassBasename($controllerClassBasename);
132
            $controllerGenerator = new ControllerGenerator($skeletonDirectory);
133
134
            try {
135
                $controllerGenerator->generate($bundle, $controllerClassBasename);
136
                $controllerName = $controllerGenerator->getClass();
137
                $output->writeln(sprintf(
138
                    '%sThe controller class "<info>%s</info>" has been generated under the file "<info>%s</info>".',
139
                    PHP_EOL,
140
                    $controllerName,
141
                    realpath($controllerGenerator->getFile())
142
                ));
143
            } catch (\Exception $e) {
144
                $this->writeError($output, $e->getMessage());
145
            }
146
        }
147
148
        if ($servicesFile = $input->getOption('services')) {
149
            $adminClass = $adminGenerator->getClass();
150
            $file = sprintf('%s/Resources/config/%s', $bundle->getPath(), $servicesFile);
151
            $servicesManipulator = new ServicesManipulator($file);
152
153
            try {
154
                $id = $input->getOption('id') ?: $this->getAdminServiceId($bundle->getName(), $adminClassBasename);
155
                $servicesManipulator->addResource($id, $modelClass, $adminClass, $controllerName, $managerType);
156
                $output->writeln(sprintf(
157
                    '%sThe service "<info>%s</info>" has been appended to the file <info>"%s</info>".',
158
                    PHP_EOL,
159
                    $id,
160
                    realpath($file)
161
                ));
162
            } catch (\Exception $e) {
163
                $this->writeError($output, $e->getMessage());
164
            }
165
        }
166
167
        return 0;
168
    }
169
170
    protected function interact(InputInterface $input, OutputInterface $output): void
171
    {
172
        $questionHelper = $this->getQuestionHelper();
173
        $questionHelper->writeSection($output, 'Welcome to the Sonata admin generator');
174
        $modelClass = $this->askAndValidate(
175
            $input,
176
            $output,
177
            'The fully qualified model class',
178
            $input->getArgument('model'),
179
            'Sonata\AdminBundle\Command\Validators::validateClass'
180
        );
181
        $modelClassBasename = current(\array_slice(explode('\\', $modelClass), -1));
182
        $bundleName = $this->askAndValidate(
183
            $input,
184
            $output,
185
            'The bundle name',
186
            $input->getOption('bundle') ?: $this->getBundleNameFromClass($modelClass),
187
            'Sensio\Bundle\GeneratorBundle\Command\Validators::validateBundleName'
188
        );
189
        $adminClassBasename = $this->askAndValidate(
190
            $input,
191
            $output,
192
            'The admin class basename',
193
            $input->getOption('admin') ?: $modelClassBasename.'Admin',
194
            'Sonata\AdminBundle\Command\Validators::validateAdminClassBasename'
195
        );
196
197
        if (\count($this->getAvailableManagerTypes()) > 1) {
198
            $managerType = $this->askAndValidate(
199
                $input,
200
                $output,
201
                'The manager type',
202
                $input->getOption('manager') ?: $this->getDefaultManagerType(),
203
                [$this, 'validateManagerType']
204
            );
205
            $input->setOption('manager', $managerType);
206
        }
207
208
        if ($this->askConfirmation($input, $output, 'Do you want to generate a controller', 'no', '?')) {
209
            $controllerClassBasename = $this->askAndValidate(
210
                $input,
211
                $output,
212
                'The controller class basename',
213
                $input->getOption('controller') ?: $modelClassBasename.'AdminController',
214
                'Sonata\AdminBundle\Command\Validators::validateControllerClassBasename'
215
            );
216
            $input->setOption('controller', $controllerClassBasename);
217
        }
218
219
        if ($this->askConfirmation($input, $output, 'Do you want to update the services YAML configuration file', 'yes', '?')) {
220
            $path = $this->getBundle($bundleName)->getPath().'/Resources/config/';
221
            $servicesFile = $this->askAndValidate(
222
                $input,
223
                $output,
224
                'The services YAML configuration file',
225
                is_file($path.'admin.yml') ? 'admin.yml' : 'services.yml',
226
                'Sonata\AdminBundle\Command\Validators::validateServicesFile'
227
            );
228
            $id = $this->askAndValidate(
229
                $input,
230
                $output,
231
                'The admin service ID',
232
                $this->getAdminServiceId($bundleName, $adminClassBasename),
233
                'Sonata\AdminBundle\Command\Validators::validateServiceId'
234
            );
235
            $input->setOption('services', $servicesFile);
236
            $input->setOption('id', $id);
237
        } else {
238
            $input->setOption('services', false);
239
        }
240
241
        $input->setArgument('model', $modelClass);
242
        $input->setOption('admin', $adminClassBasename);
243
        $input->setOption('bundle', $bundleName);
244
    }
245
246
    /**
247
     * @throws \InvalidArgumentException
248
     */
249
    private function getBundleNameFromClass(string $class): ?string
250
    {
251
        $application = $this->getApplication();
252
        /* @var $application Application */
253
254
        foreach ($application->getKernel()->getBundles() as $bundle) {
255
            if (0 === strpos($class, $bundle->getNamespace().'\\')) {
256
                return $bundle->getName();
257
            }
258
        }
259
260
        return null;
261
    }
262
263
    private function getBundle(string $name): BundleInterface
264
    {
265
        return $this->getKernel()->getBundle($name);
266
    }
267
268
    private function writeError(OutputInterface $output, string $message): void
269
    {
270
        $output->writeln(sprintf("\n<error>%s</error>", $message));
271
    }
272
273
    /**
274
     * @throws \RuntimeException
275
     */
276
    private function getDefaultManagerType(): string
277
    {
278
        if (!$managerTypes = $this->getAvailableManagerTypes()) {
279
            throw new \RuntimeException('There are no model managers registered.');
280
        }
281
282
        return current(array_keys($managerTypes));
283
    }
284
285
    private function getModelManager(string $managerType): ModelManagerInterface
286
    {
287
        $modelManager = $this->getAvailableManagerTypes()[$managerType];
288
        \assert($modelManager instanceof ModelManagerInterface);
289
290
        return $modelManager;
291
    }
292
293
    private function getAdminServiceId(string $bundleName, string $adminClassBasename): string
294
    {
295
        $prefix = 'Bundle' === substr($bundleName, -6) ? substr($bundleName, 0, -6) : $bundleName;
296
        $suffix = 'Admin' === substr($adminClassBasename, -5) ? substr($adminClassBasename, 0, -5) : $adminClassBasename;
297
        $suffix = str_replace('\\', '.', $suffix);
298
299
        return Container::underscore(sprintf(
300
            '%s.admin.%s',
301
            $prefix,
302
            $suffix
303
        ));
304
    }
305
306
    /**
307
     * @return string[]
308
     */
309
    private function getAvailableManagerTypes(): array
310
    {
311
        $managerTypes = [];
312
        foreach ($this->managerTypes as $id => $manager) {
313
            $managerType = substr($id, 21);
314
            $managerTypes[$managerType] = $manager;
315
        }
316
317
        return $managerTypes;
318
    }
319
320
    private function getKernel(): KernelInterface
321
    {
322
        /* @var $application Application */
323
        $application = $this->getApplication();
324
325
        return $application->getKernel();
326
    }
327
}
328