Completed
Push — wip-platform ( 1b7118...2b724b )
by
unknown
03:32
created

GenerateAdminCommand::getKernel()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 7
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 7
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 3
nc 1
nop 0
1
<?php
2
3
/*
4
 * This file is part of the Blast Project package.
5
 *
6
 * Copyright (C) 2015-2017 Libre Informatique
7
 *
8
 * This file is licenced under the GNU LGPL v3.
9
 * For the full copyright and license information, please view the LICENSE.md
10
 * file that was distributed with this source code.
11
 */
12
13
namespace Blast\Bundle\CoreBundle\Command;
14
15
use Sensio\Bundle\GeneratorBundle\Command\Helper\DialogHelper;
16
use Sensio\Bundle\GeneratorBundle\Command\Helper\QuestionHelper;
17
use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand;
18
use Symfony\Bundle\FrameworkBundle\Console\Application;
19
use Symfony\Component\Console\Input\InputArgument;
20
use Symfony\Component\Console\Input\InputInterface;
21
use Symfony\Component\Console\Input\InputOption;
22
use Symfony\Component\Console\Output\OutputInterface;
23
use Symfony\Component\Console\Question\ConfirmationQuestion;
24
use Symfony\Component\Console\Question\Question;
25
use Symfony\Component\DependencyInjection\Container;
26
use Symfony\Component\HttpKernel\Bundle\BundleInterface;
27
use Symfony\Component\HttpKernel\KernelInterface;
28
use Sonata\AdminBundle\Model\ModelManagerInterface;
29
use Sonata\AdminBundle\Command\Validators;
30
use Blast\Bundle\CoreBundle\Generator\AdminGenerator;
31
use Blast\Bundle\CoreBundle\Generator\ControllerGenerator;
32
use Blast\Bundle\CoreBundle\Generator\ServicesManipulator;
33
use Blast\Bundle\CoreBundle\Generator\BlastGenerator;
34
use Blast\Bundle\CoreBundle\Command\Traits\Interaction;
35
36
/**
37
 * Class GenerateAdminCommand.
38
 */
