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')); |
|
|
|
|
83
|
|
|
|
84
|
|
|
//$this->info('observer:' . $observerClass); |
|
|
|
|
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')) |
|
|
|
|
134
|
|
|
: \Sco\Admin\Component\Observer::class; |
135
|
|
|
|
136
|
|
|
$model = $this->option('model') |
137
|
|
|
? $this->parseClass($this->option('model')) |
|
|
|
|
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
|
|
|
|
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.