1818class GenerateFromDbml extends Command
1919{
2020 protected $ signature = 'generate:dbml {file} {--force : Overwrite existing files} ' ;
21+
2122 protected $ description = 'Generate models and migrations from a DBML file ' ;
23+
2224 /**
2325 * @var array<string, EnumDefinition>
2426 */
2527 private array $ enums = [];
28+
2629 private Schema $ schema ;
2730
2831 private const FORBIDDEN_MODEL_NAMES = [
2932 'Class ' , 'Trait ' , 'Interface ' , 'Namespace ' , 'Object ' , 'Resource ' , 'String ' ,
3033 'Array ' , 'Float ' , 'Int ' , 'Bool ' , 'Boolean ' , 'Null ' , 'Void ' , 'Iterable ' ,
31- 'Parent ' , 'Self ' , 'Static ' , 'Mixed '
34+ 'Parent ' , 'Self ' , 'Static ' , 'Mixed ' ,
3235 ];
3336
3437 /**
@@ -41,16 +44,18 @@ public function handle(): int
4144 $ file = $ this ->argument ('file ' );
4245
4346 // Check if the provided file exists
44- if (!file_exists ($ file )) {
47+ if (! file_exists ($ file )) {
4548 $ this ->error ("File not found: $ file " );
49+
4650 return static ::FAILURE ;
4751 }
4852
4953 try {
50- $ parser = new NodeDbmlParser () ;
54+ $ parser = new NodeDbmlParser ;
5155 $ schema = $ parser ->parse ($ file );
5256 } catch (Throwable $ e ) {
53- $ this ->error ("Failed to parse DBML file: " . $ e ->getMessage ());
57+ $ this ->error ('Failed to parse DBML file: ' .$ e ->getMessage ());
58+
5459 return static ::FAILURE ;
5560 }
5661
@@ -71,6 +76,7 @@ public function handle(): int
7176 }
7277
7378 $ this ->info ("Generated $ generatedModels models and $ generatedMigrations migrations successfully. " );
79+
7480 return static ::SUCCESS ;
7581 }
7682
@@ -81,19 +87,20 @@ protected function generateModel(Table $table): bool
8187 // Check if the model name is a reserved PHP keyword
8288 if ($ this ->isForbiddenModelName ($ modelName )) {
8389 $ this ->error ("Model \"$ modelName \" for table \"{$ table ->getName ()}\" cannot be created because it is a reserved PHP keyword. " );
90+
8491 return false ;
8592 }
8693
8794 $ filePath = app_path ("Models/ $ modelName.php " );
8895
89- if (!$ this ->option ('force ' ) && $ this ->modelExists ($ filePath , $ modelName )) {
96+ if (! $ this ->option ('force ' ) && $ this ->modelExists ($ filePath , $ modelName )) {
9097 return false ;
9198 }
9299
93100 // Generate the content for the model
94101 $ content = $ this ->generateModelContent ($ table , $ modelName );
95102
96- if (!$ content ) {
103+ if (! $ content ) {
97104 return false ;
98105 }
99106
@@ -106,25 +113,26 @@ protected function generateModel(Table $table): bool
106113
107114 protected function generateMigration (Table $ table ): bool
108115 {
109- $ migrationName = 'create_ ' . Str::snake ($ table ->getName ()) . '_table ' ;
116+ $ migrationName = 'create_ ' . Str::snake ($ table ->getName ()). '_table ' ;
110117
111118 // Generate timestamp with incremental counter
112119 $ baseDate = now ()->format ('Y_m_d ' );
113120 $ sequence = str_pad ($ this ->migrationCounter , 6 , '0 ' , STR_PAD_LEFT );
114- $ timestamp = $ baseDate . '_ ' . $ sequence ;
121+ $ timestamp = $ baseDate. '_ ' . $ sequence ;
115122
116- $ fileName = $ timestamp . '_ ' . $ migrationName . '.php ' ;
123+ $ fileName = $ timestamp. '_ ' . $ migrationName. '.php ' ;
117124 $ filePath = database_path ("migrations/ $ fileName " );
118125
119126 // Check if migration already exists
120- if (!$ this ->option ('force ' ) && $ this ->migrationExists ($ table ->getName ())) {
127+ if (! $ this ->option ('force ' ) && $ this ->migrationExists ($ table ->getName ())) {
121128 $ this ->warn ("Migration for table {$ table ->getName ()} already exists. Skipping... " );
129+
122130 return false ;
123131 }
124132
125133 $ content = $ this ->generateMigrationContent ($ table );
126134
127- if (!$ content ) {
135+ if (! $ content ) {
128136 return false ;
129137 }
130138
@@ -161,16 +169,16 @@ private function generateModelContent(Table $table, string $modelName): ?string
161169 $ tab = str_repeat ("\t" , 2 );
162170 $ castsString = '' ;
163171
164- if (!empty ($ casts )) {
172+ if (! empty ($ casts )) {
165173 $ castsString = implode (", \n$ tab " , array_map (
166- fn ($ key , $ value ) => "' $ key' => ' $ value' " ,
174+ fn ($ key , $ value ) => "' $ key' => ' $ value' " ,
167175 array_keys ($ casts ),
168176 $ casts
169177 ));
170178 }
171179
172180 $ fillableString = '' ;
173- if (!empty ($ fillable )) {
181+ if (! empty ($ fillable )) {
174182 $ fillableString = implode (", \n$ tab " , $ fillable );
175183 }
176184
@@ -207,7 +215,8 @@ private function getStubContent(string $stubName): ?string
207215 }
208216
209217 // Fall back to package stubs
210- $ packageStubPath = __DIR__ . "/../../stubs/ $ stubName " ;
218+ $ packageStubPath = __DIR__ ."/../../stubs/ $ stubName " ;
219+
211220 return file_exists ($ packageStubPath ) ? file_get_contents ($ packageStubPath ) : null ;
212221 }
213222
@@ -216,8 +225,10 @@ private function getValidatedStubContent(string $stubName, string $type): ?strin
216225 $ stub = $ this ->getStubContent ($ stubName );
217226 if ($ stub === null ) {
218227 $ this ->error ("$ type stub not found. " );
228+
219229 return null ;
220230 }
231+
221232 return $ stub ;
222233 }
223234
@@ -231,27 +242,30 @@ private function modelExists(string $filePath, string $modelName): bool
231242 // Check if the model file already exists
232243 if (file_exists ($ filePath )) {
233244 $ this ->warn ("Model $ modelName already exists. Use --force to overwrite. " );
245+
234246 return true ;
235247 }
248+
236249 return false ;
237250 }
238251
239252 private function migrationExists (string $ tableName ): bool
240253 {
241- $ migrationPattern = '*_create_ ' . Str::snake ($ tableName ) . '_table.php ' ;
254+ $ migrationPattern = '*_create_ ' . Str::snake ($ tableName ). '_table.php ' ;
242255 $ migrationPath = database_path ('migrations ' );
243256
244- if (!is_dir ($ migrationPath )) {
257+ if (! is_dir ($ migrationPath )) {
245258 return false ;
246259 }
247260
248- $ existingMigrations = glob ($ migrationPath . '/ ' . $ migrationPattern );
249- return !empty ($ existingMigrations );
261+ $ existingMigrations = glob ($ migrationPath .'/ ' .$ migrationPattern );
262+
263+ return ! empty ($ existingMigrations );
250264 }
251265
252266 private function ensureDirectoryExists (string $ directory ): void
253267 {
254- if (!is_dir ($ directory )) {
268+ if (! is_dir ($ directory )) {
255269 mkdir ($ directory , 0755 , true );
256270 }
257271 }
@@ -260,29 +274,28 @@ private function generateFillable(array $columns): array
260274 {
261275 // Generate the fillable attributes by filtering out primary keys and certain columns
262276 return collect ($ columns )
263- ->filter (fn (Column $ col ) =>
264- !$ col ->isPrimaryKey () &&
265- !in_array ($ col ->getName (), ['created_at ' , 'updated_at ' , 'id ' ], true )
277+ ->filter (fn (Column $ col ) => ! $ col ->isPrimaryKey () &&
278+ ! in_array ($ col ->getName (), ['created_at ' , 'updated_at ' , 'id ' ], true )
266279 )
267- ->map (fn (Column $ col ) => "' " . $ col ->getName () . "' " )
280+ ->map (fn (Column $ col ) => "' " . $ col ->getName (). "' " )
268281 ->values ()
269282 ->toArray ();
270283 }
271284
272285 private function generateCasts (array $ columns ): array
273286 {
274287 return collect ($ columns )
275- ->mapWithKeys (fn (Column $ col ) => [
276- $ col ->getName () => $ this ->mapCastType ($ col )
288+ ->mapWithKeys (fn (Column $ col ) => [
289+ $ col ->getName () => $ this ->mapCastType ($ col ),
277290 ])
278- ->filter (fn ($ value ) => !empty ($ value ) && !in_array ($ value , ['string ' , 'integer ' ], true ))
291+ ->filter (fn ($ value ) => ! empty ($ value ) && ! in_array ($ value , ['string ' , 'integer ' ], true ))
279292 ->toArray ();
280293 }
281294
282295 private function generateBelongsToRelations (array $ columns ): array
283296 {
284297 return collect ($ columns )
285- ->filter (fn (Column $ col ) => count ($ col ->getRefs ()) > 0 )
298+ ->filter (fn (Column $ col ) => count ($ col ->getRefs ()) > 0 )
286299 ->map (function (Column $ col ) {
287300 $ reference = $ col ->getRefs ()[0 ];
288301 $ relatedTable = Str::studly (Str::singular ($ reference ->getRightTable ()->getTable ()));
@@ -312,7 +325,7 @@ private function generateHasManyRelations(Table $table): array
312325
313326 $ relatedTable = Str::studly (Str::singular ($ candidate ->getName ()));
314327 $ method = Str::camel (Str::studly (Str::plural ($ relatedTable )));
315- $ key = $ method . ': ' . $ candidate ->getName ();
328+ $ key = $ method. ': ' . $ candidate ->getName ();
316329
317330 if (isset ($ relations [$ key ])) {
318331 continue ;
@@ -352,25 +365,25 @@ private function formatRelationBody(array $relation): string
352365 {
353366 return match ($ relation ['type ' ]) {
354367 'hasMany ' => "return \$this->hasMany( {$ relation ['relatedTable ' ]}::class, ' {$ relation ['foreignKey ' ]}', ' {$ relation ['localKey ' ]}'); " ,
355- default => "return \$this->belongsTo( {$ relation ['relatedTable ' ]}::class, ' {$ relation ['foreignKey ' ]}' " . ($ relation ['ownerKey ' ] ? ", ' {$ relation ['ownerKey ' ]}' " : '' ) . '); ' ,
368+ default => "return \$this->belongsTo( {$ relation ['relatedTable ' ]}::class, ' {$ relation ['foreignKey ' ]}' " . ($ relation ['ownerKey ' ] ? ", ' {$ relation ['ownerKey ' ]}' " : '' ). '); ' ,
356369 };
357370 }
358371
359372 private function generateMigrationColumns (array $ columns ): string
360373 {
361374 return collect ($ columns )
362- ->map (fn (Column $ column ) => $ this ->buildColumnDefinition ($ column ))
375+ ->map (fn (Column $ column ) => $ this ->buildColumnDefinition ($ column ))
363376 ->implode ("\n" );
364377 }
365378
366379 private function generateIndexDefinitions (Table $ table ): string
367380 {
368381 $ definitions = collect ($ table ->getIndexes ())
369- ->map (fn (IndexDefinition $ index ) => $ this ->buildIndexDefinition ($ index ))
382+ ->map (fn (IndexDefinition $ index ) => $ this ->buildIndexDefinition ($ index ))
370383 ->filter ()
371384 ->implode ("\n" );
372385
373- return $ definitions === '' ? '' : "\n" . $ definitions ;
386+ return $ definitions === '' ? '' : "\n" . $ definitions ;
374387 }
375388
376389 private function buildColumnDefinition (Column $ column ): string
@@ -382,14 +395,14 @@ private function buildColumnDefinition(Column $column): string
382395 }
383396
384397 if ($ column ->getDefaultValue () !== null ) {
385- $ field .= '->default( ' . $ this ->formatDefaultValue ($ column ->getDefaultValue ()) . ') ' ;
398+ $ field .= '->default( ' . $ this ->formatDefaultValue ($ column ->getDefaultValue ()). ') ' ;
386399 }
387400
388- if ($ column ->isUnique () && !$ column ->isPrimaryKey ()) {
401+ if ($ column ->isUnique () && ! $ column ->isPrimaryKey ()) {
389402 $ field .= '->unique() ' ;
390403 }
391404
392- if ($ column ->isPrimaryKey () && !$ this ->isAutoIncrementingPrimaryKey ($ column )) {
405+ if ($ column ->isPrimaryKey () && ! $ this ->isAutoIncrementingPrimaryKey ($ column )) {
393406 $ field .= '->primary() ' ;
394407 }
395408
@@ -402,7 +415,7 @@ private function buildIndexDefinition(IndexDefinition $index): ?string
402415 return null ;
403416 }
404417
405- $ columns = '[ ' . implode (', ' , array_map (fn (string $ column ) => "' {$ column }' " , $ index ->getColumns ())) . '] ' ;
418+ $ columns = '[ ' . implode (', ' , array_map (fn (string $ column ) => "' {$ column }' " , $ index ->getColumns ())). '] ' ;
406419 $ method = $ index ->isUnique () ? 'unique ' : 'index ' ;
407420 $ name = $ index ->getName () ? ", ' {$ index ->getName ()}' " : '' ;
408421
@@ -425,7 +438,7 @@ private function resolveColumnBaseDefinition(Column $column): string
425438
426439 if (isset ($ this ->enums [$ column ->getType ()->getName ()])) {
427440 $ enumValues = collect ($ this ->enums [$ column ->getType ()->getName ()]->getValues ())
428- ->map (fn ($ value ) => "' {$ value ->getValue ()}' " )
441+ ->map (fn ($ value ) => "' {$ value ->getValue ()}' " )
429442 ->implode (', ' );
430443
431444 return "\$table->enum(' $ name', [ $ enumValues]) " ;
@@ -439,7 +452,8 @@ private function resolveColumnBaseDefinition(Column $column): string
439452 : "->constrained(' {$ referencedTable }') " ;
440453
441454 $ definition = "\$table->foreignId(' $ name') {$ constraint }" ;
442- return $ definition . $ this ->formatForeignKeyActions ($ reference );
455+
456+ return $ definition .$ this ->formatForeignKeyActions ($ reference );
443457 }
444458
445459 $ stringLength = max (1 , (int ) ($ args [0 ] ?? 255 ));
@@ -472,13 +486,13 @@ private function resolveColumnBaseDefinition(Column $column): string
472486 private function formatDefaultValue (?ColumnDefaultValue $ default ): string
473487 {
474488 if ($ default === null ) {
475- return " null " ;
489+ return ' null ' ;
476490 }
477491
478492 $ value = $ default ->getValue ();
479493
480494 if ($ default ->isExpression () && is_string ($ value )) {
481- return "DB::raw(' " . addslashes ($ value ) . "') " ;
495+ return "DB::raw(' " . addslashes ($ value ). "') " ;
482496 }
483497
484498 if (is_bool ($ value )) {
@@ -493,7 +507,7 @@ private function formatDefaultValue(?ColumnDefaultValue $default): string
493507 return 'null ' ;
494508 }
495509
496- return "' " . addslashes ((string ) $ value ) . "' " ;
510+ return "' " . addslashes ((string ) $ value ). "' " ;
497511 }
498512
499513 private function formatForeignKeyActions (ColumnReference $ reference ): string
@@ -540,7 +554,7 @@ private function mapCastType(Column $column): string
540554 'date ' => 'date ' ,
541555 'time ' => 'datetime ' ,
542556 'int ' , 'integer ' , 'bigint ' , 'smallint ' , 'tinyint ' => 'integer ' ,
543- 'decimal ' , 'numeric ' => isset ($ args [1 ]) ? 'decimal: ' . (int ) $ args [1 ] : 'float ' ,
557+ 'decimal ' , 'numeric ' => isset ($ args [1 ]) ? 'decimal: ' . (int ) $ args [1 ] : 'float ' ,
544558 'double ' , 'float ' => 'float ' ,
545559 default => '' ,
546560 };
@@ -560,4 +574,3 @@ private function generateTableProperty(string $tableName, string $modelName): st
560574 return '' ;
561575 }
562576}
563-
0 commit comments