39
class GenerateAdminCommand extends ContainerAwareCommand
40
{
41
    use Interaction;
42
43
    /**
44
     * @var string[]
45
     */
46
    private $managerTypes;
47
48
    /**
49
     * {@inheritdoc}
50
     */
51
    public function configure()
52
    {
53
        $this
54
            ->setName('blast:generate:admin')
55
            ->setDescription('Generates an admin class based on the given model class')
56
            ->addArgument('model', InputArgument::REQUIRED, 'The fully qualified model class')
57
            ->addOption('bundle', 'b', InputOption::VALUE_OPTIONAL, 'The bundle name')
58
            ->addOption('admin', 'a', InputOption::VALUE_OPTIONAL, 'The admin class basename')
59
            ->addOption('controller', 'c', InputOption::VALUE_OPTIONAL, 'The controller class basename')
60
            ->addOption('manager', 'm', InputOption::VALUE_OPTIONAL, 'The model manager type')
61
            ->addOption('services', 'y', InputOption::VALUE_OPTIONAL, 'The services YAML file', 'services.yml')
62
            ->addOption('id', 'i', InputOption::VALUE_OPTIONAL, 'The admin service ID')
63
        ;
64
    }
65
66
    /**
67
     * {@inheritdoc}
68
     */
69
    public function isEnabled()
70
    {
71
        return class_exists('Sensio\\Bundle\\GeneratorBundle\\SensioGeneratorBundle');
72
    }
73
74
    /**
75
     * @param string $managerType
76
     *
77
     * @return string
78
     *
79
     * @throws \InvalidArgumentException
80
     */
81
    public function validateManagerType($managerType)
82
    {
83
        $managerTypes = $this->getAvailableManagerTypes();
84
85
        if (!isset($managerTypes[$managerType])) {
86
            throw new \InvalidArgumentException(sprintf(
87
                'Invalid manager type "%s". Available manager types are "%s".',
88
                $managerType,
89
                implode('", "', $managerTypes)
90
            ));
91
        }
92
93
        return $managerType;
94
    }
95
96
    /**
97
     * {@inheritdoc}
98
     */
99
    protected function execute(InputInterface $input, OutputInterface $output)
100
    {
101
        $modelClass = Validators::validateClass($input->getArgument('model'));
102
        $modelClassBasename = current(array_slice(explode('\\', $modelClass), -1));
103
        $bundle = $this->getBundle($input->getOption('bundle') ?: $this->getBundleNameFromClass($modelClass));
104
        $adminClassBasename = $input->getOption('admin') ?: $modelClassBasename . 'Admin';
105
        $adminClassBasename = Validators::validateAdminClassBasename($adminClassBasename);
106
        $managerType = $input->getOption('manager') ?: $this->getDefaultManagerType();
107
        $modelManager = $this->getModelManager($managerType);
108
        $skeletonDirectory = __DIR__ . '/../Resources/skeleton';
109
        $adminGenerator = new AdminGenerator($modelManager, $skeletonDirectory);
110
111
        try {
112
            $adminGenerator->generate($bundle, $adminClassBasename, $modelClass);
113
            $output->writeln(sprintf(
114
                '%sThe admin class "<info>%s</info>" has been generated under the file "<info>%s</info>".',
115
                PHP_EOL,
116
                $adminGenerator->getClass(),
117
                realpath($adminGenerator->getFile())
118
            ));
119
        } catch (\Exception $e) {
120
            $this->writeError($output, $e->getMessage());
121
        }
122
123
        if ($controllerClassBasename = $input->getOption('controller')) {
124
            $controllerClassBasename = Validators::validateControllerClassBasename($controllerClassBasename);
125
            $controllerGenerator = new ControllerGenerator($skeletonDirectory);
126
127
            try {
128
                $controllerGenerator->generate($bundle, $controllerClassBasename);
129
                $output->writeln(sprintf(
130
                    '%sThe controller class "<info>%s</info>" has been generated under the file "<info>%s</info>".',
131
                    PHP_EOL,
132
                    $controllerGenerator->getClass(),
133
                    realpath($controllerGenerator->getFile())
134
                ));
135
            } catch (\Exception $e) {
136
                $this->writeError($output, $e->getMessage());
137
            }
138
        }
139
140
        if ($servicesFile = $input->getOption('services')) {
141
            $adminClass = $adminGenerator->getClass();
142
            $file = sprintf('%s/Resources/config/%s', $bundle->getPath(), $servicesFile);
143
            $servicesManipulator = new ServicesManipulator($file);
144
            $controllerName = $controllerClassBasename
145
                ? sprintf('%s:%s', $bundle->getName(), substr($controllerClassBasename, 0, -10))
146
                : 'BlastCoreBundle:CRUD'
147
            ;
148
149
            try {
150
                $id = $input->getOption('id') ?: $this->getAdminServiceId($bundle->getName(), $adminClassBasename);
151
                $servicesManipulator->addResource($id, $modelClass, $adminClass, $controllerName, $managerType);
152
                $output->writeln(sprintf(
153
                    '%sThe service "<info>%s</info>" has been appended to the file <info>"%s</info>".%s',
154
                    PHP_EOL,
155
                    $id,
156
                    realpath($file),
157
                    PHP_EOL
158
                ));
159
            } catch (\Exception $e) {
160
                $this->writeError($output, $e->getMessage());
161
            }
162
        }
163
164
        try {
165
            $blastFile = sprintf('%s/Resources/config/blast.yml', $bundle->getPath());
166
            $blastGenerator = new BlastGenerator($blastFile, $modelManager, $skeletonDirectory);
167
            $blastGenerator->addResource($modelClass);
168
        } catch (\Exception $e) {
169
            $this->writeError($output, $e->getMessage());
170
        }
171
172
        return 0;
173
    }
174
175
    /**
176
     * {@inheritdoc}
177
     */
178
    protected function interact(InputInterface $input, OutputInterface $output)
179
    {
180
        $questionHelper = $this->getQuestionHelper();
181
        $questionHelper->writeSection($output, 'Welcome to the Blast Sonata admin generator');
182
        $modelClass = $this->askAndValidate(
183
            $input,
184
            $output,
185
            'The fully qualified model class',
186
            $input->getArgument('model'),
187
            'Sonata\AdminBundle\Command\Validators::validateClass'
188
        );
189
190
        $modelClassBasename = current(array_slice(explode('\\', $modelClass), -1));
191
        $bundleName = $this->askAndValidate(
192
            $input,
193
            $output,
194
            'The bundle name',
195
            $input->getOption('bundle') ?: $this->getBundleNameFromClass($modelClass),
196
            'Sensio\Bundle\GeneratorBundle\Command\Validators::validateBundleName'
197
        );
198
        $adminClassBasename = $this->askAndValidate(
199
            $input,
200
            $output,
201
            'The admin class basename',
202
            $input->getOption('admin') ?: $modelClassBasename . 'Admin',
203
            'Sonata\AdminBundle\Command\Validators::validateAdminClassBasename'
204
        );
205
206
        if (count($this->getAvailableManagerTypes()) > 1) {
207
            $managerType = $this->askAndValidate(
208
                $input,
209
                $output,
210
                'The manager type',
211
                $input->getOption('manager') ?: $this->getDefaultManagerType(),
212
                array($this, 'validateManagerType')
213
            );
214
            $input->setOption('manager', $managerType);
215
        }
216
217
        if ($this->askConfirmation($input, $output, 'Do you want to generate a controller', 'no', '?')) {
218
            $controllerClassBasename = $this->askAndValidate(
219
                $input,
220
                $output,
221
                'The controller class basename',
222
                $input->getOption('controller') ?: $modelClassBasename . 'AdminController',
223
                'Sonata\AdminBundle\Command\Validators::validateControllerClassBasename'
224
            );
225
            $input->setOption('controller', $controllerClassBasename);
226
        }
227
228
        if ($this->askConfirmation($input, $output, 'Do you want to update the services YAML configuration file', 'yes', '?')) {
229
            $path = $this->getBundle($bundleName)->getPath() . '/Resources/config/';
230
            $servicesFile = $this->askAndValidate(
231
                $input,
232
                $output,
233
                'The services YAML configuration file',
234
                is_file($path . 'admin.yml') ? 'admin.yml' : 'services.yml',
235
                'Sonata\AdminBundle\Command\Validators::validateServicesFile'
236
            );
237
            $id = $this->askAndValidate(
238
                $input,
239
                $output,
240
                'The admin service ID',
241
                $this->getAdminServiceId($bundleName, $adminClassBasename),
242
                'Sonata\AdminBundle\Command\Validators::validateServiceId'
243
            );
244
            $input->setOption('services', $servicesFile);
245
            $input->setOption('id', $id);
246
        }
247
248
        $input->setArgument('model', $modelClass);
249
        $input->setOption('admin', $adminClassBasename);
250
        $input->setOption('bundle', $bundleName);
251
    }
252
253
    /**
254
     * @param string $class
255
     *
256
     * @return string|null
257
     *
258
     * @throws \InvalidArgumentException
259
     */
260
    private function getBundleNameFromClass($class)
261
    {
262
        $application = $this->getApplication();
263
        /* @var $application Application */
264
265
        foreach ($application->getKernel()->getBundles() as $bundle) {
266
            if (strpos($class, $bundle->getNamespace() . '\\') === 0) {
267
                return $bundle->getName();
268
            }
269
        }
270
271
        return;
272
    }
273
274
    /**
275
     * @param string $name
276
     *
277
     * @return BundleInterface
278
     */
279
    private function getBundle($name)
280
    {
281
        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 281 which is incompatible with the return type documented by Blast\Bundle\CoreBundle\...AdminCommand::getBundle of type Symfony\Component\HttpKe...\Bundle\BundleInterface.
Loading history...
282
    }
283
284
    /**
285
     * @param OutputInterface $output
286
     * @param string          $message
287
     */
288
    private function writeError(OutputInterface $output, $message)
289
    {
290
        $output->writeln(sprintf("\n<error>%s</error>", $message));
291
    }
292
293
    /**
294
     * @param InputInterface  $input
295
     * @param OutputInterface $output
296
     * @param string          $questionText
297
     * @param mixed           $default
298
     * @param callable        $validator
299
     *
300
     * @return mixed
301
     */
302
    private function askAndValidate(InputInterface $input, OutputInterface $output, $questionText, $default, $validator)
303
    {
304
        $questionHelper = $this->getQuestionHelper();
305
306
        // NEXT_MAJOR: Remove this BC code for SensioGeneratorBundle 2.3/2.4 after dropping support for Symfony 2.3
307 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...
308
            return $questionHelper->askAndValidate($output, $questionHelper->getQuestion($questionText, $default), $validator, false, $default);
309
        }
310
311
        $question = new Question($questionHelper->getQuestion($questionText, $default), $default);
312
313
        $question->setValidator($validator);
314
315
        return $questionHelper->ask($input, $output, $question);
316
    }
