Completed
Push — wip-platform ( 03cba6...2999ab )
by
unknown
05:53 queued 02:49
created

GenerateAdminCommand::getBundleNameFromClass()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 13
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 13
rs 9.4285
c 0
b 0
f 0
cc 3
eloc 6
nc 3
nop 1
1
<?php
2
3
/*
4
 *
5
 * Copyright (C) 2015-2017 Libre Informatique
6
 *
7
 * This file is licenced under the GNU LGPL v3.
8
 * For the full copyright and license information, please view the LICENSE.md
9
 * file that was distributed with this source code.
10
 */
11
12
namespace Blast\Bundle\CoreBundle\Command;
13
14
use Sensio\Bundle\GeneratorBundle\Command\Helper\DialogHelper;
15
use Sensio\Bundle\GeneratorBundle\Command\Helper\QuestionHelper;
16
use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand;
17
use Symfony\Bundle\FrameworkBundle\Console\Application;
18
use Symfony\Component\Console\Input\InputArgument;
19
use Symfony\Component\Console\Input\InputInterface;
20
use Symfony\Component\Console\Input\InputOption;
21
use Symfony\Component\Console\Output\OutputInterface;
22
use Symfony\Component\Console\Question\ConfirmationQuestion;
23
use Symfony\Component\Console\Question\Question;
24
use Symfony\Component\DependencyInjection\Container;
25
use Symfony\Component\HttpKernel\Bundle\BundleInterface;
26
use Symfony\Component\HttpKernel\KernelInterface;
27
use Sonata\AdminBundle\Model\ModelManagerInterface;
28
use Sonata\AdminBundle\Command\Validators;
29
use Blast\Bundle\CoreBundle\Generator\AdminGenerator;
30
use Blast\Bundle\CoreBundle\Generator\ControllerGenerator;
31
use Blast\Bundle\CoreBundle\Generator\ServicesManipulator;
32
use Blast\Bundle\CoreBundle\Generator\BlastGenerator;
33
use Blast\Bundle\CoreBundle\Command\Traits\Interaction;
34
35
/**
36
 * Class GenerateAdminCommand.
37
 */
