Completed
Push — master ( 22fdaf...0ad42b )
by wen
10:44 queued 10s
created

ComponentMakeCommand   B

Complexity

Total Complexity 38

Size/Duplication

Total Lines 316
Duplicated Lines 19.62 %

Coupling/Cohesion

Components 1
Dependencies 4

Importance

Changes 2
Bugs 1 Features 0
Metric Value
wmc 38
c 2
b 1
f 0
lcom 1
cbo 4
dl 62
loc 316
rs 8.3999

17 Methods

Rating   Name   Duplication   Size   Complexity  
A getStub() 0 8 2
A buildClass() 0 14 2
B buildObserverReplacements() 0 24 4
A parseObserver() 19 19 3
B buildModelReplacements() 0 23 5
A parseModel() 14 14 3
A getViewColumns() 13 13 3
A buildViewColumn() 0 9 1
A getColumnTitle() 0 4 1
A getViewColumnType() 0 4 1
A getFormElements() 16 16 4
A buildFormElement() 0 9 1
A getFormElementType() 0 4 1
A getTableColumns() 0 20 4
A getDefaultNamespace() 0 4 1
A getComponentNamespace() 0 11 1
A getOptions() 0 17 1

How to fix   Duplicated Code   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

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 View Code Duplication
    protected function parseObserver($observer)
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...
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;
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
            'DummyModelClass'     => class_basename($modelClass),
167
            'DummyColumns'        => $columns ? implode("\n", $columns) : '',
168
            'DummyElements'       => $elements ? implode("\n", $elements) : '',
169
        ]);
170
    }
171
172
    /**
173
     * Get the fully-qualified model class name.
174
     *
175
     * @param  string $model
176
     *
177
     * @return string
178
     */
179 View Code Duplication
    protected function parseModel($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...
180
    {
181
        if (preg_match('([^A-Za-z0-9_/\\\\])', $model)) {
182
            throw new InvalidArgumentException('Model name contains invalid characters.');
183
        }
184
185
        $model = trim(str_replace('/', '\\', $model), '\\');
186
187
        if (!Str::startsWith($model, $rootNamespace = $this->laravel->getNamespace())) {
188
            $model = $rootNamespace . $model;
189
        }
190
191
        return $model;
192
    }
193
194 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...
195
    {
196
        $columns = $this->getTableColumns($model);
197
        if (!$columns) {
198
            return;
199
        }
200
201
        $list = [];
202
        foreach ($columns as $column) {
203
            $list[] = $this->buildViewColumn($column);
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
        return $list;
243
    }
244
245
    protected function buildFormElement(Column $column)
246
    {
247
        return sprintf(
248
            "            AdminElement::%s('%s', '%s')->required(),",
249
            $this->getFormElementType($column->getType()->getName()),
250
            $column->getName(),
251
            $this->getColumnTitle($column)
252
        );
253
    }
254
255
    protected function getFormElementType($name)
256
    {
257
        return $this->elementTypeMappings[$name] ?? 'text';
258
    }
259
260
    protected function getTableColumns($class)
261
    {
262
        if (empty($class)) {
263
            return;
264
        }
265
266
        if (!class_exists($class)) {
267
            return;
268
        }
269
270
        $model = new $class();
271
        if (!($model instanceof Model)) {
272
            return;
273
        }
274
        $schema = $model->getConnection()->getDoctrineSchemaManager();
275
276
        $table = $model->getConnection()->getTablePrefix() . $model->getTable();
277
278
        return $schema->listTableColumns($table);
279
    }
280
281
    /**
282
     * Get the default namespace for the class.
283
     *
284
     * @param  string $rootNamespace
285
     *
286
     * @return string
287
     */
288
    protected function getDefaultNamespace($rootNamespace)
289
    {
290
        return $rootNamespace . '\\' . $this->getComponentNamespace();
291
    }
292
293
    protected function getComponentNamespace()
294
    {
295
        return str_replace(
296
            '/',
297
            '\\',
298
            Str::after(
299
                config('admin.components'),
300
                app_path() . DIRECTORY_SEPARATOR
301
            )
302
        );
303
    }
304
305
    /**
306
     * Get the console command options.
307
     *
308
     * @return array
309
     */
310
    protected function getOptions()
311
    {
312
        return [
313
            [
314
                'observer', 'o', InputOption::VALUE_OPTIONAL,
315
                'Generate a new access observer for the component.',
316
            ],
317
            [
318
                'force', null, InputOption::VALUE_NONE,
319
                'Generate the class even if the component already exists.',
320
            ],
321
            [
322
                'model', 'm', InputOption::VALUE_OPTIONAL,
323
                'Generate a model for the component.',
324
            ],
325
        ];
326
    }
327
}
328