317
318
    /**
319
     * @param InputInterface  $input
320
     * @param OutputInterface $output
321
     * @param string          $questionText
322
     * @param string          $default
323
     * @param string          $separator
324
     *
325
     * @return string
326
     */
327 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...
328
    {
329
        $questionHelper = $this->getQuestionHelper();
330
331
        // NEXT_MAJOR: Remove this BC code for SensioGeneratorBundle 2.3/2.4 after dropping support for Symfony 2.3
332
        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...
333
            $question = $questionHelper->getQuestion($questionText, $default, $separator);
334
335
            return $questionHelper->askConfirmation($output, $question, ($default === 'no' ? false : true));
336
        }
337
338
        $question = new ConfirmationQuestion($questionHelper->getQuestion(
339
            $questionText,
340
            $default,
341
            $separator
342
        ), ($default === 'no' ? false : true));
343
344
        return $questionHelper->ask($input, $output, $question);
345
    }
346
347
    /**
348
     * @return string
349
     *
350
     * @throws \RuntimeException
351
     */
352
    private function getDefaultManagerType()
353
    {
354
        if (!$managerTypes = $this->getAvailableManagerTypes()) {
355
            throw new \RuntimeException('There are no model managers registered.');
356
        }
357
358
        return current($managerTypes);
359
    }