38
class GenerateAdminCommand extends ContainerAwareCommand
39
{
40
    use Interaction;
41
42
    /**
43
     * @var string[]
44
     */
45
    private $managerTypes;
46
47
    /**
48
     * {@inheritdoc}
49
     */
50
    public function configure()
51
    {
52
        $this
53
            ->setName('blast:generate:admin')
54
            ->setDescription('Generates an admin class based on the given model class')
55
            ->addArgument('model', InputArgument::REQUIRED, 'The fully qualified model class')
56
            ->addOption('bundle', 'b', InputOption::VALUE_OPTIONAL, 'The bundle name')
57
            ->addOption('admin', 'a', InputOption::VALUE_OPTIONAL, 'The admin class basename')
58
            ->addOption('controller', 'c', InputOption::VALUE_OPTIONAL, 'The controller class basename')
59
            ->addOption('manager', 'm', InputOption::VALUE_OPTIONAL, 'The model manager type')
60
            ->addOption('services', 'y', InputOption::VALUE_OPTIONAL, 'The services YAML file', 'services.yml')
61
            ->addOption('id', 'i', InputOption::VALUE_OPTIONAL, 'The admin service ID')
62
        ;
63
    }
64
65
    /**
66
     * {@inheritdoc}
67
     */
68
    public function isEnabled()
69
    {
70
        return class_exists('Sensio\\Bundle\\GeneratorBundle\\SensioGeneratorBundle');
71
    }
72
73
    /**
74
     * @param string $managerType
75
     *
76
     * @return string
77
     *
78
     * @throws \InvalidArgumentException
79
     */
80
    public function validateManagerType($managerType)
81
    {
82
        $managerTypes = $this->getAvailableManagerTypes();
83
84
        if (!isset($managerTypes[$managerType])) {
85
            throw new \InvalidArgumentException(sprintf(
86
                'Invalid manager type "%s". Available manager types are "%s".',
87
                $managerType,
88
                implode('", "', $managerTypes)
89
            ));
90
        }
91
92
        return $managerType;
93
    }
94
95
    /**
96
     * {@inheritdoc}
97
     */
98
    protected function execute(InputInterface $input, OutputInterface $output)
99
    {
100
        $modelClass = Validators::validateClass($input->getArgument('model'));
101
        $modelClassBasename = current(array_slice(explode('\\', $modelClass), -1));
102
        $bundle = $this->getBundle($input->getOption('bundle') ?: $this->getBundleNameFromClass($modelClass));
103
        $adminClassBasename = $input->getOption('admin') ?: $modelClassBasename . 'Admin';
104
        $adminClassBasename = Validators::validateAdminClassBasename($adminClassBasename);
105
        $managerType = $input->getOption('manager') ?: $this->getDefaultManagerType();
106
        $modelManager = $this->getModelManager($managerType);
107
        $skeletonDirectory = __DIR__ . '/../Resources/skeleton';
108
        $adminGenerator = new AdminGenerator($modelManager, $skeletonDirectory);
109
110
        try {
111
            $adminGenerator->generate($bundle, $adminClassBasename, $modelClass);
112
            $output->writeln(sprintf(
113
                '%sThe admin class "<info>%s</info>" has been generated under the file "<info>%s</info>".',
114
                PHP_EOL,
115
                $adminGenerator->getClass(),
116
                realpath($adminGenerator->getFile())
117
            ));
118
        } catch (\Exception $e) {
119
            $this->writeError($output, $e->getMessage());
120
        }
121
122
        if ($controllerClassBasename = $input->getOption('controller')) {
123
            $controllerClassBasename = Validators::validateControllerClassBasename($controllerClassBasename);
124
            $controllerGenerator = new ControllerGenerator($skeletonDirectory);
125
126
            try {
127
                $controllerGenerator->generate($bundle, $controllerClassBasename);
128
                $output->writeln(sprintf(
129
                    '%sThe controller class "<info>%s</info>" has been generated under the file "<info>%s</info>".',
130
                    PHP_EOL,
131
                    $controllerGenerator->getClass(),
132
                    realpath($controllerGenerator->getFile())
133
                ));
134
            } catch (\Exception $e) {
135
                $this->writeError($output, $e->getMessage());
136
            }
137
        }
138
139
        if ($servicesFile = $input->getOption('services')) {
140
            $adminClass = $adminGenerator->getClass();
141
            $file = sprintf('%s/Resources/config/%s', $bundle->getPath(), $servicesFile);
142
            $servicesManipulator = new ServicesManipulator($file);
143
            $controllerName = $controllerClassBasename
144
                ? sprintf('%s:%s', $bundle->getName(), substr($controllerClassBasename, 0, -10))
145
                : 'BlastCoreBundle:CRUD'
146
            ;
147
148
            try {
149
                $id = $input->getOption('id') ?: $this->getAdminServiceId($bundle->getName(), $adminClassBasename);
150
                $servicesManipulator->addResource($id, $modelClass, $adminClass, $controllerName, $managerType);
151
                $output->writeln(sprintf(
152
                    '%sThe service "<info>%s</info>" has been appended to the file <info>"%s</info>".%s',
153
                    PHP_EOL,
154
                    $id,
155
                    realpath($file),
156
                    PHP_EOL
157
                ));
158
            } catch (\Exception $e) {
159
                $this->writeError($output, $e->getMessage());
160
            }
161
        }
162
163
        try {
164
            $blastFile = sprintf('%s/Resources/config/blast.yml', $bundle->getPath());
165
            $blastGenerator = new BlastGenerator($blastFile, $modelManager, $skeletonDirectory);
166
            $blastGenerator->addResource($modelClass);
167
        } catch (\Exception $e) {
168
            $this->writeError($output, $e->getMessage());
169
        }
170
171
        return 0;
172
    }
173
174
    /**
175
     * {@inheritdoc}
176
     */
177
    protected function interact(InputInterface $input, OutputInterface $output)
178
    {
179
        $questionHelper = $this->getQuestionHelper();
180
        $questionHelper->writeSection($output, 'Welcome to the Blast Sonata admin generator');
181
        $modelClass = $this->askAndValidate(
182
            $input,
183
            $output,
184
            'The fully qualified model class',
185
            $input->getArgument('model'),
186
            'Sonata\AdminBundle\Command\Validators::validateClass'
187
        );
188
189
        $modelClassBasename = current(array_slice(explode('\\', $modelClass), -1));
190
        $bundleName = $this->askAndValidate(
191
            $input,
192
            $output,
193
            'The bundle name',
194
            $input->getOption('bundle') ?: $this->getBundleNameFromClass($modelClass),
195
            'Sensio\Bundle\GeneratorBundle\Command\Validators::validateBundleName'
196
        );
197
        $adminClassBasename = $this->askAndValidate(
198
            $input,
199
            $output,
200
            'The admin class basename',
201
            $input->getOption('admin') ?: $modelClassBasename . 'Admin',
202
            'Sonata\AdminBundle\Command\Validators::validateAdminClassBasename'
203
        );
204
205
        if (count($this->getAvailableManagerTypes()) > 1) {
206
            $managerType = $this->askAndValidate(
207
                $input,
208
                $output,
209
                'The manager type',
210
                $input->getOption('manager') ?: $this->getDefaultManagerType(),
211
                array($this, 'validateManagerType')
212
            );
213
            $input->setOption('manager', $managerType);
214
        }
215
216
        if ($this->askConfirmation($input, $output, 'Do you want to generate a controller', 'no', '?')) {
217
            $controllerClassBasename = $this->askAndValidate(
218
                $input,
219
                $output,
220
                'The controller class basename',
221
                $input->getOption('controller') ?: $modelClassBasename . 'AdminController',
222
                'Sonata\AdminBundle\Command\Validators::validateControllerClassBasename'
223
            );
224
            $input->setOption('controller', $controllerClassBasename);
225
        }
226
227
        if ($this->askConfirmation($input, $output, 'Do you want to update the services YAML configuration file', 'yes', '?')) {
228
            $path = $this->getBundle($bundleName)->getPath() . '/Resources/config/';
229
            $servicesFile = $this->askAndValidate(
230
                $input,
231
                $output,
232
                'The services YAML configuration file',
233
                is_file($path . 'admin.yml') ? 'admin.yml' : 'services.yml',
234
                'Sonata\AdminBundle\Command\Validators::validateServicesFile'
235
            );
236
            $id = $this->askAndValidate(
237
                $input,
238
                $output,
239
                'The admin service ID',
240
                $this->getAdminServiceId($bundleName, $adminClassBasename),
241
                'Sonata\AdminBundle\Command\Validators::validateServiceId'
242
            );
243
            $input->setOption('services', $servicesFile);
244
            $input->setOption('id', $id);
245
        }
246
247
        $input->setArgument('model', $modelClass);
248
        $input->setOption('admin', $adminClassBasename);
249
        $input->setOption('bundle', $bundleName);
250
    }
251
252
    /**
253
     * @param string $class
254
     *
255
     * @return string|null
256
     *
257
     * @throws \InvalidArgumentException
258
     */
259
    private function getBundleNameFromClass($class)
260
    {
261
        $application = $this->getApplication();
262
        /* @var $application Application */
263
264
        foreach ($application->getKernel()->getBundles() as $bundle) {
265
            if (strpos($class, $bundle->getNamespace() . '\\') === 0) {
266
                return $bundle->getName();
267
            }
268
        }
269
270
        return;
271
    }
272
273
    /**
274
     * @param string $name
275
     *
276
     * @return BundleInterface
277
     */
278
    private function getBundle($name)
279
    {
280
        return $this->getKernel()->getBundle($name);
0 ignored issues
show
Bug Compatibility introduced by
The expression $this->getKernel()->getBundle($name); of type Symfony\Component\HttpKe...undle\BundleInterface[] adds the type Symfony\Component\HttpKe...undle\BundleInterface[] to the return on line 280 which is incompatible with the return type documented by Blast\Bundle\CoreBundle\...AdminCommand::getBundle of type Symfony\Component\HttpKe...\Bundle\BundleInterface.
Loading history...
281
    }
282
283
    /**
284
     * @param OutputInterface $output
285
     * @param string          $message
286
     */
287
    private function writeError(OutputInterface $output, $message)
288
    {
289
        $output->writeln(sprintf("\n<error>%s</error>", $message));
290
    }
291
292
    /**
293
     * @param InputInterface  $input
294
     * @param OutputInterface $output
295
     * @param string          $questionText
296
     * @param mixed           $default
297
     * @param callable        $validator
298
     *
299
     * @return mixed
300
     */
301
    private function askAndValidate(InputInterface $input, OutputInterface $output, $questionText, $default, $validator)
302
    {
303
        $questionHelper = $this->getQuestionHelper();
304
305
        // NEXT_MAJOR: Remove this BC code for SensioGeneratorBundle 2.3/2.4 after dropping support for Symfony 2.3
306 View Code Duplication
        if ($questionHelper instanceof DialogHelper) {
0 ignored issues
show
Bug introduced by
The class Sensio\Bundle\GeneratorB...and\Helper\DialogHelper does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
307
            return $questionHelper->askAndValidate($output, $questionHelper->getQuestion($questionText, $default), $validator, false, $default);
308
        }
309
310
        $question = new Question($questionHelper->getQuestion($questionText, $default), $default);
311
312
        $question->setValidator($validator);
313
314
        return $questionHelper->ask($input, $output, $question);
315
    }
316
317
    /**
318
     * @param InputInterface  $input
319
     * @param OutputInterface $output
320
     * @param string          $questionText
321
     * @param string          $default
322
     * @param string          $separator
323
     *
324
     * @return string
325
     */
326 View Code Duplication
    private function askConfirmation(InputInterface $input, OutputInterface $output, $questionText, $default, $separator)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
327
    {
328
        $questionHelper = $this->getQuestionHelper();
329
330
        // NEXT_MAJOR: Remove this BC code for SensioGeneratorBundle 2.3/2.4 after dropping support for Symfony 2.3
331
        if ($questionHelper instanceof DialogHelper) {
0 ignored issues
show
Bug introduced by
The class Sensio\Bundle\GeneratorB...and\Helper\DialogHelper does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
332
            $question = $questionHelper->getQuestion($questionText, $default, $separator);
333
334
            return $questionHelper->askConfirmation($output, $question, ($default === 'no' ? false : true));
335
        }
336
337
        $question = new ConfirmationQuestion($questionHelper->getQuestion(
338
            $questionText,
339
            $default,
340
            $separator
341
        ), ($default === 'no' ? false : true));
342
343
        return $questionHelper->ask($input, $output, $question);
344
    }
345
346
    /**
347
     * @return string
348
     *
349
     * @throws \RuntimeException
350
     */
351
    private function getDefaultManagerType()
352
    {
353
        if (!$managerTypes = $this->getAvailableManagerTypes()) {
354
            throw new \RuntimeException('There are no model managers registered.');
355
        }
356
357
        return current($managerTypes);
358
    }
359
360
    /**
361
     * @param string $managerType
362
     *
363
     * @return ModelManagerInterface
364
     */
365
    private function getModelManager($managerType)
366
    {
367
        return $this->getContainer()->get('sonata.admin.manager.' . $managerType);
368
    }
369
370
    /**
371
     * @param string $bundleName
372
     * @param string $adminClassBasename
373
     *
374
     * @return string
375
     */
376
    private function getAdminServiceId($bundleName, $adminClassBasename)
377
    {
378
        $prefix = substr($bundleName, -6) == 'Bundle' ? substr($bundleName, 0, -6) : $bundleName;
379
        $suffix = substr($adminClassBasename, -5) == 'Admin' ? substr($adminClassBasename, 0, -5) : $adminClassBasename;
380
        $suffix = str_replace('\\', '.', $suffix);
381
382
        return Container::underscore(sprintf(
383
            '%s.admin.%s',
384
            $prefix,
385
            $suffix
386
        ));
387
    }
388
389
    /**
390
     * @return string[]
391
     */
392
    private function getAvailableManagerTypes()
393
    {
394
        $container = $this->getContainer();
395
396
        if (!$container instanceof Container) {
397
            return array();
398
        }
399
400
        if ($this->managerTypes === null) {
401
            $this->managerTypes = array();
402
403
            foreach ($container->getServiceIds() as $id) {
404
                if (strpos($id, 'sonata.admin.manager.') === 0) {
405
                    $managerType = substr($id, 21);
406
                    $this->managerTypes[$managerType] = $managerType;
407
                }
408
            }
409
        }
410
411
        return $this->managerTypes;
412
    }
413
414
    /**
415
     * @return KernelInterface
416
     */
417
    private function getKernel()
418
    {
419
        /* @var $application Application */
420
        $application = $this->getApplication();
421
422
        return $application->getKernel();
423
    }
424
425
    /**
426
     * @return QuestionHelper|DialogHelper
427
     */
428 View Code Duplication
    private function getQuestionHelper()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
429
    {
430
        // NEXT_MAJOR: Remove this BC code for SensioGeneratorBundle 2.3/2.4 after dropping support for Symfony 2.3
431
        if (class_exists('Sensio\Bundle\GeneratorBundle\Command\Helper\DialogHelper')) {
432
            $questionHelper = $this->getHelper('dialog');
433
434
            if (!$questionHelper instanceof DialogHelper) {
0 ignored issues
show
Bug introduced by
The class Sensio\Bundle\GeneratorB...and\Helper\DialogHelper does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
435
                $questionHelper = new DialogHelper();
436
                $this->getHelperSet()->set($questionHelper);
437
            }
438
        } else {
439
            $questionHelper = $this->getHelper('question');
440
441
            if (!$questionHelper instanceof QuestionHelper) {
442
                $questionHelper = new QuestionHelper();
443
                $this->getHelperSet()->set($questionHelper);
444
            }
445
        }
446
447
        return $questionHelper;
448
    }
449
}
450