Completed
Push — master ( 650783...0c9838 )
by wen
15:06
created

ComponentMakeCommand::getOptions()   B

Complexity

Conditions 1
Paths 1

Size

Total Lines 29
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 29
rs 8.8571
c 0
b 0
f 0
cc 1
eloc 18
nc 1
nop 0
1
<?php
2
3
namespace Sco\Admin\Console;
4
5
use Doctrine\DBAL\Schema\Column;
6
use Illuminate\Console\GeneratorCommand;
7
use Illuminate\Database\Eloquent\Model;
8
use Illuminate\Support\Str;
9
use InvalidArgumentException;
10
use Symfony\Component\Console\Input\InputOption;
11
12
class ComponentMakeCommand extends GeneratorCommand
13
{
14
    /**
15
     * The console command name.
16
     *
17
     * @var string
18
     */
19
    protected $name = 'make:component';
20
21
    /**
22
     * The console command description.
23
     *
24
     * @var string
25
     */
26
    protected $description = 'Create a new component class(ScoAdmin)';
27
28
    /**
29
     * The type of class being generated.
30
     *
31
     * @var string
32
     */
33
    protected $type = 'Component';
34
35
    protected $columnTypeMappings = [
36
        'smallint' => 'text',
37
        'integer'  => 'text',
38
        'bigint'   => 'text',
39
        'float'    => 'text',
40
        'string'   => 'text',
41
        'text'     => 'text',
42
        'boolean'  => 'mapping',
43
        'datetime' => 'datetime',
44
        'date'     => 'datetime',
45
    ];
46
47
    protected $elementTypeMappings = [
48
        'smallint' => 'number',
49
        'integer'  => 'number',
50
        'bigint'   => 'number',
51
        'float'    => 'number',
52
        'string'   => 'text',
53
        'text'     => 'textarea',
54
        'boolean'  => 'elswitch',
55
        'datetime' => 'datetime',
56
        'date'     => 'date',
57
        'time'     => 'time',
58
    ];
59
60
    /**
61
     * Get the stub file for the generator.
62
     *
63
     * @return string
64
     */
65
    protected function getStub()
66
    {
67
        if ($this->option('model')) {
68
            return __DIR__ . '/stubs/component-model.stub';
69
        }
70
71
        return __DIR__ . '/stubs/component.stub';
72
    }
73
74
    /**
75
     * Build the class with the given name.
76
     *
77
     * @param string $name
78
     *
79
     * @return string
80
     */
81
    protected function buildClass($name)
82
    {
83
        $replace = $this->buildObserverReplacements();
84
85
        if ($this->option('model')) {
86
            $replace = $this->buildModelReplacements($replace);
87
        }
88
89
        return str_replace(
90
            array_keys($replace),
91
            array_values($replace),
92
            parent::buildClass($name)
93
        );
94
    }
95
96
    protected function buildObserverReplacements()
97
    {
98
        if ($this->option('observer')) {
99
            $observer = $this->parseObserver($this->option('observer'));
0 ignored issues
show
Bug introduced by
It seems like $this->option('observer') targeting Illuminate\Console\Command::option() can also be of type array; however, Sco\Admin\Console\Compon...ommand::parseObserver() does only seem to accept string, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
100
101
            if (! class_exists($observer)) {
102
                if ($this->confirm(
103
                    "A {$observer} observer does not exist. Do you want to generate it?",
104
                    true
105
                )) {
106
                    $this->call('make:observer', [
107
                        'name' => $observer,
108
                    ]);
109
                }
110
            }
111
        } else {
112
            $observer = \Sco\Admin\Component\Observer::class;
113
        }
114
115
        return [
116
            'DummyFullObserverClass' => $observer,
117
            'DummyObserverClass'     => class_basename($observer),
118
        ];
119
    }
120
121
    /**
122
     * Get the fully-qualified observer class name.
123
     *
124
     * @param  string $observer
125
     *
126
     * @return string
127
     */
128
    protected function parseObserver($observer)
129
    {
130
        if (preg_match('([^A-Za-z0-9_/\\\\])', $observer)) {
131
            throw new InvalidArgumentException('Observer name contains invalid characters.');
132
        }
133
134
        $observer = trim(str_replace('/', '\\', $observer), '\\');
135
136
        if (! Str::startsWith(
137
            $observer,
138
            $rootNamespace = $this->laravel->getNamespace()
139
        )) {
140
            $observer = $rootNamespace . $this->getComponentNamespace()
141
                . '\Observers\\'
142
                . $observer . 'Observer';
143
        }
144
145
        return $observer;
146
    }
147
148
    protected function buildModelReplacements(array $replace)
149
    {
150
        $modelClass = $this->parseModel($this->option('model'));
0 ignored issues
show
Bug introduced by
It seems like $this->option('model') targeting Illuminate\Console\Command::option() can also be of type array; however, Sco\Admin\Console\Compon...keCommand::parseModel() does only seem to accept string, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
151
152
        if (! class_exists($modelClass)) {
153
            if ($this->confirm(
154
                "A {$modelClass} model does not exist. Do you want to generate it?",
155
                true
156
            )) {
157
                $this->call('make:model', ['name' => $modelClass]);
158
            }
159
        }
160
161
        $columns = $this->getViewColumns($modelClass);
162
        $elements = $this->getFormElements($modelClass);
163
164
        return array_merge($replace, [
165
            'DummyFullModelClass' => $modelClass,
166
            'DummyColumns'        => $columns ? implode("\n", $columns) : '',
167
            'DummyElements'       => $elements ? implode("\n", $elements) : '',
168
        ]);
169
    }
170
171
    /**
172
     * Get the fully-qualified model class name.
173
     *
174
     * @param  string $model
175
     *
176
     * @return string
177
     */
178
    protected function parseModel($model)
179
    {
180
        if (preg_match('([^A-Za-z0-9_/\\\\])', $model)) {
181
            throw new InvalidArgumentException('Model name contains invalid characters.');
182
        }
183
184
        $model = trim(str_replace('/', '\\', $model), '\\');
185
186
        if (! Str::startsWith($model, $rootNamespace = $this->laravel->getNamespace())) {
187
            $model = $rootNamespace . $model;
188
        }
189
190
        return $model;
191
    }
192
193 View Code Duplication
    protected function getViewColumns($model)
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...
194
    {
195
        $columns = $this->getTableColumns($model);
196
        if (! $columns) {
197
            return;
198
        }
199
200
        $list = [];
201
        foreach ($columns as $column) {
202
            $list[] = $this->buildViewColumn($column);
203
        }
204
205
        return $list;
206
    }
207
208
    protected function buildViewColumn(Column $column)
209
    {
210
        return sprintf(
211
            "            AdminColumn::%s('%s', '%s'),",
212
            $this->getViewColumnType($column->getType()->getName()),
213
            $column->getName(),
214
            $this->getColumnTitle($column)
215
        );
216
    }
217
218
    protected function getColumnTitle(Column $column)
219
    {
220
        return $column->getComment() ?? studly_case($column->getName());
221
    }
222
223
    protected function getViewColumnType($name)
224
    {
225
        return $this->columnTypeMappings[$name] ?? 'text';
226
    }
227
228 View Code Duplication
    protected function getFormElements($model)
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...
229
    {
230
        $columns = $this->getTableColumns($model);
231
232
        if (! $columns) {
233
            return;
234
        }
235
236
        $list = [];
237
        foreach ($columns as $column) {
238
            if (! $column->getAutoincrement()) {
239
                $list[] = $this->buildFormElement($column);
240
            }
241
        }
242
243
        return $list;
244
    }
245
246
    protected function buildFormElement(Column $column)
247
    {
248
        return sprintf(
249
            "            AdminElement::%s('%s', '%s')->required(),",
250
            $this->getFormElementType($column->getType()->getName()),
251
            $column->getName(),
252
            $this->getColumnTitle($column)
253
        );
254
    }
255
256
    protected function getFormElementType($name)
257
    {
258
        return $this->elementTypeMappings[$name] ?? 'text';
259
    }
260
261
    protected function getTableColumns($class)
262
    {
263
        if (empty($class)) {
264
            return;
265
        }
266
267
        if (! class_exists($class)) {
268
            return;
269
        }
270
271
        $model = new $class();
272
        if (! ($model instanceof Model)) {
273
            return;
274
        }
275
        $schema = $model->getConnection()->getDoctrineSchemaManager();
276
277
        $table = $model->getConnection()->getTablePrefix() . $model->getTable();
278
279
        return $schema->listTableColumns($table);
280
    }
281
282
    /**
283
     * Get the default namespace for the class.
284
     *
285
     * @param  string $rootNamespace
286
     *
287
     * @return string
288
     */
289
    protected function getDefaultNamespace($rootNamespace)
290
    {
291
        return $rootNamespace . '\\' . $this->getComponentNamespace();
292
    }
293
294
    /**
295
     *
296
     * @return string
297
     */
298
    protected function getComponentNamespace()
299
    {
300
        return str_replace(
301
            '/',
302
            '\\',
303
            Str::after(
304
                config('admin.components'),
305
                app_path() . DIRECTORY_SEPARATOR
306
            )
307
        );
308
    }
309
310
    /**
311
     * Get the console command options.
312
     *
313
     * @return array
314
     */
315
    protected function getOptions()
316
    {
317
        return [
318
            [
319
                'observer',
320
                'o',
321
                InputOption::VALUE_OPTIONAL,
322
                'Generate a new access observer for the component.',
323
            ],
324
            [
325
                'force',
326
                null,
327
                InputOption::VALUE_NONE,
328
                'Generate the class even if the component already exists.',
329
            ],
330
            [
331
                'model',
332
                'm',
333
                InputOption::VALUE_OPTIONAL,
334
                'Generate a model for the component.',
335
            ],
336
            [
337
                'display',
338
                'd',
339
                InputOption::VALUE_OPTIONAL,
340
                'Choose a type of data display'
341
            ]
342
        ];
343
    }
344
}
345