Completed
Push — 3.x ( 3e834f...38b337 )
by Grégoire
03:36
created

src/Maker/AdminMaker.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
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\Maker;
15
16
use Sonata\AdminBundle\Command\Validators;
17
use Sonata\AdminBundle\Manipulator\ServicesManipulator;
18
use Sonata\AdminBundle\Model\ModelManagerInterface;
19
use Symfony\Bundle\MakerBundle\ConsoleStyle;
20
use Symfony\Bundle\MakerBundle\DependencyBuilder;
21
use Symfony\Bundle\MakerBundle\Generator;
22
use Symfony\Bundle\MakerBundle\InputConfiguration;
23
use Symfony\Bundle\MakerBundle\Maker\AbstractMaker;
24
use Symfony\Bundle\MakerBundle\Util\ClassNameDetails;
25
use Symfony\Component\Console\Command\Command;
26
use Symfony\Component\Console\Input\InputArgument;
27
use Symfony\Component\Console\Input\InputInterface;
28
use Symfony\Component\Console\Input\InputOption;
29
use Symfony\Component\DependencyInjection\Container;
30
31
/**
32
 * @author Gaurav Singh Faujdar <[email protected]>
33
 */
34
final class AdminMaker extends AbstractMaker
35
{
36
    /**
37
     * @var string
38
     */
39
    private $projectDirectory;
40
    /**
41
     * @var string[]
42
     */
43
    private $availableModelManagers;
44
    /**
45
     * @var string
46
     */
47
    private $skeletonDirectory;
48
    /**
49
     * @var string
50
     */
51
    private $modelClass;
52
    /**
53
     * @var string
54
     */
55
    private $modelClassBasename;
56
    /**
57
     * @var string
58
     */
59
    private $adminClassBasename;
60
    /**
61
     * @var string
62
     */
63
    private $controllerClassBasename;
64
    /**
65
     * @var string
66
     */
67
    private $managerType;
68
    /**
69
     * @var ModelManagerInterface
70
     */
71
    private $modelManager;
72
73
    public function __construct($projectDirectory, array $modelManagers = [])
74
    {
75
        $this->projectDirectory = $projectDirectory;
76
        $this->availableModelManagers = $modelManagers;
77
        $this->skeletonDirectory = __DIR__.'/../Resources/skeleton';
78
    }
79
80
    public static function getCommandName(): string
81
    {
82
        return 'make:sonata:admin';
83
    }
84
85
    public function configureCommand(Command $command, InputConfiguration $inputConfig): void
86
    {
87
        $command
88
            ->setDescription('Generates an admin class based on the given model class')
89
            ->addArgument('model', InputArgument::REQUIRED, 'The fully qualified model class')
90
            ->addOption('admin', 'a', InputOption::VALUE_OPTIONAL, 'The admin class basename')
91
            ->addOption('controller', 'c', InputOption::VALUE_OPTIONAL, 'The controller class basename')
92
            ->addOption('manager', 'm', InputOption::VALUE_OPTIONAL, 'The model manager type')
93
            ->addOption('services', 's', InputOption::VALUE_OPTIONAL, 'The services YAML file', 'services.yaml')
94
            ->addOption('id', 'i', InputOption::VALUE_OPTIONAL, 'The admin service ID');
95
96
        $inputConfig->setArgumentAsNonInteractive('model');
97
    }
98
99
    public function interact(InputInterface $input, ConsoleStyle $io, Command $command): void
100
    {
101
        $io->section('Welcome to the Sonata Admin');
102
        $this->modelClass = $io->ask(
103
            'The fully qualified model class',
104
            $input->getArgument('model'),
105
            [Validators::class, 'validateClass']
106
        );
107
        $this->modelClassBasename = current(\array_slice(explode('\\', $this->modelClass), -1));
108
109
        $this->adminClassBasename = $io->ask(
110
            'The admin class basename',
111
            $input->getOption('admin') ?: $this->modelClassBasename.'Admin',
112
            [Validators::class, 'validateAdminClassBasename']
113
        );
114
        if (\count($this->availableModelManagers) > 1) {
115
            $managerTypes = array_keys($this->availableModelManagers);
116
            $this->managerType = $io->choice('The manager type', $managerTypes, $managerTypes[0]);
117
118
            $input->setOption('manager', $this->managerType);
119
        }
120
        if ($io->confirm('Do you want to generate a controller?', false)) {
121
            $this->controllerClassBasename = $io->ask(
122
                'The controller class basename',
123
                $input->getOption('controller') ?: $this->modelClassBasename.'AdminController',
124
                [Validators::class, 'validateControllerClassBasename']
125
            );
126
            $input->setOption('controller', $this->controllerClassBasename);
127
        }
128
        $input->setOption('services', false);
129
        if ($io->confirm('Do you want to update the services YAML configuration file?', true)) {
130
            $path = $this->projectDirectory.'/config/';
131
            $servicesFile = $io->ask(
132
                'The services YAML configuration file',
133
                is_file($path.'admin.yaml') ? 'admin.yaml' : 'services.yaml',
134
                [Validators::class, 'validateServicesFile']
135
            );
136
            $id = $io->ask(
137
                'The admin service ID',
138
                $this->getAdminServiceId($this->adminClassBasename),
139
                [Validators::class, 'validateServiceId']
140
            );
141
            $input->setOption('services', $servicesFile);
142
            $input->setOption('id', $id);
143
        }
144
        $input->setArgument('model', $this->modelClass);
145
        $input->setOption('admin', $this->adminClassBasename);
146
    }
147
148
    /**
149
     * Configure any library dependencies that your maker requires.
150
     */
151
    public function configureDependencies(DependencyBuilder $dependencies): void
152
    {
153
    }
154
155
    /**
156
     * Called after normal code generation: allows you to do anything.
157
     */
158
    public function generate(InputInterface $input, ConsoleStyle $io, Generator $generator): void
159
    {
160
        $this->configure($input);
161
162
        $adminClassNameDetails = $generator->createClassNameDetails(
163
            $this->adminClassBasename,
164
            'Admin\\',
165
            'Admin'
166
        );
167
168
        $adminClassFullName = $adminClassNameDetails->getFullName();
169
        $this->generateAdmin($io, $generator, $adminClassNameDetails);
170
171
        $controllerClassFullName = '';
172
        if ($this->controllerClassBasename) {
173
            $controllerClassNameDetails = $generator->createClassNameDetails(
174
                $this->controllerClassBasename,
175
                'Controller\\',
176
                'Controller'
177
            );
178
179
            $this->generateController($input, $io, $generator, $controllerClassNameDetails);
180
181
            $controllerClassFullName = $controllerClassNameDetails->getFullName();
182
        }
183
184
        $this->generateService($input, $io, $adminClassFullName, $controllerClassFullName);
185
    }
186
187
    private function getAdminServiceId(string $adminClassBasename): string
188
    {
189
        return Container::underscore(sprintf(
190
            'admin.%s',
191
            str_replace('\\', '.', 'Admin' === substr($adminClassBasename, -5) ?
192
                substr($adminClassBasename, 0, -5) : $adminClassBasename)
193
        ));
194
    }
195
196
    private function generateService(
197
        InputInterface $input,
198
        ConsoleStyle $io,
199
        string $adminClassFullName,
200
        string $controllerClassFullName
201
    ): void {
202
        if ($servicesFile = $input->getOption('services')) {
203
            $file = sprintf('%s/config/%s', $this->projectDirectory, $servicesFile);
204
            $servicesManipulator = new ServicesManipulator($file);
205
            $controllerName = $this->controllerClassBasename ? $controllerClassFullName : '~';
206
207
            $id = $input->getOption('id') ?:
208
                $this->getAdminServiceId('App', $this->adminClassBasename);
0 ignored issues
show
The call to AdminMaker::getAdminServiceId() has too many arguments starting with $this->adminClassBasename.

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...
209
210
            $servicesManipulator->addResource(
211
                $id,
212
                $this->modelClass,
213
                $adminClassFullName,
214
                $controllerName,
215
                substr($this->managerType, \strlen('sonata.admin.manager.'))
216
            );
217
218
            $io->writeln(sprintf(
219
                '%sThe service "<info>%s</info>" has been appended to the file <info>"%s</info>".',
220
                PHP_EOL,
221
                $id,
222
                realpath($file)
223
            ));
224
        }
225
    }
226
227
    private function generateController(
228
        InputInterface $input,
229
        ConsoleStyle $io,
230
        Generator $generator,
231
        ClassNameDetails $controllerClassNameDetails
232
    ): void {
233
        $controllerClassFullName = null;
234
        if ($controllerClassNameDetails) {
235
            $controllerClassFullName = $controllerClassNameDetails->getFullName();
236
            $generator->generateClass(
237
                $controllerClassFullName,
238
                $this->skeletonDirectory.'/AdminController.tpl.php',
239
                []
240
            );
241
            $generator->writeChanges();
242
            $io->writeln(sprintf(
243
                '%sThe controller class "<info>%s</info>" has been generated under the file "<info>%s</info>".',
244
                PHP_EOL,
245
                $controllerClassNameDetails->getShortName(),
246
                $controllerClassFullName
247
            ));
248
        }
249
    }
250
251
    private function generateAdmin(
252
        ConsoleStyle $io,
253
        Generator $generator,
254
        ClassNameDetails $adminClassNameDetails
255
    ): void {
256
        $adminClassFullName = $adminClassNameDetails->getFullName();
257
258
        $fields = $this->modelManager->getExportFields($this->modelClass);
259
        $fieldString = '';
260
        foreach ($fields as $field) {
261
            $fieldString = $fieldString.sprintf('%12s', '')."->add('".$field."')".PHP_EOL;
262
        }
263
264
        $fieldString .= sprintf('%12s', '');
265
266
        $generator->generateClass(
267
            $adminClassFullName,
268
            $this->skeletonDirectory.'/Admin.tpl.php',
269
            ['fields' => $fieldString]
270
        );
271
272
        $generator->writeChanges();
273
274
        $io->writeln(sprintf(
275
            '%sThe admin class "<info>%s</info>" has been generated under the file "<info>%s</info>".',
276
            PHP_EOL,
277
            $adminClassNameDetails->getShortName(),
278
            $adminClassFullName
279
        ));
280
    }
281
282
    private function configure(InputInterface $input): void
283
    {
284
        $this->modelClass = Validators::validateClass($input->getArgument('model'));
285
        $this->modelClassBasename = (new \ReflectionClass($this->modelClass))->getShortName();
286
        $this->adminClassBasename = Validators::validateAdminClassBasename(
287
            $input->getOption('admin') ?: $this->modelClassBasename.'Admin'
288
        );
289
290
        if ($this->controllerClassBasename = $input->getOption('controller')) {
291
            $this->controllerClassBasename = Validators::validateControllerClassBasename($this->controllerClassBasename);
292
        }
293
294
        if (0 === \count($this->availableModelManagers)) {
295
            throw new \InvalidArgumentException('There are no model managers registered.');
296
        }
297
298
        $this->managerType = $input->getOption('manager') ?: array_keys($this->availableModelManagers)[0];
299
        $this->modelManager = $this->availableModelManagers[$this->managerType] ?: current($this->availableModelManagers);
300
    }
301
}
302