Completed
Push — 4.0 ( 87d096...bcc1be )
by Kiyotaka
05:44 queued 11s
created

src/Eccube/Command/PluginGenerateCommand.php (1 issue)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

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