Completed
Pull Request — experimental/sf (#3233)
by chihiro
38:48
created

PluginGenerateCommand::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 1
dl 0
loc 5
rs 10
c 0
b 0
f 0
1
<?php
2
3
/*
4
 * This file is part of EC-CUBE
5
 *
6
 * Copyright(c) LOCKON CO.,LTD. All Rights Reserved.
7
 *
8
 * http://www.lockon.co.jp/
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 Eccube\Command;
15
16
use Symfony\Component\Console\Command\Command;
17
use Symfony\Component\Console\Exception\InvalidArgumentException;
18
use Symfony\Component\Console\Input\InputInterface;
19
use Symfony\Component\Console\Input\InputOption;
20
use Symfony\Component\Console\Output\OutputInterface;
21
use Symfony\Component\Console\Style\SymfonyStyle;
22
use Symfony\Component\DependencyInjection\Container;
23
use Symfony\Component\DependencyInjection\ContainerInterface;
24
use Symfony\Component\Filesystem\Filesystem;
25
26
class PluginGenerateCommand extends Command
0 ignored issues
show
introduced by
Missing class doc comment
Loading history...
27
{
28
    protected static $defaultName = 'eccube:plugin:generate';
29
30
    /**
31
     * @var SymfonyStyle
32
     */
33
    protected $io;
34
35
    /**
36
     * @var Filesystem
37
     */
38
    protected $fs;
39
40
    /**
41
     * @var ContainerInterface
42
     */
43
    protected $container;
44
45
    public function __construct(ContainerInterface $container)
46
    {
47
        parent::__construct();
48
        $this->container = $container;
49
    }
50
51
    protected function configure()
52
    {
53
        $this
54
            ->addArgument('name', InputOption::VALUE_REQUIRED, 'plugin name')
55
            ->addArgument('code', InputOption::VALUE_REQUIRED, 'plugin code')
56
            ->addArgument('ver', InputOption::VALUE_REQUIRED, 'plugin version')
57
            ->setDescription('Generate plugin skeleton.');
58
    }
59
60
    protected function initialize(InputInterface $input, OutputInterface $output)
61
    {
62
        $this->io = new SymfonyStyle($input, $output);
63
        $this->fs = new Filesystem();
64
    }
65
66
    protected function interact(InputInterface $input, OutputInterface $output)
67
    {
68
        if (null !== $input->getArgument('name') && null !== $input->getArgument('code') && null !== $input->getArgument('ver')) {
69
            return;
70
        }
71
72
        $this->io->title('EC-CUBE Plugin Generator Interactive Wizard');
73
74
        // Plugin name.
75
        $name = $input->getArgument('name');
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $name is correct as $input->getArgument('name') (which targets Symfony\Component\Consol...nterface::getArgument()) seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
76 View Code Duplication
        if (null !== $name) {
77
            $this->io->text(' > <info>name</info>: '.$name);
78
        } else {
79
            $name = $this->io->ask('name', 'EC-CUBE Sample Plugin');
80
            $input->setArgument('name', $name);
81
        }
82
83
        // Plugin code.
84
        $code = $input->getArgument('code');
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $code is correct as $input->getArgument('code') (which targets Symfony\Component\Consol...nterface::getArgument()) seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
85 View Code Duplication
        if (null !== $code) {
86
            $this->io->text(' > <info>code</info>: '.$code);
87
        } else {
88
            $code = $this->io->ask('code', 'Sample', [$this, 'validateCode']);
89
            $input->setArgument('code', $code);
90
        }
91
92
        // Plugin version.
93
        $version = $input->getArgument('ver');
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $version is correct as $input->getArgument('ver') (which targets Symfony\Component\Consol...nterface::getArgument()) seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
94 View Code Duplication
        if (null !== $version) {
95
            $this->io->text(' > <info>ver</info>: '.$version);
96
        } else {
97
            $version = $this->io->ask('ver', '1.0.0', [$this, 'validateVersion']);
98
            $input->setArgument('ver', $version);
99
        }
100
    }
101
102
    protected function execute(InputInterface $input, OutputInterface $output)
103
    {
104
        $name = $input->getArgument('name');
105
        $code = $input->getArgument('code');
106
        $version = $input->getArgument('ver');
107
108
        $this->validateCode($code);
109
        $this->validateVersion($version);
0 ignored issues
show
Unused Code introduced by
The call to the method Eccube\Command\PluginGen...mand::validateVersion() seems un-needed as the method has no side-effects.

PHP Analyzer performs a side-effects analysis of your code. A side-effect is basically anything that might be visible after the scope of the method is left.

Let’s take a look at an example:

class User
{
    private $email;

    public function getEmail()
    {
        return $this->email;
    }

    public function setEmail($email)
    {
        $this->email = $email;
    }
}

If we look at the getEmail() method, we can see that it has no side-effect. Whether you call this method or not, no future calls to other methods are affected by this. As such code as the following is useless:

$user = new User();
$user->getEmail(); // This line could safely be removed as it has no effect.

On the hand, if we look at the setEmail(), this method _has_ side-effects. In the following case, we could not remove the method call:

$user = new User();
$user->setEmail('email@domain'); // This line has a side-effect (it changes an
                                 // instance variable).
Loading history...
110
111
        $pluginDir = $this->container->getParameter('kernel.project_dir').'/app/Plugin/'.$code;
112
113
        $this->createDirectories($pluginDir);
114
        $this->createConfig($pluginDir, $name, $code, $version);
115
        $this->createEvent($pluginDir, $code);
116
        $this->createMessages($pluginDir, $code);
0 ignored issues
show
Unused Code introduced by
The call to PluginGenerateCommand::createMessages() has too many arguments starting with $code.

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.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
117
        $this->createNav($pluginDir, $code);
118
        $this->createTwigBlock($pluginDir, $code);
119
        $this->createConfigController($pluginDir, $code);
120
121
        $this->io->success(sprintf('Plugin was successfully created: %s %s %s', $name, $code, $version));
122
    }
123
124
    public function validateCode($code)
0 ignored issues
show
introduced by
Declare public methods first, then protected ones and finally private ones
Loading history...
125
    {
126
        if (empty($code)) {
127
            throw new InvalidArgumentException('The code can not be empty.');
128
        }
129
        if (strlen($code) > 255) {
130
            throw new InvalidArgumentException('The code can enter up to 255 characters');
131
        }
132
        if (1 !== preg_match('/^\w+$/', $code)) {
133
            throw new InvalidArgumentException('The code [a-zA-Z_] is available.');
134
        }
135
136
        $pluginDir = $this->container->getParameter('kernel.project_dir').'/app/Plugin/'.$code;
137
        if (file_exists($pluginDir)) {
138
            throw new InvalidArgumentException('Plugin directory exists.');
139
        }
140
141
        return $code;
142
    }
143
144
    public function validateVersion($version)
145
    {
146
        // TODO
147
        return $version;
148
    }
149
150
    protected function createDirectories($pluginDir)
151
    {
152
        $dirs = [
153
            'Controller/Admin',
154
            'Entity',
155
            'Repository',
156
            'Form/Type',
157
            'Form/Extension',
158
            'Resource/doctrine',
159
            'Resource/locale',
160
            'Resource/template/admin',
161
        ];
162
163
        foreach ($dirs as $dir) {
164
            $this->fs->mkdir($pluginDir.'/'.$dir);
165
        }
166
    }
167
168
    protected function createConfig($pluginDir, $name, $code, $version)
169
    {
170
        $source = <<<EOL
171
name: $name
172
code: $code
173
version: $version
174
EOL;
175
176
        $this->fs->dumpFile($pluginDir.'/config.yml', $source);
177
    }
178
179
    protected function createMessages($pluginDir)
180
    {
181
        $this->fs->dumpFile($pluginDir.'/Resource/locale/messages.ja.yaml', '');
182
        $this->fs->dumpFile($pluginDir.'/Resource/locale/validators.ja.yaml', '');
183
    }
184
185
    protected function createTwigBlock($pluginDir, $code)
186
    {
187
        $source = <<<EOL
188
<?php
189
190
namespace Plugin\\${code};
191
192
use Eccube\\Common\\EccubeTwigBlock;
193
194
class TwigBlock implements EccubeTwigBlock
195
{
196
    /**
197
     * @return array
198
     */
199
    public static function getTwigBlock()
200
    {
201
        return [];
202
    }
203
}
204
205
EOL;
206
        $this->fs->dumpFile($pluginDir.'/TwigBlock.php', $source);
207
    }
208
209
    protected function createNav($pluginDir, $code)
210
    {
211
        $source = <<<EOL
212
<?php
213
214
namespace Plugin\\${code};
215
216
use Eccube\\Common\\EccubeNav;
217
218
class Nav implements EccubeNav
219
{
220
    /**
221
     * @return array
222
     */
223
    public static function getNav()
224
    {
225
        return [];
226
    }
227
}
228
229
EOL;
230
        $this->fs->dumpFile($pluginDir.'/Nav.php', $source);
231
    }
232
233
    protected function createEvent($pluginDir, $code)
234
    {
235
        $source = <<<EOL
236
<?php
237
238
namespace Plugin\\${code};
239
240
use Symfony\\Component\\EventDispatcher\\EventSubscriberInterface;
241
242
class Event implements EventSubscriberInterface
243
{
244
    /**
245
     * @return array
246
     */
247
    public static function getSubscribedEvents()
248
    {
249
        return [];
250
    }
251
}
252
253
EOL;
254
        $this->fs->dumpFile($pluginDir.'/Event.php', $source);
255
    }
256
257
    protected function createConfigController($pluginDir, $code)
258
    {
259
        $snakecased = Container::underscore($code);
260
261
        $source = <<<EOL
262
<?php
263
264
namespace Plugin\\${code}\\Controller\\Admin;
265
266
use Eccube\\Controller\\AbstractController;
267
use Plugin\\${code}\\Form\\Type\\Admin\\ConfigType;
268
use Plugin\\${code}\\Repository\\ConfigRepository;
269
use Sensio\\Bundle\\FrameworkExtraBundle\\Configuration\\Route;
270
use Sensio\\Bundle\\FrameworkExtraBundle\\Configuration\\Template;
271
use Symfony\\Component\\HttpFoundation\\Request;
272
273
class ConfigController extends AbstractController
274
{
275
    /**
276
     * @var ConfigRepository
277
     */
278
    protected \$configRepository;
279
280
    /**
281
     * ConfigController constructor.
282
     *
283
     * @param ConfigRepository \$configRepository
284
     */
285
    public function __construct(ConfigRepository \$configRepository)
286
    {
287
        \$this->configRepository = \$configRepository;
288
    }
289
    /**
290
     * @Route("/%eccube_admin_route%/${snakecased}/config", name="plugin_${code}_config")
291
     * @Template("@${code}/admin/config.twig")
292
     */
293
    public function index(Request \$request)
294
    {
295
        \$Config = \$this->configRepository->get();
296
        \$form = \$this->createForm(ConfigType::class, \$Config);
297
        \$form->handleRequest(\$request);
298
299
        if (\$form->isSubmitted() && \$form->isValid()) {
300
            \$Config = \$form->getData();
301
            \$this->entityManager->persist(\$Config);
302
            \$this->entityManager->flush(\$Config);
303
            \$this->addSuccess('登録しました。', 'admin');
304
305
            return \$this->redirectToRoute('plugin_${code}_config');
306
        }
307
308
        return [
309
            'form' => \$form->createView(),
310
        ];
311
    }
312
}
313
EOL;
314
315
        $this->fs->dumpFile($pluginDir.'/Controller/Admin/ConfigController.php', $source);
316
317
        $source = <<<EOL
318
<?php
319
320
namespace Plugin\\${code}\\Entity;
321
322
use Doctrine\\ORM\\Mapping as ORM;
323
324
/**
325
 * Config
326
 *
327
 * @ORM\Table(name="plg_${snakecased}_config")
328
 * @ORM\Entity(repositoryClass="Plugin\\${code}\\Repository\\ConfigRepository")
329
 */
330
class Config
331
{
332
    /**
333
     * @var int
334
     *
335
     * @ORM\Column(name="id", type="integer", options={"unsigned":true})
336
     * @ORM\Id
337
     * @ORM\GeneratedValue(strategy="IDENTITY")
338
     */
339
    private \$id;
340
341
    /**
342
     * @var string
343
     *
344
     * @ORM\Column(name="name", type="string", length=255)
345
     */
346
    private \$name;
347
348
    /**
349
     * @return int
350
     */
351
    public function getId()
352
    {
353
        return \$this->id;
354
    }
355
356
    /**
357
     * @return string
358
     */
359
    public function getName()
360
    {
361
        return \$this->name;
362
    }
363
364
    /**
365
     * @param string \$name
366
     *
367
     * @return \$this;
368
     */
369
    public function setName(\$name)
370
    {
371
        \$this->name = \$name;
372
373
        return \$this;
374
    }
375
}
376
EOL;
377
378
        $this->fs->dumpFile($pluginDir.'/Entity/Config.php', $source);
379
380
        $source = <<<EOL
381
<?php
382
383
namespace Plugin\\${code}\\Repository;
384
385
use Eccube\\Repository\\AbstractRepository;
386
use Plugin\\${code}\\Entity\\Config;
387
use Symfony\\Bridge\\Doctrine\\RegistryInterface;
388
use Symfony\\Component\\HttpKernel\\KernelInterface;
389
390
/**
391
 * ConfigRepository
392
 *
393
 * This class was generated by the Doctrine ORM. Add your own custom
394
 * repository methods below.
395
 */
396
class ConfigRepository extends AbstractRepository
397
{
398
    /**
399
     * ConfigRepository constructor.
400
     *
401
     * @param RegistryInterface \$registry
402
     */
403
    public function __construct(RegistryInterface \$registry)
404
    {
405
        parent::__construct(\$registry, Config::class);
406
    }
407
408
    /**
409
     * @param int \$id
410
     * @return null|Config
411
     */
412
    public function get(\$id = 1)
413
    {
414
        return \$this->find(\$id);
415
    }
416
}
417
418
EOL;
419
420
        $this->fs->dumpFile($pluginDir.'/Repository/ConfigRepository.php', $source);
421
422
        $source = <<<EOL
423
<?php
424
425
namespace Plugin\\${code}\\Form\\Type\\Admin;
426
427
use Plugin\\${code}\\Entity\\Config;
428
use Symfony\\Component\\Form\\AbstractType;
429
use Symfony\\Component\\Form\\Extension\\Core\\Type\\TextType;
430
use Symfony\\Component\\Form\\FormBuilderInterface;
431
use Symfony\\Component\\OptionsResolver\\OptionsResolver;
432
use Symfony\Component\Validator\Constraints\Length;
433
use Symfony\Component\Validator\Constraints\NotBlank;
434
435
class ConfigType extends AbstractType
436
{
437
    /**
438
     * {@inheritdoc}
439
     */
440
    public function buildForm(FormBuilderInterface \$builder, array \$options)
441
    {
442
        \$builder->add('name', TextType::class, [
443
            'constraints' => [
444
                new NotBlank(),
445
                new Length(['max' => 255]),                
446
            ],
447
        ]);
448
    }
449
450
    /**
451
     * {@inheritdoc}
452
     */
453
    public function configureOptions(OptionsResolver \$resolver)
454
    {
455
        \$resolver->setDefaults([
456
            'data_class' => Config::class,
457
        ]);
458
    }
459
}
460
461
EOL;
462
463
        $this->fs->dumpFile($pluginDir.'/Form/Type/Admin/ConfigType.php', $source);
464
465
        $source = <<<EOL
466
{% extends '@admin/styleguide_frame.twig' %}
467
468
{% set menus = ['store', 'plugin', 'plugin_list'] %}
469
470
{% block title %}${code}{% endblock %}
471
{% block sub_title %}プラグイン一覧{% endblock %}
472
473
{% form_theme form '@admin/Form/bootstrap_4_horizontal_layout.html.twig' %}
474
475
{% block stylesheet %}{% endblock stylesheet %}
476
477
{% block javascript %}{% endblock javascript %}
478
479
{% block main %}
480
    <form role="form" method="post">
481
482
        {{ form_widget(form._token) }}
483
484
        <div class="c-contentsArea__cols">
485
            <div class="c-contentsArea__primaryCol">
486
                <div class="c-primaryCol">
487
                    <div class="card rounded border-0 mb-4">
488
                        <div class="card-header"><span>設定</span></div>
489
                        <div class="card-body">
490
                            <div class="row">
491
                                <div class="col-3"><span>名前</span><span
492
                                            class="badge badge-primary ml-1">必須</span></div>
493
                                <div class="col mb-2">
494
                                    {{ form_widget(form.name) }}
495
                                    {{ form_errors(form.name) }}
496
                                </div>
497
                            </div>
498
                        </div>
499
                    </div>
500
                </div>
501
            </div>
502
        </div>
503
        <div class="c-conversionArea">
504
            <div class="c-conversionArea__container">
505
                <div class="row justify-content-between align-items-center">
506
                    <div class="col-6">
507
                        <div class="c-conversionArea__leftBlockItem">
508
                            <a class="c-beseLink"
509
                               href="{{ url('admin_store_plugin') }}">
510
                                <i class="fa fa-backward" aria-hidden="true"></i>
511
                                <span>プラグイン一覧</span>
512
                            </a>
513
                        </div>
514
                    </div>
515
                    <div class="col-6">
516
                        <div class="row align-items-center justify-content-end">
517
                            <div class="col-auto">
518
                                <button class="btn btn-ec-conversion px-5"
519
                                        type="submit">登録</button>
520
                            </div>
521
                        </div>
522
                    </div>
523
                </div>
524
            </div>
525
        </div>
526
    </form>
527
{% endblock %}
528
EOL;
529
        $this->fs->dumpFile($pluginDir.'/Resource/template/admin/config.twig', $source);
530
    }
531
}
532