Completed
Pull Request — master (#254)
by Loïc
01:47
created

MakeGrid::interact()   A

Complexity

Conditions 4
Paths 8

Size

Total Lines 36

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 36
rs 9.344
c 0
b 0
f 0
cc 4
nc 8
nop 3
1
<?php
2
3
/*
4
 * This file is part of monofony.
5
 *
6
 * (c) Mobizel
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
declare(strict_types=1);
13
14
namespace App\Maker;
15
16
use App\Maker\Helper\GridHelper;
17
use App\Maker\Helper\ResourceHelper;
18
use Symfony\Bundle\MakerBundle\ConsoleStyle;
19
use Symfony\Bundle\MakerBundle\DependencyBuilder;
20
use Symfony\Bundle\MakerBundle\Generator;
21
use Symfony\Bundle\MakerBundle\InputConfiguration;
22
use Symfony\Bundle\MakerBundle\Maker\AbstractMaker;
23
use Symfony\Component\Console\Command\Command;
24
use Symfony\Component\Console\Input\InputArgument;
25
use Symfony\Component\Console\Input\InputInterface;
26
use Symfony\Component\Console\Question\ChoiceQuestion;
27
use Symfony\Component\Filesystem\Filesystem;
28
use Symfony\Component\Yaml\Yaml;
29
use Webmozart\Assert\Assert;
30
31
final class MakeGrid extends AbstractMaker
32
{
33
    public static $defaultActionTypes = [
34
        'main' => [
35
            'create' => 'create',
36
        ],
37
        'item' => [
38
            'update' => 'update',
39
            'delete' => 'delete',
40
        ],
41
        'bulk' => [
42
            'delete' => 'delete',
43
        ],
44
    ];
45
46
    /** @var string */
47
    private $projectDir;
48
49
    /** @var ResourceHelper */
50
    private $resourceHelper;
51
52
    /** @var GridHelper */
53
    private $gridHelper;
54
55
    /** @var Filesystem */
56
    private $fileSystem;
57
58
    public function __construct(string $projectDir, ResourceHelper $resourceHelper, GridHelper $gridHelper)
59
    {
60
        $this->projectDir = $projectDir;
61
        $this->resourceHelper = $resourceHelper;
62
        $this->gridHelper = $gridHelper;
63
64
        $this->fileSystem = new Filesystem();
65
    }
66
67
    public static function getCommandName(): string
68
    {
69
        return 'make:sylius-grid';
70
    }
71
72
    public function configureCommand(Command $command, InputConfiguration $inputConfig)
73
    {
74
        $command
75
            ->setDescription('Creates a new grid')
76
            ->addArgument('section', InputArgument::REQUIRED, 'Section of the grid (backend or frontend)')
77
            ->addArgument('resource', InputArgument::REQUIRED, 'Resource alias of the grid')
78
        ;
79
80
        $inputConfig->setArgumentAsNonInteractive('resource');
81
        $inputConfig->setArgumentAsNonInteractive('section');
82
    }
83
84
    public function interact(InputInterface $input, ConsoleStyle $io, Command $command)
85
    {
86
        if (!$input->getArgument('section')) {
87
            $question = new ChoiceQuestion(
88
                'Please select a section for your grid',
89
                ['backend', 'frontend'],
90
                0
91
            );
92
93
            $section = $io->askQuestion($question);
94
95
            $input->setArgument('section', $section);
96
        }
97
98
        if (!$input->getArgument('resource')) {
99
            $aliases = $this->resourceHelper->getResourcesAliases();
100
            $question = new ChoiceQuestion(
101
                'Please select a resource for your grid',
102
                array_combine($aliases, $aliases),
103
                0
104
            );
105
            $question->setAutocompleterValues($aliases);
0 ignored issues
show
Documentation introduced by
$aliases is of type array, but the function expects a object<Symfony\Component...Question\iterable>|null.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
106
107
            $resourceAlias = $io->askQuestion($question);
108
109
            $input->setArgument('resource', $resourceAlias);
110
        }
111
112
        if ($resourceAlias = $input->getArgument('resource')) {
113
            Assert::true($this->resourceHelper->isResourceAliasExist($resourceAlias), sprintf(
0 ignored issues
show
Bug introduced by
It seems like $resourceAlias defined by $input->getArgument('resource') on line 112 can also be of type array<integer,string>; however, App\Maker\Helper\Resourc...:isResourceAliasExist() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
114
                    'Resource with alias %s not found',
115
                    $resourceAlias
116
                )
117
            );
118
        }
119
    }
120
121
    public function configureDependencies(DependencyBuilder $dependencies)
122
    {
123
    }
124
125
    public function generate(InputInterface $input, ConsoleStyle $io, Generator $generator)
126
    {
127
        $fields = [];
128
        $isFirstField = true;
129
        while (true) {
130
            $newField = $this->askForNextField($io, $isFirstField);
131
            $isFirstField = false;
132
133
            if (null === $newField) {
134
                break;
135
            }
136
137
            $fields = array_merge($fields, $newField);
138
        }
139
140
        $actions = [
141
            'main' => [],
142
            'item' => [],
143
            'bulk' => [],
144
        ];
145
146
        foreach ($actions as $section => $data) {
147
            $hasSectionAction = $io->confirm(sprintf('Do you have %s actions?', $section), true);
148
149
            if ($hasSectionAction) {
150
                $newActions = $this->askForNextAction($io, $section);
151
                $actions[$section] = array_merge($actions[$section], $newActions);
152
            } else {
153
                unset($actions[$section]);
154
            }
155
        }
156
157
        $this->generateGridConfigFile($input, $io, $generator, $fields, $actions);
158
    }
159
160
    private function askForNextField(ConsoleStyle $io, bool $isFirstField)
161
    {
162
        $io->writeln('');
163
164
        if ($isFirstField) {
165
            $questionText = 'New field name (press <return> to stop adding fields)';
166
        } else {
167
            $questionText = 'Add another field? Enter the field name (or press <return> to stop adding fields)';
168
        }
169
170
        $fieldName = $io->ask($questionText, null, function ($name) {
171
            // allow it to be empty
172
            if (!$name) {
173
                return $name;
174
            }
175
176
            return $name;
177
        });
178
179
        if (!$fieldName) {
180
            return null;
181
        }
182
183
        $fieldData = [];
184
        $fieldData['type'] = $io->choice('Enter the field type', ['string', 'datetime', 'twig'], 'string');
185
        $fieldData['label'] = $io->ask('Enter the field label', null);
186
        $isSortable = $io->confirm('Is the field sortable?', true);
187
188
        if ($isSortable) {
189
            $fieldData['sortable'] = $io->ask('Enter the sortable path (leave blank to set the default value)', null);
190
        }
191
192
        if ('twig' === $fieldData['type']) {
193
            $fieldData['options'] = [];
194
            $fieldData['options']['template'] = $io->ask('Enter the twig template path', null);
195
        }
196
197
        if ('datetime' === $fieldData['type']) {
198
            $fieldData['options'] = [];
199
            $fieldData['options']['format'] = $io->ask('Enter the date format', 'Y-m-d H:i:s');
200
        }
201
202
        return [
203
            $fieldName => $fieldData,
204
        ];
205
    }
206
207
    private function askForNextAction(ConsoleStyle $io, string $section)
208
    {
209
        $io->writeln('');
210
211
        $actionTypes = static::$defaultActionTypes[$section];
212
213
        if (count($actionTypes) > 1) {
214
            $choiceQuestion = new ChoiceQuestion(
215
                'Enter the action types (coma-separated)',
216
                static::$defaultActionTypes[$section],
217
                implode(', ', static::$defaultActionTypes[$section])
218
            );
219
            $choiceQuestion->setMultiselect(true);
220
            $choiceQuestion->setAutocompleterValues($actionTypes);
221
            $actionTypes = $io->askQuestion($choiceQuestion);
222
        }
223
224
        $data = [];
225
        foreach ($actionTypes as $type) {
226
            $data[$type] = [
227
                'type' => $type,
228
            ];
229
        }
230
231
        return $data;
232
    }
233
234
    private function generateGridConfigFile(
235
        InputInterface $input,
236
        ConsoleStyle $io,
0 ignored issues
show
Unused Code introduced by
The parameter $io is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
237
        Generator $generator,
0 ignored issues
show
Unused Code introduced by
The parameter $generator is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
238
        array $fields,
239
        array $actions
240
    ): void {
241
        $section = $input->getArgument('section');
242
        $resourceAlias = $input->getArgument('resource');
243
        [$appName, $resourceName] = $this->resourceHelper->splitResourceAlias($resourceAlias);
0 ignored issues
show
Bug introduced by
It seems like $resourceAlias defined by $input->getArgument('resource') on line 242 can also be of type array<integer,string> or null; however, App\Maker\Helper\Resourc...r::splitResourceAlias() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
244
        $gridId = sprintf('%s_%s_%s', $appName, $section, $resourceName);
245
246
        $gridConfigDir = $this->getGridConfigDir($resourceAlias, $section);
0 ignored issues
show
Bug introduced by
It seems like $resourceAlias defined by $input->getArgument('resource') on line 242 can also be of type array<integer,string> or null; however, App\Maker\MakeGrid::getGridConfigDir() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
Bug introduced by
It seems like $section defined by $input->getArgument('section') on line 241 can also be of type array<integer,string> or null; however, App\Maker\MakeGrid::getGridConfigDir() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
247
        $modelClass = $this->resourceHelper->getResourceModelFromAlias($resourceAlias);
0 ignored issues
show
Bug introduced by
It seems like $resourceAlias defined by $input->getArgument('resource') on line 242 can also be of type array<integer,string> or null; however, App\Maker\Helper\Resourc...esourceModelFromAlias() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
248
249
        $gridData = [
250
            'driver' => [
251
                'name' => 'doctrine/orm',
252
                'options' => [
253
                    'class' => '"%'.$modelClass.'%"',
254
                ],
255
            ],
256
        ];
257
258
        if (count($fields) > 0) {
259
            $gridData['fields'] = $fields;
260
        }
261
262
        if (count($actions) > 0) {
263
            $gridData['actions'] = $actions;
264
        }
265
266
        $data = [
267
            'sylius_grid' => [
268
                'grids' => [
269
                    $gridId => $gridData,
270
                ],
271
            ],
272
        ];
273
274
        $yaml = Yaml::dump($data, 10, 4, Yaml::DUMP_NULL_AS_TILDE);
275
276
        $this->fileSystem->dumpFile($gridConfigDir, $yaml);
277
    }
278
279
    private function getGridConfigDir(string $resourceAlias, string $section)
280
    {
281
        $resource = $this->resourceHelper->getResourceNameFromAlias($resourceAlias);
282
        $filename = $resource.'.yaml';
283
284
        return sprintf('%s/%s/%s', $this->projectDir.'/config/packages/grids', $section, $filename);
285
    }
286
}
287