1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
/** |
4
|
|
|
* This file is part of the Zemit Framework. |
5
|
|
|
* |
6
|
|
|
* (c) Zemit Team <[email protected]> |
7
|
|
|
* |
8
|
|
|
* For the full copyright and license information, please view the LICENSE.txt |
9
|
|
|
* file that was distributed with this source code. |
10
|
|
|
*/ |
11
|
|
|
|
12
|
|
|
namespace Zemit\Modules\Cli\Tasks; |
13
|
|
|
|
14
|
|
|
use Phalcon\Db\Column; |
15
|
|
|
use Phalcon\Db\ColumnInterface; |
16
|
|
|
use Phalcon\Mvc\Model\Relation; |
17
|
|
|
use Zemit\Modules\Cli\Task; |
18
|
|
|
use Zemit\Mvc\Model; |
19
|
|
|
use Zemit\Support\Helper; |
20
|
|
|
use Zemit\Support\Slug; |
21
|
|
|
|
22
|
|
|
class TsScaffoldTask extends Task |
23
|
|
|
{ |
24
|
|
|
|
25
|
|
|
public string $cliDoc = <<<DOC |
26
|
|
|
Usage: |
27
|
|
|
zemit cli ts-scaffold <action> [<params>...] [--force] [--table=<table>] [--directory=<directory>] |
28
|
|
|
|
29
|
|
|
Options: |
30
|
|
|
--force Overwrite existing files |
31
|
|
|
--path=<directory> Directory path to generate new files |
32
|
|
|
--table=<table> Comma seperated list of table to generate |
33
|
|
|
|
34
|
|
|
|
35
|
|
|
DOC; |
36
|
|
|
|
37
|
|
|
public string $path = '../sdk/src/'; |
38
|
|
|
public string $servicesPath = 'services/'; |
39
|
|
|
public string $modelsPath = 'models/'; |
40
|
|
|
public string $abstractsPath = 'abstracts/'; |
41
|
|
|
public string $interfacesPath = 'interfaces/'; |
42
|
|
|
|
43
|
|
|
public function getDefinitionsAction(string $name): array |
44
|
|
|
{ |
45
|
|
|
$definitions = []; |
46
|
|
|
|
47
|
|
|
$definitions['table'] = $this->getTableName($name); |
48
|
|
|
$definitions['slug'] = Slug::generate(Helper::uncamelize($name)); |
49
|
|
|
|
50
|
|
|
// backend model |
51
|
|
|
$definitions['backend'] = 'Zemit\\Models\\' . $definitions['table']; |
52
|
|
|
|
53
|
|
|
// model |
54
|
|
|
$definitions['model']['name'] = $definitions['table'] . 'Model'; |
55
|
|
|
$definitions['model']['file'] = $definitions['model']['name'] . '.ts'; |
56
|
|
|
$definitions['model']['export'] = trim($this->modelsPath, '/') . '.ts'; |
57
|
|
|
$definitions['model']['path'] = $this->modelsPath; |
58
|
|
|
|
59
|
|
|
// service |
60
|
|
|
$definitions['service']['name'] = $definitions['table'] . 'Service'; |
61
|
|
|
$definitions['service']['file'] = $definitions['service']['name'] . '.ts'; |
62
|
|
|
$definitions['service']['export'] = trim($this->servicesPath, '/') . '.ts'; |
63
|
|
|
$definitions['service']['path'] = $this->servicesPath; |
64
|
|
|
|
65
|
|
|
// service |
66
|
|
|
$definitions['interface']['name'] = $definitions['table'] . 'ModelInterface'; |
67
|
|
|
$definitions['interface']['file'] = $definitions['interface']['name'] . '.ts'; |
68
|
|
|
$definitions['interface']['export'] = trim($this->interfacesPath, '/') . '.ts'; |
69
|
|
|
$definitions['interface']['path'] = $this->interfacesPath; |
70
|
|
|
|
71
|
|
|
// abstract |
72
|
|
|
$definitions['abstract']['name'] = $definitions['table'] . 'ModelAbstract'; |
73
|
|
|
$definitions['abstract']['file'] = $definitions['abstract']['name'] . '.ts'; |
74
|
|
|
$definitions['abstract']['export'] = trim($this->abstractsPath, '/') . '.ts'; |
75
|
|
|
$definitions['abstract']['path'] = $this->abstractsPath; |
76
|
|
|
|
77
|
|
|
|
78
|
|
|
return $definitions; |
79
|
|
|
} |
80
|
|
|
|
81
|
|
|
public function generateExportsAction(): array |
82
|
|
|
{ |
83
|
|
|
$ret = []; |
84
|
|
|
|
85
|
|
|
$directory = $this->dispatcher->getParam('directory'); |
86
|
|
|
$files = glob($directory . '*.ts'); |
87
|
|
|
|
88
|
|
|
$exports = []; |
89
|
|
|
foreach ($files as $file) { |
90
|
|
|
$fileName = pathinfo($file, PATHINFO_FILENAME); |
91
|
|
|
if ($fileName !== 'index') { |
92
|
|
|
$exports [] = "export {{$fileName}} from './{$fileName}'"; |
93
|
|
|
} |
94
|
|
|
} |
95
|
|
|
|
96
|
|
|
$interfacesExportPath = $directory . 'index.ts'; |
97
|
|
|
|
98
|
|
|
$ret ['exports'] = $exports; |
99
|
|
|
$ret ['filePath'] = $interfacesExportPath; |
100
|
|
|
$ret ['saved'] = $this->saveFile($interfacesExportPath, implode("\n", $exports)); |
101
|
|
|
|
102
|
|
|
return $ret; |
103
|
|
|
} |
104
|
|
|
|
105
|
|
|
public function runAction(): array |
106
|
|
|
{ |
107
|
|
|
$ret = []; |
108
|
|
|
|
109
|
|
|
$exports = [ |
110
|
|
|
'models' => [], |
111
|
|
|
'services' => [], |
112
|
|
|
'interfaces' => [], |
113
|
|
|
'abstracts' => [], |
114
|
|
|
]; |
115
|
|
|
|
116
|
|
|
$force = $this->dispatcher->getParam('force') ?? false; |
117
|
|
|
$whitelisted = array_filter(explode(',', $this->dispatcher->getParam('table') ?? '')); |
118
|
|
|
$tables = $this->db->listTables(); |
119
|
|
|
foreach ($tables as $table) { |
120
|
|
|
if (!empty($whitelisted) && !in_array($table, $whitelisted)) { |
121
|
|
|
continue; |
122
|
|
|
} |
123
|
|
|
|
124
|
|
|
$columns = $this->db->describeColumns($table); |
125
|
|
|
$definitions = $this->getDefinitionsAction($table); |
126
|
|
|
$related = $this->getRelatedMeta($definitions['backend']); |
127
|
|
|
|
128
|
|
|
// Save Interface File |
129
|
|
|
$savePath = $this->path . $this->modelsPath . $this->abstractsPath . $this->interfacesPath . $definitions['interface']['file']; |
130
|
|
|
if (!file_exists($savePath) || $force) { |
131
|
|
|
$columns = $this->db->describeColumns($table); |
132
|
|
|
$this->saveFile($savePath, $this->createInterfaceOutput($definitions, $columns), $force); |
133
|
|
|
$ret [] = 'Interface `' . $definitions['interface']['file'] . '` created'; |
134
|
|
|
} |
135
|
|
|
|
136
|
|
|
// Abstract |
137
|
|
|
$savePath = $this->path . $this->modelsPath . $this->abstractsPath . $definitions['abstract']['file']; |
138
|
|
|
if (!file_exists($savePath) || $force) { |
139
|
|
|
$this->saveFile($savePath, $this->createAbstractOutput($definitions, $columns), $force); |
140
|
|
|
$ret [] = 'Abstract `' . $definitions['abstract']['file'] . '` created'; |
141
|
|
|
} |
142
|
|
|
|
143
|
|
|
// Model |
144
|
|
|
$savePath = $this->path . $this->modelsPath . $definitions['model']['file']; |
145
|
|
|
if (!file_exists($savePath) || $force) { |
146
|
|
|
$this->saveFile($savePath, $this->createModelOutput($definitions, $related), $force); |
147
|
|
|
$ret [] = 'Model ' . $definitions['model']['file'] . ' created'; |
148
|
|
|
} |
149
|
|
|
|
150
|
|
|
// Create Service |
151
|
|
|
$savePath = $this->path . $this->servicesPath . $definitions['service']['file']; |
152
|
|
|
if (!file_exists($savePath) || $force) { |
153
|
|
|
$this->saveFile($savePath, $this->createServiceOutput($definitions), $force); |
154
|
|
|
$ret [] = 'Service `' . $definitions['service']['file'] . '` created'; |
155
|
|
|
} |
156
|
|
|
|
157
|
|
|
$this->appendExport($definitions, $exports); |
158
|
|
|
} |
159
|
|
|
|
160
|
|
|
// interfaces |
161
|
|
|
$exportDirectory = $this->path . $this->modelsPath . $this->abstractsPath . $this->interfacesPath; |
162
|
|
|
$this->dispatcher->setParam('directory', $exportDirectory); |
163
|
|
|
$this->generateExportsAction(); |
164
|
|
|
$ret [] = 'Interfaces Export `index.ts` created'; |
165
|
|
|
|
166
|
|
|
// abstracts |
167
|
|
|
$exportDirectory = $this->path . $this->modelsPath . $this->abstractsPath; |
168
|
|
|
$this->dispatcher->setParam('directory', $exportDirectory); |
169
|
|
|
$this->generateExportsAction(); |
170
|
|
|
$ret [] = 'Abstracts Export `index.ts` created'; |
171
|
|
|
|
172
|
|
|
// models |
173
|
|
|
$exportDirectory = $this->path . $this->modelsPath; |
174
|
|
|
$this->dispatcher->setParam('directory', $exportDirectory); |
175
|
|
|
$this->generateExportsAction(); |
176
|
|
|
$ret [] = 'Models Export `index.ts` created'; |
177
|
|
|
|
178
|
|
|
// services |
179
|
|
|
$exportDirectory = $this->path . $this->servicesPath; |
180
|
|
|
$this->dispatcher->setParam('directory', $exportDirectory); |
181
|
|
|
$this->generateExportsAction(); |
182
|
|
|
$ret [] = 'Services Export `index.ts` created'; |
183
|
|
|
|
184
|
|
|
return $ret; |
185
|
|
|
} |
186
|
|
|
|
187
|
|
|
public function appendExport(array $definitions, array &$exports) |
188
|
|
|
{ |
189
|
|
|
$exports['models'] [] = "export {{$definitions['model']['name']}} from './{$definitions['model']['name']}'"; |
190
|
|
|
$exports['interfaces'] [] = "export {{$definitions['interface']['name']}} from './{$definitions['interface']['name']}'"; |
191
|
|
|
$exports['services'] [] = "export {{$definitions['service']['name']}} from './{$definitions['service']['name']}'"; |
192
|
|
|
$exports['abstracts'] [] = "export {{$definitions['abstract']['name']}} from './{$definitions['abstract']['name']}'"; |
193
|
|
|
} |
194
|
|
|
|
195
|
|
|
public function createInterfaceOutput(array $definitions, array $columns): string |
196
|
|
|
{ |
197
|
|
|
$propertyItems = str_replace('!: ', ': ', $this->getPropertyItems($columns)); |
198
|
|
|
return <<<EOT |
199
|
|
|
export interface {$definitions['interface']['name']} { |
200
|
|
|
{$propertyItems} |
201
|
|
|
} |
202
|
|
|
EOT; |
203
|
|
|
} |
204
|
|
|
|
205
|
|
|
/** |
206
|
|
|
* Creates a typescript abstract model output based on the given definitions. |
207
|
|
|
*/ |
208
|
|
|
public function createAbstractOutput(array $definitions, array $columns): string |
209
|
|
|
{ |
210
|
|
|
$from = './' . $this->interfacesPath . $definitions['interface']['name']; |
211
|
|
|
$propertyItems = $this->getPropertyItems($columns); |
212
|
|
|
return <<<EOT |
213
|
|
|
import { AbstractModel } from '../AbstractModel'; |
214
|
|
|
import { {$definitions['interface']['name']} } from '$from'; |
215
|
|
|
|
216
|
|
|
export class {$definitions['abstract']['name']} extends AbstractModel implements {$definitions['interface']['name']} { |
217
|
|
|
{$propertyItems} |
218
|
|
|
} |
219
|
|
|
EOT; |
220
|
|
|
} |
221
|
|
|
|
222
|
|
|
/** |
223
|
|
|
* Creates a typescript model output based on the given definitions. |
224
|
|
|
*/ |
225
|
|
|
public function createModelOutput(array $definitions, array $related): string |
226
|
|
|
{ |
227
|
|
|
$from = './' . $this->abstractsPath . $definitions['abstract']['name']; |
228
|
|
|
$relatedImportItems = $this->getRelatedImportItems($related); |
229
|
|
|
// $relatedDefaultItems = $this->getRelatedDefaultItems($related); |
230
|
|
|
$relatedPropertyItems = $this->getRelatedProperties($related); |
231
|
|
|
$importTypeClassTransformer = !empty($relatedPropertyItems) ? |
232
|
|
|
"import { Type } from 'class-transformer';" : ''; |
233
|
|
|
return <<<EOT |
234
|
|
|
import 'reflect-metadata'; |
235
|
|
|
{$importTypeClassTransformer} |
236
|
|
|
import { {$definitions['abstract']['name']} } from '$from'; |
237
|
|
|
{$relatedImportItems} |
238
|
|
|
|
239
|
|
|
export class {$definitions['model']['name']} extends {$definitions['abstract']['name']} { |
240
|
|
|
{$relatedPropertyItems} |
241
|
|
|
} |
242
|
|
|
EOT; |
243
|
|
|
} |
244
|
|
|
|
245
|
|
|
/** |
246
|
|
|
* |
247
|
|
|
*/ |
248
|
|
|
public function createServiceOutput(array $definitions) |
249
|
|
|
{ |
250
|
|
|
|
251
|
|
|
$from = '../' . $this->modelsPath . $definitions['model']['name']; |
252
|
|
|
return <<<EOT |
253
|
|
|
import { AbstractService } from './AbstractService'; |
254
|
|
|
import { {$definitions['model']['name']} } from '$from'; |
255
|
|
|
|
256
|
|
|
export class {$definitions['service']['name']} extends AbstractService { |
257
|
|
|
modelUrl = '{$definitions['slug']}'; |
258
|
|
|
model = {$definitions['model']['name']}; |
259
|
|
|
} |
260
|
|
|
EOT; |
261
|
|
|
} |
262
|
|
|
|
263
|
|
|
public function getRelatedImportItems(array $related): string |
264
|
|
|
{ |
265
|
|
|
$relatedImportItems = []; |
266
|
|
|
if (!empty($related['import'])) { |
267
|
|
|
foreach ($related['import'] as $key => $value) { |
268
|
|
|
$relatedImportItems [] = 'import { ' . $key . ' } from \'./' . $key . '\';'; |
269
|
|
|
} |
270
|
|
|
} |
271
|
|
|
return implode("\n", $relatedImportItems); |
272
|
|
|
} |
273
|
|
|
|
274
|
|
|
/** |
275
|
|
|
* Returns a formatted string representation of the related default items. |
276
|
|
|
*/ |
277
|
|
|
public function getRelatedDefaultItems(array $related): string |
278
|
|
|
{ |
279
|
|
|
$relatedMapItems = []; |
280
|
|
|
if (!empty($related['default'])) { |
281
|
|
|
foreach ($related['default'] as $key => $value) { |
282
|
|
|
$relatedMapItems [] = ' ' . $key . ' = ' . $value . ','; |
283
|
|
|
} |
284
|
|
|
} |
285
|
|
|
return implode("\n", $relatedMapItems); |
286
|
|
|
} |
287
|
|
|
|
288
|
|
|
/** |
289
|
|
|
* Returns a formatted string representation of the related map items. |
290
|
|
|
*/ |
291
|
|
|
public function getRelatedMapItems(array $related): string |
292
|
|
|
{ |
293
|
|
|
$relatedMapItems = []; |
294
|
|
|
if (!empty($related['map'])) { |
295
|
|
|
foreach ($related['map'] as $key => $value) { |
296
|
|
|
$relatedMapItems [] = ' ' . $key . '!: ' . $value . ';'; |
297
|
|
|
} |
298
|
|
|
} |
299
|
|
|
return implode("\n", $relatedMapItems); |
300
|
|
|
} |
301
|
|
|
|
302
|
|
|
/** |
303
|
|
|
* Returns a formatted string representation of the related map items. |
304
|
|
|
*/ |
305
|
|
|
public function getRelatedProperties(array $related): string |
306
|
|
|
{ |
307
|
|
|
$relatedMapItems = []; |
308
|
|
|
if (!empty($related['data'])) { |
309
|
|
|
foreach ($related['data'] as $key => $value) { |
310
|
|
|
$type = str_replace('[]', '', $value); |
311
|
|
|
assert(is_string($type)); |
312
|
|
|
$relatedMapItems [] = ''; |
313
|
|
|
$relatedMapItems [] = ' @Type(() => ' . $type . ')'; |
314
|
|
|
$relatedMapItems [] = ' ' . $key . '!: ' . $value . ';'; |
315
|
|
|
} |
316
|
|
|
} |
317
|
|
|
return implode("\n", $relatedMapItems); |
318
|
|
|
} |
319
|
|
|
|
320
|
|
|
/** |
321
|
|
|
* Returns a formatted string representation of the property items. |
322
|
|
|
* |
323
|
|
|
* @param ColumnInterface[] $columns An array of column objects. |
324
|
|
|
* @return string A string representation of the property items. |
325
|
|
|
*/ |
326
|
|
|
public function getPropertyItems(array $columns): string |
327
|
|
|
{ |
328
|
|
|
$propertyItems = []; |
329
|
|
|
foreach ($columns as $column) { |
330
|
|
|
$columnName = $this->getColumnName($column->getName()); |
331
|
|
|
$columnType = $this->getColumnTsType($column); |
332
|
|
|
// $defaultValue = $this->getDefaultValue($column, $columnType); |
333
|
|
|
$propertyItems[] = " $columnName!: $columnType;"; |
334
|
|
|
} |
335
|
|
|
return implode("\n", $propertyItems); |
336
|
|
|
} |
337
|
|
|
|
338
|
|
|
public function getRelatedMeta(string $modelClassName): array |
339
|
|
|
{ |
340
|
|
|
$related = [ |
341
|
|
|
'import' => [], |
342
|
|
|
'map' => [], |
343
|
|
|
'default' => [], |
344
|
|
|
'data' => [], |
345
|
|
|
]; |
346
|
|
|
|
347
|
|
|
$modelInstance = $this->getModelInstance($modelClassName); |
348
|
|
|
$modelManager = $modelInstance->getModelsManager(); |
349
|
|
|
$relations = $modelManager->getRelations($modelClassName); |
350
|
|
|
foreach ($relations as $relation) { |
351
|
|
|
$relationAlias = $relation->getOption('alias'); |
352
|
|
|
$relationModelName = $this->getModelNameFromClassName($relation->getReferencedModel()); |
353
|
|
|
// do not import itself |
354
|
|
|
$modelName = basename(str_replace('\\', '/', $modelClassName)) . 'Model'; |
355
|
|
|
if ($relationModelName !== $modelName) { |
356
|
|
|
// import related model |
357
|
|
|
$related['import'][$relationModelName] = ''; |
358
|
|
|
} |
359
|
|
|
// add related map entry |
360
|
|
|
$related['map'][$relationAlias] = $relationModelName; |
361
|
|
|
|
362
|
|
|
if ( |
363
|
|
|
$relation->getType() === Relation::HAS_MANY || |
364
|
|
|
$relation->getType() === Relation::HAS_MANY_THROUGH |
365
|
|
|
) { |
366
|
|
|
// set default value to empty array |
367
|
|
|
$related['default'][$relationAlias] = '[]'; |
368
|
|
|
$related['data'][$relationAlias] = $relationModelName . '[]'; |
369
|
|
|
} |
370
|
|
|
else { |
371
|
|
|
$related['data'][$relationAlias] = $relationModelName; |
372
|
|
|
} |
373
|
|
|
} |
374
|
|
|
|
375
|
|
|
return $related; |
376
|
|
|
} |
377
|
|
|
|
378
|
|
|
public function getColumnTsType(ColumnInterface $column): string |
379
|
|
|
{ |
380
|
|
|
$tsType = 'null'; |
381
|
|
|
|
382
|
|
|
if ($column->isNumeric()) { |
383
|
|
|
$tsType = 'number'; |
384
|
|
|
} |
385
|
|
|
|
386
|
|
|
switch ($column->getType()) { |
387
|
|
|
case Column::TYPE_TIMESTAMP: |
388
|
|
|
case Column::TYPE_BIGINTEGER: |
389
|
|
|
case Column::TYPE_MEDIUMINTEGER: |
390
|
|
|
case Column::TYPE_SMALLINTEGER: |
391
|
|
|
// case Column::TYPE_TINYINTEGER: // conflict with TYPE_BINARY |
392
|
|
|
case Column::TYPE_INTEGER: |
393
|
|
|
case Column::TYPE_DECIMAL: |
394
|
|
|
case Column::TYPE_DOUBLE: |
395
|
|
|
case Column::TYPE_FLOAT: |
396
|
|
|
case Column::TYPE_BIT: |
397
|
|
|
$tsType = 'number'; |
398
|
|
|
break; |
399
|
|
|
|
400
|
|
|
case Column::TYPE_ENUM: |
401
|
|
|
case Column::TYPE_VARCHAR: |
402
|
|
|
case Column::TYPE_CHAR: |
403
|
|
|
case Column::TYPE_TEXT: |
404
|
|
|
case Column::TYPE_TINYTEXT: |
405
|
|
|
case Column::TYPE_MEDIUMTEXT: |
406
|
|
|
case Column::TYPE_BLOB: |
407
|
|
|
case Column::TYPE_TINYBLOB: |
408
|
|
|
case Column::TYPE_LONGBLOB: |
409
|
|
|
case Column::TYPE_DATETIME: |
410
|
|
|
case Column::TYPE_DATE: |
411
|
|
|
case Column::TYPE_TIME: |
412
|
|
|
case Column::TYPE_BINARY: |
413
|
|
|
$tsType = 'string'; |
414
|
|
|
break; |
415
|
|
|
|
416
|
|
|
case Column::TYPE_JSON: |
417
|
|
|
case Column::TYPE_JSONB: |
418
|
|
|
$tsType = 'object'; |
419
|
|
|
break; |
420
|
|
|
|
421
|
|
|
case Column::TYPE_BOOLEAN: |
422
|
|
|
$tsType = 'boolean'; |
423
|
|
|
break; |
424
|
|
|
|
425
|
|
|
default: |
426
|
|
|
break; |
427
|
|
|
} |
428
|
|
|
|
429
|
|
|
return $tsType; |
430
|
|
|
} |
431
|
|
|
|
432
|
|
|
public function getDefaultValue(ColumnInterface $column, string $type): ?string |
433
|
|
|
{ |
434
|
|
|
$default = null; |
435
|
|
|
$columnDefault = $column->getDefault(); |
436
|
|
|
switch (getType(strtolower($columnDefault ?? ''))) { |
437
|
|
|
case 'boolean': |
438
|
|
|
case 'integer': |
439
|
|
|
case 'double': |
440
|
|
|
case 'null': |
441
|
|
|
$default = $columnDefault; |
442
|
|
|
break; |
443
|
|
|
|
444
|
|
|
case 'string': |
445
|
|
|
if ($type === 'string') { |
446
|
|
|
$default = !empty($columnDefault) ? '"' . addslashes($columnDefault) . '"' : ''; |
447
|
|
|
} |
448
|
|
|
if ($type === 'number') { |
449
|
|
|
$default = !empty($columnDefault) ? $columnDefault : ''; |
450
|
|
|
} |
451
|
|
|
if ($type === 'array') { |
452
|
|
|
$default = '[]'; |
453
|
|
|
} |
454
|
|
|
if ($type === 'object') { |
455
|
|
|
$default = '{}'; |
456
|
|
|
} |
457
|
|
|
break; |
458
|
|
|
|
459
|
|
|
case 'array': |
460
|
|
|
$default = '[]'; |
461
|
|
|
break; |
462
|
|
|
|
463
|
|
|
case 'object': |
464
|
|
|
$default = '{}'; |
465
|
|
|
break; |
466
|
|
|
|
467
|
|
|
default: |
468
|
|
|
break; |
469
|
|
|
} |
470
|
|
|
|
471
|
|
|
return $default; |
472
|
|
|
} |
473
|
|
|
|
474
|
|
|
public function getColumnName(string $name) |
475
|
|
|
{ |
476
|
|
|
return lcfirst( |
477
|
|
|
Helper::camelize( |
478
|
|
|
Helper::uncamelize( |
479
|
|
|
$name |
480
|
|
|
) |
481
|
|
|
) |
482
|
|
|
); |
483
|
|
|
} |
484
|
|
|
|
485
|
|
|
public function getTableName(string $name) |
486
|
|
|
{ |
487
|
|
|
return ucfirst( |
488
|
|
|
Helper::camelize( |
489
|
|
|
Helper::uncamelize( |
490
|
|
|
$name |
491
|
|
|
) |
492
|
|
|
) |
493
|
|
|
); |
494
|
|
|
} |
495
|
|
|
|
496
|
|
|
public function getModelInstance(string $modelClassName): Model |
497
|
|
|
{ |
498
|
|
|
if (class_exists($modelClassName)) { |
499
|
|
|
$modelInstance = new $modelClassName(); |
500
|
|
|
assert($modelInstance instanceof Model); |
501
|
|
|
return $modelInstance; |
502
|
|
|
} |
503
|
|
|
return new Model(); |
504
|
|
|
} |
505
|
|
|
|
506
|
|
|
public function getModelNameFromClassName(string $className) |
507
|
|
|
{ |
508
|
|
|
return ucfirst( |
509
|
|
|
Helper::camelize( |
510
|
|
|
Helper::uncamelize( |
511
|
|
|
basename( |
512
|
|
|
str_replace( |
513
|
|
|
'\\', |
514
|
|
|
'/', |
515
|
|
|
$className |
516
|
|
|
) |
517
|
|
|
) |
518
|
|
|
) |
519
|
|
|
) |
520
|
|
|
) . 'Model'; |
521
|
|
|
} |
522
|
|
|
|
523
|
|
|
public function saveFile(string $file, string $text, bool $force = false): bool |
524
|
|
|
{ |
525
|
|
|
if (!$force && file_exists($file)) { |
526
|
|
|
return false; |
527
|
|
|
} |
528
|
|
|
|
529
|
|
|
$directory = dirname($file); |
530
|
|
|
|
531
|
|
|
// Create the directory if it doesn't exist |
532
|
|
|
if (!is_dir($directory)) { |
533
|
|
|
mkdir($directory, 0755, true); |
534
|
|
|
} |
535
|
|
|
|
536
|
|
|
$file = fopen($file, 'w'); |
537
|
|
|
return fwrite($file, $text) && fclose($file); |
538
|
|
|
} |
539
|
|
|
} |
540
|
|
|
|