Skip to content

Commit a024ffb

Browse files
committed
Initial commit 🎉
0 parents  commit a024ffb

File tree

2 files changed

+160
-0
lines changed

2 files changed

+160
-0
lines changed
Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
<?php
2+
3+
namespace App\Console\Commands;
4+
5+
use Illuminate\Database\Eloquent\Model;
6+
use Illuminate\Support\Facades\DB;
7+
use Illuminate\Console\Command;
8+
use Illuminate\Support\Str;
9+
10+
use stdClass;
11+
12+
class GenerateTypeScriptInterfaceFromModel extends Command {
13+
/**
14+
* The name and signature of the console command.
15+
*
16+
* @var string
17+
*/
18+
protected $signature = 'typescript:generate-interface-from-model {model}';
19+
20+
/**
21+
* The console command description.
22+
*
23+
* @var string
24+
*/
25+
protected $description = 'Generate a TypeScript interface from a model';
26+
27+
/**
28+
* Check if the current database connection type is supported.
29+
*
30+
* @return bool
31+
*/
32+
private function hasSupportedDatabaseConnection(): bool {
33+
return collect(['mysql'])->contains(DB::getDefaultConnection());
34+
}
35+
36+
/**
37+
* Check if the model passed to this command is a valid model.
38+
*
39+
* @return bool
40+
*/
41+
private function hasValidModel(): bool {
42+
$className = $this->getFullyQualifiedModelName();
43+
44+
if (!class_exists($className)) return false;
45+
if (!is_subclass_of($className, Model::class)) return false;
46+
47+
return true;
48+
}
49+
50+
/**
51+
* Get the fully qualified model name passed as an argument to this command.
52+
*
53+
* @return string
54+
*/
55+
private function getFullyQualifiedModelName(): string {
56+
return $this->argument('model');
57+
}
58+
59+
/**
60+
* Get the table name for the supplied model.
61+
*
62+
* @return string
63+
*/
64+
private function getTableName(): string {
65+
return (new ($this->getFullyQualifiedModelName()))->getTable();
66+
}
67+
68+
/**
69+
* Get the mapped TypeScript type of a type.
70+
*
71+
* @param stdClass $columnSchema
72+
*
73+
* @return string
74+
*/
75+
private function getTypeScriptType(stdClass $columnSchema): string {
76+
$columnType = Str::of($columnSchema->Type);
77+
78+
// @todo sets
79+
// @todo enums
80+
$mappedType = match (true) {
81+
$columnType->startsWith('bit') => 'number',
82+
$columnType->startsWith('int') => 'number',
83+
$columnType->startsWith('dec') => 'number',
84+
$columnType->startsWith('char') => 'string',
85+
$columnType->startsWith('text') => 'string',
86+
$columnType->startsWith('blob') => 'string',
87+
$columnType->startsWith('date') => 'string',
88+
$columnType->startsWith('time') => 'string',
89+
$columnType->startsWith('year') => 'string',
90+
$columnType->startsWith('bool') => 'boolean',
91+
$columnType->startsWith('float') => 'number',
92+
$columnType->startsWith('bigint') => 'number',
93+
$columnType->startsWith('double') => 'number',
94+
$columnType->startsWith('binary') => 'string',
95+
$columnType->startsWith('bigint') => 'number',
96+
$columnType->startsWith('decimal') => 'number',
97+
$columnType->startsWith('integer') => 'number',
98+
$columnType->startsWith('varchar') => 'string',
99+
$columnType->startsWith('boolean') => 'boolean',
100+
$columnType->startsWith('tinyblob') => 'string',
101+
$columnType->startsWith('tinytext') => 'string',
102+
$columnType->startsWith('longtext') => 'string',
103+
$columnType->startsWith('longblob') => 'string',
104+
$columnType->startsWith('datetime') => 'string',
105+
$columnType->startsWith('smallint') => 'boolean',
106+
$columnType->startsWith('varbinary') => 'string',
107+
$columnType->startsWith('mediumint') => 'number',
108+
$columnType->startsWith('timestamp') => 'string',
109+
$columnType->startsWith('mediumtext') => 'string',
110+
$columnType->startsWith('mediumblob') => 'string',
111+
112+
default => 'unknown',
113+
};
114+
115+
if ($mappedType === 'unknown') return $mappedType;
116+
117+
if ($columnSchema->Null === 'YES') $mappedType .= '|null';
118+
119+
return $mappedType;
120+
}
121+
122+
/**
123+
* Print the generated interface.
124+
*
125+
* @return void
126+
*/
127+
private function printGeneratedInterface(): void {
128+
$tableColumns = collect(DB::select(DB::raw('SHOW COLUMNS FROM ' . $this->getTableName())));
129+
130+
$this->info('interface ' . (Str::of($this->getFullyQualifiedModelName())->afterLast('\\')) . ' {');
131+
132+
$tableColumns->each(fn ($column) => $this->info(' ' . $column->Field . ': ' . $this->getTypeScriptType($column) . ';'));
133+
134+
$this->info('}');
135+
}
136+
137+
/**
138+
* Execute the console command.
139+
*
140+
* @return int
141+
*/
142+
public function handle(): int {
143+
if (!$this->hasValidModel()) {
144+
$this->error('That\'s not a valid model!');
145+
146+
return Command::FAILURE;
147+
}
148+
149+
if (!$this->hasSupportedDatabaseConnection()) {
150+
$this->error('We currently only support a MySQL connection!');
151+
152+
return Command::FAILURE;
153+
}
154+
155+
$this->printGeneratedInterface();
156+
157+
return Command::SUCCESS;
158+
}
159+
}

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
# typescriptify-laravel-models

0 commit comments

Comments
 (0)