Completed
Push — feature/php-7.1-partial ( 412847 )
by Tom
03:40
created

MakeTableCommand::askForIdentityColumn()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 20
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 20
rs 9.2
c 0
b 0
f 0
cc 4
eloc 14
nc 3
nop 4
1
<?php
2
/**
3
 * Copyright © 2016 netz98 new media GmbH. All rights reserved.
4
 * See COPYING.txt for license details.
5
 */
6
7
namespace N98\Magento\Command\Developer\Console;
8
9
use Magento\Framework\DB\Ddl\Table;
10
use N98\Magento\Command\Developer\Console\Renderer\PHPCode\TableRenderer;
11
use N98\Magento\Command\Developer\Console\Structure\DDLTable;
12
use N98\Magento\Command\Developer\Console\Structure\DDLTableColumn;
13
use Symfony\Component\Console\Helper\QuestionHelper;
14
use Symfony\Component\Console\Input\InputInterface;
15
use Symfony\Component\Console\Output\OutputInterface;
16
use Symfony\Component\Console\Question\ChoiceQuestion;
17
use Symfony\Component\Console\Question\ConfirmationQuestion;
18
use Symfony\Component\Console\Question\Question;
19
20
class MakeTableCommand extends AbstractGeneratorCommand
21
{
22
    /**
23
     * @var array
24
     */
25
    private $columnTypes = [
26
        Table::TYPE_BOOLEAN,
27
        Table::TYPE_SMALLINT,
28
        Table::TYPE_INTEGER,
29
        Table::TYPE_BIGINT,
30
        Table::TYPE_FLOAT,
31
        Table::TYPE_NUMERIC,
32
        Table::TYPE_DECIMAL,
33
        Table::TYPE_DATE,
34
        Table::TYPE_TIMESTAMP,
35
        Table::TYPE_DATETIME,
36
        Table::TYPE_TEXT,
37
        Table::TYPE_BLOB,
38
        Table::TYPE_VARBINARY,
39
    ];
40
41
    /**
42
     * @var string
43
     */
44
    private $identityColumn = null;
45
46
    protected function configure()
47
    {
48
        $this
49
            ->setName('make:table')
50
            ->setDescription('Creates a new database table')
51
        ;
52
    }
53
54
    /**
55
     * @param InputInterface $input
56
     * @param OutputInterface $output
57
     *
58
     * @return int|void
59
     */
60
    protected function execute(InputInterface $input, OutputInterface $output)
61
    {
62
        /** @var QuestionHelper $questionHelper */
63
        $questionHelper = $this->getHelper('question');
64
65
        $table = new DDLTable();
66
67
        $this->askForTableName($input, $output, $questionHelper, $table);
68
        $this->processColumns($input, $output, $questionHelper, $table);
69
        $this->askForTableComment($input, $output, $questionHelper, $table);
70
71
        $this->generateCode($input, $output, $table);
72
    }
73
74
    /**
75
     * @param InputInterface $input
76
     * @param OutputInterface $output
77
     * @param QuestionHelper $questionHelper
78
     * @param DDLTable $table
79
     * @return void
80
     */
81
    private function processColumns(
82
        InputInterface $input,
83
        OutputInterface $output,
84
        QuestionHelper $questionHelper,
85
        DDLTable $table
86
    ) {
87
        $columns = [];
88
89
        do {
90
            $columns[] = $this->processColumn($input, $output, $questionHelper);
91
92
            $question = new ConfirmationQuestion('<question>Add a new column? [y/n]</question>:');
93
        } while ($questionHelper->ask($input, $output, $question));
94
95
        $table->setColumns($columns);
96
    }
97
98
    /**
99
     * @param InputInterface $input
100
     * @param OutputInterface $output
101
     * @param QuestionHelper $questionHelper
102
     * @return DDLTableColumn
103
     */
104
    private function processColumn(
105
        InputInterface $input,
106
        OutputInterface $output,
107
        QuestionHelper $questionHelper
108
    ) {
109
        $column = new DDLTableColumn();
110
111
        $this->askForColumnName($input, $output, $questionHelper, $column);
112
        $this->askForColumnType($input, $output, $questionHelper, $column);
113
        $this->askForIdentityColumn($input, $output, $questionHelper, $column);
114
        $this->askForColumnSize($input, $output, $questionHelper, $column);
115
        $this->askForColumnIsNullable($input, $output, $questionHelper, $column);
116
        $this->askForColumnIsUnsigned($input, $output, $questionHelper, $column);
117
        $this->askForColumnDefault($input, $output, $questionHelper, $column);
118
        $this->askForColumnComment($input, $output, $questionHelper, $column);
119
120
        $output->writeln('');
121
122
        return $column;
123
    }
124
125
    /**
126
     * @param InputInterface $input
127
     * @param OutputInterface $output
128
     * @param QuestionHelper $questionHelper
129
     * @param DDLTableColumn $column
130
     * @return void
131
     */
132 View Code Duplication
    private function askForColumnName(
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...
133
        InputInterface $input,
134
        OutputInterface $output,
135
        QuestionHelper $questionHelper,
136
        DDLTableColumn $column
137
    ) {
138
        $columnNameQuestion = new Question('<question>Column name:</question>');
139
        $columnNameQuestion->setValidator(function ($answer) {
140
            if (empty($answer)) {
141
                throw new \RuntimeException('Column name could not be empty');
142
            }
143
144
            return $answer;
145
        });
146
147
        $column->setName($questionHelper->ask($input, $output, $columnNameQuestion));
148
    }
149
150
    /**
151
     * @param InputInterface $input
152
     * @param OutputInterface $output
153
     * @param QuestionHelper $questionHelper
154
     * @param DDLTableColumn $column
155
     * @return void
156
     */
157
    private function askForIdentityColumn(
158
        InputInterface $input,
159
        OutputInterface $output,
160
        QuestionHelper $questionHelper,
161
        DDLTableColumn $column
162
    ) {
163
        if (!empty($this->identityColumn) || !$column->isIntType()) { // there can only be one identity column
164
            return;
165
        }
166
167
        $columnNameQuestion = new ConfirmationQuestion('<question>Is this your identity column? (y/n)</question>');
168
169
        if ($questionHelper->ask($input, $output, $columnNameQuestion)) {
170
            $this->identityColumn = $column->getName();
171
            $column->setUnsigned(true);
172
            $column->setPrimary(true);
173
            $column->setNullable(false);
174
            $column->setIdentity(true);
175
        }
176
    }
177
178
    /**
179
     * @param InputInterface $input
180
     * @param OutputInterface $output
181
     * @param QuestionHelper $questionHelper
182
     * @param DDLTableColumn $data
183
     * @return void
184
     */
185
    private function askForColumnType(
186
        InputInterface $input,
187
        OutputInterface $output,
188
        QuestionHelper $questionHelper,
189
        DDLTableColumn $data
190
    ) {
191
        $columnTypeQuestion = new ChoiceQuestion('<question>Column type:</question>', $this->columnTypes);
192
        $columnTypeQuestion->setErrorMessage('Type %s is invalid.');
193
194
        $data->setType($questionHelper->ask($input, $output, $columnTypeQuestion));
195
    }
196
197
    /**
198
     * @param InputInterface $input
199
     * @param OutputInterface $output
200
     * @param QuestionHelper $questionHelper
201
     * @param DDLTableColumn $column
202
     * @return void
203
     */
204 View Code Duplication
    private function askForColumnIsUnsigned(
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...
205
        InputInterface $input,
206
        OutputInterface $output,
207
        QuestionHelper $questionHelper,
208
        DDLTableColumn $column
209
    ) {
210
        if (!$column->isIntType()) {
211
            return;
212
        }
213
214
        if ($column->getUnsigned() !== null) {
215
            return;
216
        }
217
218
        $columnTypeQuestion = new ConfirmationQuestion(
219
            '<question>Is column unsigned?</question><info>(default yes)</info>'
220
        );
221
222
        $column->setUnsigned($questionHelper->ask($input, $output, $columnTypeQuestion));
0 ignored issues
show
Documentation introduced by
$questionHelper->ask($in...t, $columnTypeQuestion) is of type string, but the function expects a boolean.

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...
223
    }
224
225
    /**
226
     * @param InputInterface $input
227
     * @param OutputInterface $output
228
     * @param QuestionHelper $questionHelper
229
     * @param DDLTableColumn $column
230
     * @return void
231
     */
232
    private function askForColumnSize(
233
        InputInterface $input,
234
        OutputInterface $output,
235
        QuestionHelper $questionHelper,
236
        DDLTableColumn $column
237
    ) {
238
        if (!$column->isTypeWithSize()) {
239
            return;
240
        }
241
242
        $default = null;
243
244
        if ($column->getType() == Table::TYPE_TEXT) {
245
            $default = 255;
246
        }
247
248
        $columnNameQuestion = new Question(
249
            '<question>Column size:</question> <info>(default: ' . $default . ')</info>',
250
            $default
251
        );
252
        $columnNameQuestion->setValidator(function ($answer) {
253
            if (empty($answer)) {
254
                throw new \RuntimeException('Column size could not be empty');
255
            }
256
257
            if ($answer <= 0) {
258
                throw new \RuntimeException('Column size could be greater than zero');
259
            }
260
261
            return (int) $answer;
262
        });
263
264
        $column->setSize($questionHelper->ask($input, $output, $columnNameQuestion));
265
    }
266
267
    /**
268
     * @param InputInterface $input
269
     * @param OutputInterface $output
270
     * @param QuestionHelper $questionHelper
271
     * @param DDLTableColumn $column
272
     * @return void
273
     */
274 View Code Duplication
    private function askForColumnIsNullable(
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...
275
        InputInterface $input,
276
        OutputInterface $output,
277
        QuestionHelper $questionHelper,
278
        DDLTableColumn $column
279
    ) {
280
        if ($column->isNullable() !== null) {
281
            return;
282
        }
283
284
        $columnTypeQuestion = new ConfirmationQuestion(
285
            '<question>Is column nullable:</question><info>(default yes)</info>'
286
        );
287
288
        $column->setNullable($questionHelper->ask($input, $output, $columnTypeQuestion));
0 ignored issues
show
Documentation introduced by
$questionHelper->ask($in...t, $columnTypeQuestion) is of type string, but the function expects a boolean.

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...
289
    }
290
291
    /**
292
     * @param InputInterface $input
293
     * @param OutputInterface $output
294
     * @param QuestionHelper $questionHelper
295
     * @param DDLTableColumn $column
296
     * @return void
297
     */
298
    private function askForColumnDefault(
299
        InputInterface $input,
300
        OutputInterface $output,
301
        QuestionHelper $questionHelper,
302
        DDLTableColumn $column
303
    ) {
304
        if ($column->getIdentity()) {
305
            return;
306
        }
307
308
        $question = new Question('<question>Column default value:</question>');
309
        $question->setValidator(function ($answer) use ($column) {
310
            if ($column->isIntType() && !is_numeric($answer) && !empty($answer)) {
311
                throw new \InvalidArgumentException('Invalid default value');
312
            }
313
314
            return $answer;
315
        });
316
317
        $column->setDefault($questionHelper->ask($input, $output, $question));
318
    }
319
320
    /**
321
     * @param InputInterface $input
322
     * @param OutputInterface $output
323
     * @param QuestionHelper $questionHelper
324
     * @param DDLTableColumn $column
325
     * @return void
326
     */
327 View Code Duplication
    private function askForColumnComment(
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
        InputInterface $input,
329
        OutputInterface $output,
330
        QuestionHelper $questionHelper,
331
        DDLTableColumn $column
332
    ) {
333
        $columnNameQuestion = new Question('<question>Column comment:</question>');
334
        $columnNameQuestion->setValidator(function ($answer) {
335
            if (empty($answer)) {
336
                throw new \RuntimeException('Column comment could not be empty');
337
            }
338
339
            return $answer;
340
        });
341
342
        $column->setComment($questionHelper->ask($input, $output, $columnNameQuestion));
343
    }
344
345
    /**
346
     * @param InputInterface $input
347
     * @param OutputInterface $output
348
     * @param QuestionHelper $questionHelper
349
     * @param DDLTable $table
350
     * @return void
351
     */
352 View Code Duplication
    private function askForTableName(
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...
353
        InputInterface $input,
354
        OutputInterface $output,
355
        QuestionHelper $questionHelper,
356
        DDLTable $table
357
    ) {
358
        $question = new Question('<question>Table name:</question>');
359
        $question->setValidator(function ($answer) {
360
            if (empty($answer)) {
361
                throw new \RuntimeException('Table name could not be empty');
362
            }
363
364
            return $answer;
365
        });
366
367
        $table->setName($questionHelper->ask($input, $output, $question));
368
    }
369
370
    /**
371
     * @param InputInterface $input
372
     * @param OutputInterface $output
373
     * @param QuestionHelper $questionHelper
374
     * @param DDLTable $column
375
     * @return void
376
     */
377 View Code Duplication
    private function askForTableComment(
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...
378
        InputInterface $input,
379
        OutputInterface $output,
380
        QuestionHelper $questionHelper,
381
        DDLTable $column
382
    ) {
383
        $question = new Question('<question>Table comment:</question>');
384
        $question->setValidator(function ($answer) {
385
            if (empty($answer)) {
386
                throw new \RuntimeException('Table comment could not be empty');
387
            }
388
389
            return $answer;
390
        });
391
392
        $column->setComment($questionHelper->ask($input, $output, $question));
393
    }
394
395
    /**
396
     * @param InputInterface $input
397
     * @param OutputInterface $output
398
     * @param DDLTable $table
399
     * @return void
400
     */
401
    private function generateCode(
402
        InputInterface $input,
0 ignored issues
show
Unused Code introduced by
The parameter $input 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...
403
        OutputInterface $output,
0 ignored issues
show
Unused Code introduced by
The parameter $output 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...
404
        DDLTable $table
405
    ) {
406
        $renderer = new TableRenderer($table, $this->getHelper('twig'));
407
408
        echo $renderer->render();
409
    }
410
}
411