Completed
Push — master ( 237512...590bd8 )
by wen
11:19
created

ComponentMakeCommand::buildViewColumn()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 9
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 9
rs 9.6666
c 0
b 0
f 0
cc 1
eloc 6
nc 1
nop 1
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 Sco\Admin\Facades\AdminElement;
11
use Symfony\Component\Console\Input\InputOption;
12
13
class ComponentMakeCommand extends GeneratorCommand
14
{
15
    /**
16
     * The console command name.
17
     *
18
     * @var string
19
     */
20
    protected $name = 'make:component';
21
22
    /**
23
     * The console command description.
24
     *
25
     * @var string
26
     */
27
    protected $description = 'Create a new component class(ScoAdmin)';
28
29
    /**
30
     * The type of class being generated.
31
     *
32
     * @var string
33
     */
34
    protected $type = 'Component';
35
36
    protected $columnTypeMappings = [
37
        'smallint' => 'text',
38
        'integer'  => 'text',
39
        'bigint'   => 'text',
40
        'float'    => 'text',
41
        'string'   => 'text',
42
        'text'     => 'text',
43
        'boolean'  => 'mapping',
44
        'datetime' => 'datetime',
45
        'date'     => 'datetime',
46
    ];
47
48
    protected $elementTypeMappings = [
49
        'smallint' => 'number',
50
        'integer'  => 'number',
51
        'bigint'   => 'number',
52
        'float'    => 'number',
53
        'string'   => 'text',
54
        'text'     => 'textarea',
55
        'boolean'  => 'elswitch',
56
        'datetime' => 'datetime',
57
        'date'     => 'date',
58
        'time'     => 'time',
59
    ];
60
61
    /**
62
     * Execute the console command.
63
     *
64
     * @return void
65
     */
66
    public function handle()
67
    {
68
        if (parent::handle() === false && !$this->option('force')) {
69
            return;
70
        }
71
72
        if ($this->option('observer')) {
73
            $this->createObserver();
74
        }
75
    }
76
77
    /**
78
     * Create a new permission observer for the component
79
     */
80
    protected function createObserver()
81
    {
82
        $observerClass = $this->parseClass($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...keCommand::parseClass() 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...
83
84
        //$this->info('observer:' . $observerClass);
0 ignored issues
show
Unused Code Comprehensibility introduced by
64% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
85
86
        $this->call('make:observer', [
87
            'name' => $observerClass,
88
        ]);
89
    }
90
91
    /**
92
     * Get the fully-qualified class name.
93
     *
94
     * @param string $class
95
     *
96
     * @return string
97
     */
98
    protected function parseClass($class)
99
    {
100
        if (preg_match('([^A-Za-z0-9_/\\\\])', $class)) {
101
            throw new InvalidArgumentException('Model name contains invalid characters.');
102
        }
103
104
        $class = trim(str_replace('/', '\\', $class), '\\');
105
106
        if (!Str::startsWith($class, $rootNamespace = $this->laravel->getNamespace())) {
107
            $class = $rootNamespace . $class;
108
        }
109
110
        return $class;
111
    }
112
113
    /**
114
     * Get the stub file for the generator.
115
     *
116
     * @return string
117
     */
118
    protected function getStub()
119
    {
120
        return __DIR__ . '/stubs/component.stub';
121
    }
122
123
    /**
124
     * Build the class with the given name.
125
     *
126
     * @param string $name
127
     *
128
     * @return string
129
     */
130
    protected function buildClass($name)
131
    {
132
        $observer = $this->option('observer')
133
            ? $this->parseClass($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...keCommand::parseClass() 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...
134
            : \Sco\Admin\Component\Observer::class;
135
136
        $model = $this->option('model')
137
            ? $this->parseClass($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::parseClass() 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...
138
            : '';
139
140
        $columns  = $this->getViewColumns($model);
141
        $elements = $this->getFormElements($model);
142
143
144
        return str_replace(
145
            ['DummyObserver', 'DummyModel', 'DummyColumns', 'DummyElements'],
146
            [$observer, $model, $columns, $elements],
147
            parent::buildClass($name)
148
        );
149
    }
150
151
    protected function getViewColumns($model)
152
    {
153
        $columns = $this->getTableColumns($model);
154
        if (!$columns) {
155
            return;
156
        }
157
158
        $list = [];
159
        foreach ($columns as $column) {
160
            $list[] = $this->buildViewColumn($column);
161
        }
162
        return implode("\n", $list);
163
    }
164
165
    protected function buildViewColumn(Column $column)
166
    {
167
        return sprintf(
168
            "            AdminColumn::%s('%s', '%s'),",
169
            $this->getViewColumnType($column->getType()->getName()),
170
            $column->getName(),
171
            $this->getColumnTitle($column)
172
        );
173
    }
174
175
    protected function getColumnTitle(Column $column)
176
    {
177
        return $column->getComment() ?? studly_case($column->getName());
178
    }
179
180
    protected function getViewColumnType($name)
181
    {
182
        return $this->columnTypeMappings[$name] ?? 'text';
183
    }
184
185
    protected function getFormElements($model)
186
    {
187
        $columns = $this->getTableColumns($model);
188
189
        $list = [];
190
        foreach ($columns as $column) {
191
            if (!$column->getAutoincrement()) {
192
                $list[] = $this->buildFormElement($column);
193
            }
194
        }
195
        return implode("\n", $list);
196
    }
197
198
    protected function buildFormElement(Column $column)
199
    {
200
        return sprintf(
201
            "            AdminElement::%s('%s', '%s')->required(),",
202
            $this->getFormElementType($column->getType()->getName()),
203
            $column->getName(),
204
            $this->getColumnTitle($column)
205
        );
206
    }
207
208
    protected function getFormElementType($name)
209
    {
210
        return $this->elementTypeMappings[$name] ?? 'text';
211
    }
212
213
    protected function getTableColumns($class)
214
    {
215
        if (empty($class)) {
216
            return;
217
        }
218
219
        if (!class_exists($class)) {
220
            return;
221
        }
222
223
        $model = new $class();
224
        if (!($model instanceof Model)) {
225
            return;
226
        }
227
        $schema = $model->getConnection()->getDoctrineSchemaManager();
228
229
        $table = $model->getConnection()->getTablePrefix() . $model->getTable();
230
231
        return $schema->listTableColumns($table);
232
    }
233
234
    /**
235
     * Get the default namespace for the class.
236
     *
237
     * @param  string $rootNamespace
238
     *
239
     * @return string
240
     */
241
    protected function getDefaultNamespace($rootNamespace)
242
    {
243
        return $rootNamespace . '\Components';
244
    }
245
246
    /**
247
     * Get the console command options.
248
     *
249
     * @return array
250
     */
251
    protected function getOptions()
252
    {
253
        return [
254
            [
255
                'observer', 'o', InputOption::VALUE_OPTIONAL,
256
                'Generate a new access observer for the component.',
257
            ],
258
            [
259
                'force', null, InputOption::VALUE_NONE,
260
                'Generate the class even if the component already exists.',
261
            ],
262
            [
263
                'model', 'm', InputOption::VALUE_OPTIONAL,
264
                'Generate a model for the component.',
265
            ],
266
        ];
267
    }
268
}
269