360
361
    /**
362
     * @param string $managerType
363
     *
364
     * @return ModelManagerInterface
365
     */
366
    private function getModelManager($managerType)
367
    {
368
        return $this->getContainer()->get('sonata.admin.manager.' . $managerType);
369
    }
370
371
    /**
372
     * @param string $bundleName
373
     * @param string $adminClassBasename
374
     *
375
     * @return string
376
     */
377
    private function getAdminServiceId($bundleName, $adminClassBasename)
378
    {
379
        $prefix = substr($bundleName, -6) == 'Bundle' ? substr($bundleName, 0, -6) : $bundleName;
380
        $suffix = substr($adminClassBasename, -5) == 'Admin' ? substr($adminClassBasename, 0, -5) : $adminClassBasename;
381
        $suffix = str_replace('\\', '.', $suffix);
382
383
        return Container::underscore(sprintf(
384
            '%s.admin.%s',
385
            $prefix,
386
            $suffix
387
        ));
388
    }
389
390
    /**
391
     * @return string[]
392
     */
393
    private function getAvailableManagerTypes()
394
    {
395
        $container = $this->getContainer();
396
397
        if (!$container instanceof Container) {
398
            return array();
399
        }
400
401
        if ($this->managerTypes === null) {
402
            $this->managerTypes = array();
403
404
            foreach ($container->getServiceIds() as $id) {
405
                if (strpos($id, 'sonata.admin.manager.') === 0) {
406
                    $managerType = substr($id, 21);
407
                    $this->managerTypes[$managerType] = $managerType;
408
                }
409
            }
410
        }
411
412
        return $this->managerTypes;
413
    }
414
415
    /**
416
     * @return KernelInterface
417
     */
418
    private function getKernel()
419
    {
420
        /* @var $application Application */
421
        $application = $this->getApplication();
422
423
        return $application->getKernel();
424
    }
425
426
    /**
427
     * @return QuestionHelper|DialogHelper
428
     */
429 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...
430
    {
431
        // NEXT_MAJOR: Remove this BC code for SensioGeneratorBundle 2.3/2.4 after dropping support for Symfony 2.3
432
        if (class_exists('Sensio\Bundle\GeneratorBundle\Command\Helper\DialogHelper')) {
433
            $questionHelper = $this->getHelper('dialog');
434
435
            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...
436
                $questionHelper = new DialogHelper();
437
                $this->getHelperSet()->set($questionHelper);
438
            }
439
        } else {
440
            $questionHelper = $this->getHelper('question');
441
442
            if (!$questionHelper instanceof QuestionHelper) {
443
                $questionHelper = new QuestionHelper();
444
                $this->getHelperSet()->set($questionHelper);
445
            }
446
        }
447
448
        return $questionHelper;
449
    }
450
}
451