@famgia/omnify-laravel 0.0.13 → 0.0.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -311,11 +311,18 @@ function schemaToBlueprint(schema, allSchemas) {
311
311
  }
312
312
  if (schema.options?.indexes) {
313
313
  for (const index of schema.options.indexes) {
314
- indexes.push({
315
- name: index.name,
316
- columns: index.columns.map(toColumnName),
317
- unique: index.unique ?? false
318
- });
314
+ if (typeof index === "string") {
315
+ indexes.push({
316
+ columns: [toColumnName(index)],
317
+ unique: false
318
+ });
319
+ } else {
320
+ indexes.push({
321
+ name: index.name,
322
+ columns: index.columns.map(toColumnName),
323
+ unique: index.unique ?? false
324
+ });
325
+ }
319
326
  }
320
327
  }
321
328
  if (schema.options?.unique) {
@@ -1033,6 +1040,579 @@ function generateMigrationsFromChanges(changes, options = {}) {
1033
1040
  return migrations;
1034
1041
  }
1035
1042
 
1043
+ // src/utils.ts
1044
+ function toSnakeCase(str) {
1045
+ return str.replace(/([A-Z])/g, "_$1").replace(/^_/, "").toLowerCase();
1046
+ }
1047
+ function toPascalCase(str) {
1048
+ return str.replace(/[-_](.)/g, (_, c) => c.toUpperCase()).replace(/^(.)/, (_, c) => c.toUpperCase());
1049
+ }
1050
+ function toCamelCase(str) {
1051
+ const pascal = toPascalCase(str);
1052
+ return pascal.charAt(0).toLowerCase() + pascal.slice(1);
1053
+ }
1054
+ function pluralize(word) {
1055
+ if (word.endsWith("y") && !["ay", "ey", "iy", "oy", "uy"].some((v) => word.endsWith(v))) {
1056
+ return word.slice(0, -1) + "ies";
1057
+ }
1058
+ if (word.endsWith("s") || word.endsWith("x") || word.endsWith("z") || word.endsWith("ch") || word.endsWith("sh")) {
1059
+ return word + "es";
1060
+ }
1061
+ return word + "s";
1062
+ }
1063
+
1064
+ // src/model/generator.ts
1065
+ var DEFAULT_OPTIONS = {
1066
+ baseModelNamespace: "App\\Models\\OmnifyBase",
1067
+ modelNamespace: "App\\Models",
1068
+ baseModelClassName: "BaseModel",
1069
+ baseModelPath: "app/Models/OmnifyBase",
1070
+ modelPath: "app/Models"
1071
+ };
1072
+ function resolveOptions(options) {
1073
+ return {
1074
+ baseModelNamespace: options?.baseModelNamespace ?? DEFAULT_OPTIONS.baseModelNamespace,
1075
+ modelNamespace: options?.modelNamespace ?? DEFAULT_OPTIONS.modelNamespace,
1076
+ baseModelClassName: options?.baseModelClassName ?? DEFAULT_OPTIONS.baseModelClassName,
1077
+ baseModelPath: options?.baseModelPath ?? DEFAULT_OPTIONS.baseModelPath,
1078
+ modelPath: options?.modelPath ?? DEFAULT_OPTIONS.modelPath
1079
+ };
1080
+ }
1081
+ function getCastType(propDef) {
1082
+ switch (propDef.type) {
1083
+ case "Boolean":
1084
+ return "boolean";
1085
+ case "Int":
1086
+ case "BigInt":
1087
+ return "integer";
1088
+ case "Float":
1089
+ return "float";
1090
+ case "Decimal":
1091
+ return "decimal:" + (propDef.scale ?? 2);
1092
+ case "Json":
1093
+ return "array";
1094
+ case "Date":
1095
+ return "date";
1096
+ case "Timestamp":
1097
+ return "datetime";
1098
+ case "Password":
1099
+ return "hashed";
1100
+ default:
1101
+ return null;
1102
+ }
1103
+ }
1104
+ function isNullable(propDef) {
1105
+ return "nullable" in propDef && propDef.nullable === true;
1106
+ }
1107
+ function getPhpDocType(propDef, schemas) {
1108
+ const nullable = isNullable(propDef);
1109
+ switch (propDef.type) {
1110
+ case "String":
1111
+ case "Text":
1112
+ case "LongText":
1113
+ case "Email":
1114
+ case "Password":
1115
+ return "string" + (nullable ? "|null" : "");
1116
+ case "Int":
1117
+ case "BigInt":
1118
+ return "int" + (nullable ? "|null" : "");
1119
+ case "Float":
1120
+ case "Decimal":
1121
+ return "float" + (nullable ? "|null" : "");
1122
+ case "Boolean":
1123
+ return "bool" + (nullable ? "|null" : "");
1124
+ case "Date":
1125
+ case "Time":
1126
+ case "Timestamp":
1127
+ return "\\Carbon\\Carbon" + (nullable ? "|null" : "");
1128
+ case "Json":
1129
+ return "array" + (nullable ? "|null" : "");
1130
+ case "Enum":
1131
+ case "EnumRef":
1132
+ return "string" + (nullable ? "|null" : "");
1133
+ case "Association": {
1134
+ const assoc = propDef;
1135
+ if (assoc.target) {
1136
+ const className = toPascalCase(assoc.target);
1137
+ switch (assoc.relation) {
1138
+ case "OneToMany":
1139
+ case "ManyToMany":
1140
+ case "MorphMany":
1141
+ case "MorphToMany":
1142
+ case "MorphedByMany":
1143
+ return `\\Illuminate\\Database\\Eloquent\\Collection<${className}>`;
1144
+ default:
1145
+ return className + "|null";
1146
+ }
1147
+ }
1148
+ return "mixed";
1149
+ }
1150
+ default:
1151
+ return "mixed";
1152
+ }
1153
+ }
1154
+ function generateBaseModel(schemas, options, stubContent) {
1155
+ const modelMap = Object.values(schemas).filter((s) => s.kind !== "enum").map((s) => {
1156
+ const className = toPascalCase(s.name);
1157
+ return ` '${s.name}' => \\${options.modelNamespace}\\${className}::class,`;
1158
+ }).join("\n");
1159
+ const content = stubContent.replace(/\{\{BASE_MODEL_NAMESPACE\}\}/g, options.baseModelNamespace).replace(/\{\{BASE_MODEL_CLASS\}\}/g, options.baseModelClassName).replace(/\{\{MODEL_MAP\}\}/g, modelMap);
1160
+ return {
1161
+ path: `${options.baseModelPath}/${options.baseModelClassName}.php`,
1162
+ content,
1163
+ type: "base-model",
1164
+ overwrite: true,
1165
+ schemaName: "__base__"
1166
+ };
1167
+ }
1168
+ function generateEntityBaseModel(schema, schemas, options, stubContent, authStubContent) {
1169
+ const className = toPascalCase(schema.name);
1170
+ const tableName = schema.options?.tableName ?? pluralize(toSnakeCase(schema.name));
1171
+ const isAuth = schema.options?.authenticatable ?? false;
1172
+ const primaryKey = "id";
1173
+ const idType = schema.options?.idType ?? "BigInt";
1174
+ const isUuid = idType === "Uuid";
1175
+ const isStringKey = idType === "Uuid" || idType === "String";
1176
+ const imports = [];
1177
+ const traits = [];
1178
+ const fillable = [];
1179
+ const hidden = [];
1180
+ const appends = [];
1181
+ const casts = [];
1182
+ const relations = [];
1183
+ const docProperties = [];
1184
+ if (schema.options?.softDelete) {
1185
+ imports.push("use Illuminate\\Database\\Eloquent\\SoftDeletes;");
1186
+ traits.push(" use SoftDeletes;");
1187
+ }
1188
+ const properties = schema.properties ?? {};
1189
+ for (const [propName, propDef] of Object.entries(properties)) {
1190
+ const snakeName = toSnakeCase(propName);
1191
+ const phpType = getPhpDocType(propDef, schemas);
1192
+ docProperties.push(` * @property ${phpType} $${snakeName}`);
1193
+ if (propDef.type === "Association") {
1194
+ const assoc = propDef;
1195
+ if (assoc.target) {
1196
+ imports.push(`use ${options.modelNamespace}\\${toPascalCase(assoc.target)};`);
1197
+ }
1198
+ relations.push(generateRelation(propName, assoc, options));
1199
+ if (assoc.relation === "ManyToOne" || assoc.relation === "OneToOne") {
1200
+ if (!assoc.mappedBy) {
1201
+ const fkName = toSnakeCase(propName) + "_id";
1202
+ fillable.push(` '${fkName}',`);
1203
+ docProperties.push(` * @property int|null $${fkName}`);
1204
+ }
1205
+ }
1206
+ } else if (propDef.type === "Password") {
1207
+ fillable.push(` '${snakeName}',`);
1208
+ hidden.push(` '${snakeName}',`);
1209
+ const cast = getCastType(propDef);
1210
+ if (cast) {
1211
+ casts.push(` '${snakeName}' => '${cast}',`);
1212
+ }
1213
+ } else if (propDef.type === "File") {
1214
+ const relMethod = generateFileRelation(propName, propDef);
1215
+ relations.push(relMethod);
1216
+ } else {
1217
+ fillable.push(` '${snakeName}',`);
1218
+ const cast = getCastType(propDef);
1219
+ if (cast) {
1220
+ casts.push(` '${snakeName}' => '${cast}',`);
1221
+ }
1222
+ }
1223
+ }
1224
+ const docComment = `/**
1225
+ * ${className}BaseModel
1226
+ *
1227
+ ${docProperties.join("\n")}
1228
+ */`;
1229
+ const stub = isAuth ? authStubContent : stubContent;
1230
+ const keyType = isStringKey ? ` /**
1231
+ * The "type" of the primary key ID.
1232
+ */
1233
+ protected $keyType = 'string';
1234
+
1235
+ ` : "";
1236
+ const incrementing = isUuid ? ` /**
1237
+ * Indicates if the IDs are auto-incrementing.
1238
+ */
1239
+ public $incrementing = false;
1240
+
1241
+ ` : "";
1242
+ if (isUuid) {
1243
+ imports.push("use Illuminate\\Database\\Eloquent\\Concerns\\HasUuids;");
1244
+ traits.push(" use HasUuids;");
1245
+ }
1246
+ const content = stub.replace(/\{\{BASE_MODEL_NAMESPACE\}\}/g, options.baseModelNamespace).replace(/\{\{BASE_MODEL_CLASS\}\}/g, options.baseModelClassName).replace(/\{\{CLASS_NAME\}\}/g, className).replace(/\{\{TABLE_NAME\}\}/g, tableName).replace(/\{\{PRIMARY_KEY\}\}/g, primaryKey).replace(/\{\{KEY_TYPE\}\}/g, keyType).replace(/\{\{INCREMENTING\}\}/g, incrementing).replace(/\{\{TIMESTAMPS\}\}/g, schema.options?.timestamps !== false ? "true" : "false").replace(/\{\{IMPORTS\}\}/g, [...new Set(imports)].sort().join("\n")).replace(/\{\{TRAITS\}\}/g, traits.join("\n")).replace(/\{\{DOC_COMMENT\}\}/g, docComment).replace(/\{\{FILLABLE\}\}/g, fillable.join("\n")).replace(/\{\{HIDDEN\}\}/g, hidden.join("\n")).replace(/\{\{APPENDS\}\}/g, appends.join("\n")).replace(/\{\{CASTS\}\}/g, casts.join("\n")).replace(/\{\{RELATIONS\}\}/g, relations.join("\n\n"));
1247
+ return {
1248
+ path: `${options.baseModelPath}/${className}BaseModel.php`,
1249
+ content,
1250
+ type: "entity-base",
1251
+ overwrite: true,
1252
+ schemaName: schema.name
1253
+ };
1254
+ }
1255
+ function generateRelation(propName, assoc, options) {
1256
+ const methodName = toCamelCase(propName);
1257
+ const targetClass = assoc.target ? toPascalCase(assoc.target) : "";
1258
+ const fkName = toSnakeCase(propName) + "_id";
1259
+ switch (assoc.relation) {
1260
+ case "ManyToOne":
1261
+ return ` /**
1262
+ * Get the ${propName} that owns this model.
1263
+ */
1264
+ public function ${methodName}(): BelongsTo
1265
+ {
1266
+ return $this->belongsTo(${targetClass}::class, '${fkName}');
1267
+ }`;
1268
+ case "OneToOne":
1269
+ if (assoc.mappedBy) {
1270
+ return ` /**
1271
+ * Get the ${propName} for this model.
1272
+ */
1273
+ public function ${methodName}(): HasOne
1274
+ {
1275
+ return $this->hasOne(${targetClass}::class, '${toSnakeCase(assoc.mappedBy)}_id');
1276
+ }`;
1277
+ }
1278
+ return ` /**
1279
+ * Get the ${propName} that owns this model.
1280
+ */
1281
+ public function ${methodName}(): BelongsTo
1282
+ {
1283
+ return $this->belongsTo(${targetClass}::class, '${fkName}');
1284
+ }`;
1285
+ case "OneToMany":
1286
+ return ` /**
1287
+ * Get the ${propName} for this model.
1288
+ */
1289
+ public function ${methodName}(): HasMany
1290
+ {
1291
+ return $this->hasMany(${targetClass}::class, '${toSnakeCase(assoc.inversedBy ?? propName)}_id');
1292
+ }`;
1293
+ case "ManyToMany": {
1294
+ const pivotTable = assoc.joinTable ?? `${toSnakeCase(propName)}_pivot`;
1295
+ return ` /**
1296
+ * The ${propName} that belong to this model.
1297
+ */
1298
+ public function ${methodName}(): BelongsToMany
1299
+ {
1300
+ return $this->belongsToMany(${targetClass}::class, '${pivotTable}')
1301
+ ->withTimestamps();
1302
+ }`;
1303
+ }
1304
+ case "MorphTo":
1305
+ return ` /**
1306
+ * Get the parent ${propName} model.
1307
+ */
1308
+ public function ${methodName}(): MorphTo
1309
+ {
1310
+ return $this->morphTo('${methodName}');
1311
+ }`;
1312
+ case "MorphOne":
1313
+ return ` /**
1314
+ * Get the ${propName} for this model.
1315
+ */
1316
+ public function ${methodName}(): MorphOne
1317
+ {
1318
+ return $this->morphOne(${targetClass}::class, '${assoc.morphName ?? propName}');
1319
+ }`;
1320
+ case "MorphMany":
1321
+ return ` /**
1322
+ * Get the ${propName} for this model.
1323
+ */
1324
+ public function ${methodName}(): MorphMany
1325
+ {
1326
+ return $this->morphMany(${targetClass}::class, '${assoc.morphName ?? propName}');
1327
+ }`;
1328
+ default:
1329
+ return ` // TODO: Implement ${assoc.relation} relation for ${propName}`;
1330
+ }
1331
+ }
1332
+ function generateFileRelation(propName, propDef) {
1333
+ const methodName = toCamelCase(propName);
1334
+ const relationType = propDef.multiple ? "MorphMany" : "MorphOne";
1335
+ const relationMethod = propDef.multiple ? "morphMany" : "morphOne";
1336
+ return ` /**
1337
+ * Get the ${propName} file(s) for this model.
1338
+ */
1339
+ public function ${methodName}(): ${relationType}
1340
+ {
1341
+ return $this->${relationMethod}(FileUpload::class, 'uploadable')
1342
+ ->where('attribute_name', '${propName}');
1343
+ }`;
1344
+ }
1345
+ function generateEntityModel(schema, options, stubContent) {
1346
+ const className = toPascalCase(schema.name);
1347
+ const content = stubContent.replace(/\{\{BASE_MODEL_NAMESPACE\}\}/g, options.baseModelNamespace).replace(/\{\{MODEL_NAMESPACE\}\}/g, options.modelNamespace).replace(/\{\{CLASS_NAME\}\}/g, className);
1348
+ return {
1349
+ path: `${options.modelPath}/${className}.php`,
1350
+ content,
1351
+ type: "entity",
1352
+ overwrite: false,
1353
+ // Never overwrite user models
1354
+ schemaName: schema.name
1355
+ };
1356
+ }
1357
+ function getStubContent(stubName) {
1358
+ const stubs = {
1359
+ "base-model": `<?php
1360
+
1361
+ namespace {{BASE_MODEL_NAMESPACE}};
1362
+
1363
+ /**
1364
+ * Base model class for all Omnify-generated models.
1365
+ * Contains model mapping for polymorphic relations.
1366
+ *
1367
+ * DO NOT EDIT - This file is auto-generated by Omnify.
1368
+ * Any changes will be overwritten on next generation.
1369
+ *
1370
+ * @generated by @famgia/omnify-laravel
1371
+ */
1372
+
1373
+ use Illuminate\\Database\\Eloquent\\Model;
1374
+ use Illuminate\\Database\\Eloquent\\Relations\\Relation;
1375
+
1376
+ abstract class {{BASE_MODEL_CLASS}} extends Model
1377
+ {
1378
+ /**
1379
+ * Model class map for polymorphic relations.
1380
+ */
1381
+ protected static array $modelMap = [
1382
+ {{MODEL_MAP}}
1383
+ ];
1384
+
1385
+ /**
1386
+ * Boot the model and register morph map.
1387
+ */
1388
+ protected static function boot(): void
1389
+ {
1390
+ parent::boot();
1391
+
1392
+ // Register morph map for polymorphic relations
1393
+ Relation::enforceMorphMap(static::$modelMap);
1394
+ }
1395
+
1396
+ /**
1397
+ * Get the model class for a given morph type.
1398
+ */
1399
+ public static function getModelClass(string $morphType): ?string
1400
+ {
1401
+ return static::$modelMap[$morphType] ?? null;
1402
+ }
1403
+ }
1404
+ `,
1405
+ "entity-base": `<?php
1406
+
1407
+ namespace {{BASE_MODEL_NAMESPACE}};
1408
+
1409
+ /**
1410
+ * DO NOT EDIT - This file is auto-generated by Omnify.
1411
+ * Any changes will be overwritten on next generation.
1412
+ *
1413
+ * @generated by @famgia/omnify-laravel
1414
+ */
1415
+
1416
+ use Illuminate\\Database\\Eloquent\\Relations\\BelongsTo;
1417
+ use Illuminate\\Database\\Eloquent\\Relations\\HasMany;
1418
+ use Illuminate\\Database\\Eloquent\\Relations\\HasOne;
1419
+ use Illuminate\\Database\\Eloquent\\Relations\\BelongsToMany;
1420
+ use Illuminate\\Database\\Eloquent\\Relations\\MorphTo;
1421
+ use Illuminate\\Database\\Eloquent\\Relations\\MorphOne;
1422
+ use Illuminate\\Database\\Eloquent\\Relations\\MorphMany;
1423
+ use Illuminate\\Database\\Eloquent\\Relations\\MorphToMany;
1424
+ use Illuminate\\Database\\Eloquent\\Collection as EloquentCollection;
1425
+ {{IMPORTS}}
1426
+
1427
+ {{DOC_COMMENT}}
1428
+ class {{CLASS_NAME}}BaseModel extends {{BASE_MODEL_CLASS}}
1429
+ {
1430
+ {{TRAITS}}
1431
+ /**
1432
+ * The table associated with the model.
1433
+ */
1434
+ protected $table = '{{TABLE_NAME}}';
1435
+
1436
+ /**
1437
+ * The primary key for the model.
1438
+ */
1439
+ protected $primaryKey = '{{PRIMARY_KEY}}';
1440
+
1441
+ {{KEY_TYPE}}
1442
+ {{INCREMENTING}}
1443
+ /**
1444
+ * Indicates if the model should be timestamped.
1445
+ */
1446
+ public $timestamps = {{TIMESTAMPS}};
1447
+
1448
+ /**
1449
+ * The attributes that are mass assignable.
1450
+ */
1451
+ protected $fillable = [
1452
+ {{FILLABLE}}
1453
+ ];
1454
+
1455
+ /**
1456
+ * The attributes that should be hidden for serialization.
1457
+ */
1458
+ protected $hidden = [
1459
+ {{HIDDEN}}
1460
+ ];
1461
+
1462
+ /**
1463
+ * The accessors to append to the model's array form.
1464
+ */
1465
+ protected $appends = [
1466
+ {{APPENDS}}
1467
+ ];
1468
+
1469
+ /**
1470
+ * Get the attributes that should be cast.
1471
+ */
1472
+ protected function casts(): array
1473
+ {
1474
+ return [
1475
+ {{CASTS}}
1476
+ ];
1477
+ }
1478
+
1479
+ {{RELATIONS}}
1480
+ }
1481
+ `,
1482
+ "entity-base-auth": `<?php
1483
+
1484
+ namespace {{BASE_MODEL_NAMESPACE}};
1485
+
1486
+ /**
1487
+ * DO NOT EDIT - This file is auto-generated by Omnify.
1488
+ * Any changes will be overwritten on next generation.
1489
+ *
1490
+ * @generated by @famgia/omnify-laravel
1491
+ */
1492
+
1493
+ use Illuminate\\Foundation\\Auth\\User as Authenticatable;
1494
+ use Illuminate\\Database\\Eloquent\\Relations\\BelongsTo;
1495
+ use Illuminate\\Database\\Eloquent\\Relations\\HasMany;
1496
+ use Illuminate\\Database\\Eloquent\\Relations\\HasOne;
1497
+ use Illuminate\\Database\\Eloquent\\Relations\\BelongsToMany;
1498
+ use Illuminate\\Database\\Eloquent\\Relations\\MorphTo;
1499
+ use Illuminate\\Database\\Eloquent\\Relations\\MorphOne;
1500
+ use Illuminate\\Database\\Eloquent\\Relations\\MorphMany;
1501
+ use Illuminate\\Database\\Eloquent\\Relations\\MorphToMany;
1502
+ use Illuminate\\Database\\Eloquent\\Collection as EloquentCollection;
1503
+ use Illuminate\\Notifications\\Notifiable;
1504
+ {{IMPORTS}}
1505
+
1506
+ {{DOC_COMMENT}}
1507
+ class {{CLASS_NAME}}BaseModel extends Authenticatable
1508
+ {
1509
+ use Notifiable;
1510
+ {{TRAITS}}
1511
+ /**
1512
+ * The table associated with the model.
1513
+ */
1514
+ protected $table = '{{TABLE_NAME}}';
1515
+
1516
+ /**
1517
+ * The primary key for the model.
1518
+ */
1519
+ protected $primaryKey = '{{PRIMARY_KEY}}';
1520
+
1521
+ {{KEY_TYPE}}
1522
+ {{INCREMENTING}}
1523
+ /**
1524
+ * Indicates if the model should be timestamped.
1525
+ */
1526
+ public $timestamps = {{TIMESTAMPS}};
1527
+
1528
+ /**
1529
+ * The attributes that are mass assignable.
1530
+ */
1531
+ protected $fillable = [
1532
+ {{FILLABLE}}
1533
+ ];
1534
+
1535
+ /**
1536
+ * The attributes that should be hidden for serialization.
1537
+ */
1538
+ protected $hidden = [
1539
+ {{HIDDEN}}
1540
+ ];
1541
+
1542
+ /**
1543
+ * The accessors to append to the model's array form.
1544
+ */
1545
+ protected $appends = [
1546
+ {{APPENDS}}
1547
+ ];
1548
+
1549
+ /**
1550
+ * Get the attributes that should be cast.
1551
+ */
1552
+ protected function casts(): array
1553
+ {
1554
+ return [
1555
+ {{CASTS}}
1556
+ ];
1557
+ }
1558
+
1559
+ {{RELATIONS}}
1560
+ }
1561
+ `,
1562
+ "entity": `<?php
1563
+
1564
+ namespace {{MODEL_NAMESPACE}};
1565
+
1566
+ use {{BASE_MODEL_NAMESPACE}}\\{{CLASS_NAME}}BaseModel;
1567
+ use Illuminate\\Database\\Eloquent\\Factories\\HasFactory;
1568
+
1569
+ /**
1570
+ * {{CLASS_NAME}} Model
1571
+ *
1572
+ * This file is generated once and can be customized.
1573
+ * Add your custom methods and logic here.
1574
+ */
1575
+ class {{CLASS_NAME}} extends {{CLASS_NAME}}BaseModel
1576
+ {
1577
+ use HasFactory;
1578
+
1579
+ /**
1580
+ * Create a new model instance.
1581
+ */
1582
+ public function __construct(array $attributes = [])
1583
+ {
1584
+ parent::__construct($attributes);
1585
+ }
1586
+
1587
+ // Add your custom methods here
1588
+ }
1589
+ `
1590
+ };
1591
+ return stubs[stubName] ?? "";
1592
+ }
1593
+ function generateModels(schemas, options) {
1594
+ const resolved = resolveOptions(options);
1595
+ const models = [];
1596
+ models.push(generateBaseModel(schemas, resolved, getStubContent("base-model")));
1597
+ for (const schema of Object.values(schemas)) {
1598
+ if (schema.kind === "enum") {
1599
+ continue;
1600
+ }
1601
+ models.push(generateEntityBaseModel(
1602
+ schema,
1603
+ schemas,
1604
+ resolved,
1605
+ getStubContent("entity-base"),
1606
+ getStubContent("entity-base-auth")
1607
+ ));
1608
+ models.push(generateEntityModel(schema, resolved, getStubContent("entity")));
1609
+ }
1610
+ return models;
1611
+ }
1612
+ function getModelPath(model) {
1613
+ return model.path;
1614
+ }
1615
+
1036
1616
  // src/plugin.ts
1037
1617
  var LARAVEL_CONFIG_SCHEMA = {
1038
1618
  fields: [
@@ -1044,6 +1624,30 @@ var LARAVEL_CONFIG_SCHEMA = {
1044
1624
  default: "database/migrations",
1045
1625
  group: "output"
1046
1626
  },
1627
+ {
1628
+ key: "modelsPath",
1629
+ type: "path",
1630
+ label: "Models Path",
1631
+ description: "Directory for user-editable model files",
1632
+ default: "app/Models",
1633
+ group: "output"
1634
+ },
1635
+ {
1636
+ key: "baseModelsPath",
1637
+ type: "path",
1638
+ label: "Base Models Path",
1639
+ description: "Directory for auto-generated base model files",
1640
+ default: "app/Models/OmnifyBase",
1641
+ group: "output"
1642
+ },
1643
+ {
1644
+ key: "generateModels",
1645
+ type: "boolean",
1646
+ label: "Generate Models",
1647
+ description: "Generate Eloquent model classes",
1648
+ default: true,
1649
+ group: "options"
1650
+ },
1047
1651
  {
1048
1652
  key: "connection",
1049
1653
  type: "string",
@@ -1054,41 +1658,69 @@ var LARAVEL_CONFIG_SCHEMA = {
1054
1658
  }
1055
1659
  ]
1056
1660
  };
1057
- function resolveOptions(options) {
1661
+ function resolveOptions2(options) {
1058
1662
  return {
1059
1663
  migrationsPath: options?.migrationsPath ?? "database/migrations",
1664
+ modelsPath: options?.modelsPath ?? "app/Models",
1665
+ baseModelsPath: options?.baseModelsPath ?? "app/Models/OmnifyBase",
1666
+ modelNamespace: options?.modelNamespace ?? "App\\Models",
1667
+ baseModelNamespace: options?.baseModelNamespace ?? "App\\Models\\OmnifyBase",
1668
+ generateModels: options?.generateModels ?? true,
1060
1669
  connection: options?.connection,
1061
1670
  timestamp: options?.timestamp
1062
1671
  };
1063
1672
  }
1064
1673
  function laravelPlugin(options) {
1065
- const resolved = resolveOptions(options);
1674
+ const resolved = resolveOptions2(options);
1675
+ const migrationGenerator = {
1676
+ name: "laravel-migrations",
1677
+ description: "Generate Laravel migration files",
1678
+ generate: async (ctx) => {
1679
+ const migrationOptions = {
1680
+ connection: resolved.connection,
1681
+ timestamp: resolved.timestamp
1682
+ };
1683
+ const migrations = generateMigrations(ctx.schemas, migrationOptions);
1684
+ return migrations.map((migration) => ({
1685
+ path: getMigrationPath(migration, resolved.migrationsPath),
1686
+ content: migration.content,
1687
+ type: "migration",
1688
+ metadata: {
1689
+ tableName: migration.tables[0],
1690
+ migrationType: migration.type
1691
+ }
1692
+ }));
1693
+ }
1694
+ };
1695
+ const modelGenerator = {
1696
+ name: "laravel-models",
1697
+ description: "Generate Eloquent model classes",
1698
+ generate: async (ctx) => {
1699
+ const modelOptions = {
1700
+ modelNamespace: resolved.modelNamespace,
1701
+ baseModelNamespace: resolved.baseModelNamespace,
1702
+ modelPath: resolved.modelsPath,
1703
+ baseModelPath: resolved.baseModelsPath
1704
+ };
1705
+ const models = generateModels(ctx.schemas, modelOptions);
1706
+ return models.map((model) => ({
1707
+ path: getModelPath(model),
1708
+ content: model.content,
1709
+ type: "model",
1710
+ // Skip writing user models if they already exist
1711
+ skipIfExists: !model.overwrite,
1712
+ metadata: {
1713
+ modelType: model.type,
1714
+ schemaName: model.schemaName
1715
+ }
1716
+ }));
1717
+ }
1718
+ };
1066
1719
  return {
1067
1720
  name: "@famgia/omnify-laravel",
1068
- version: "0.0.13",
1721
+ version: "0.0.14",
1069
1722
  configSchema: LARAVEL_CONFIG_SCHEMA,
1070
- generators: [
1071
- {
1072
- name: "laravel-migrations",
1073
- description: "Generate Laravel migration files",
1074
- generate: async (ctx) => {
1075
- const migrationOptions = {
1076
- connection: resolved.connection,
1077
- timestamp: resolved.timestamp
1078
- };
1079
- const migrations = generateMigrations(ctx.schemas, migrationOptions);
1080
- return migrations.map((migration) => ({
1081
- path: getMigrationPath(migration, resolved.migrationsPath),
1082
- content: migration.content,
1083
- type: "migration",
1084
- metadata: {
1085
- tableName: migration.tables[0],
1086
- migrationType: migration.type
1087
- }
1088
- }));
1089
- }
1090
- }
1091
- ]
1723
+ generators: resolved.generateModels ? [migrationGenerator, modelGenerator] : [migrationGenerator]
1092
1724
  };
1093
1725
  }
1094
1726
  // Annotate the CommonJS export names for ESM import in node: