@famgia/omnify-laravel 2.0.21 → 2.0.22

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.
@@ -420,7 +420,8 @@ function expandCompoundType(propName, property, customTypes, options = {}) {
420
420
  return expanded;
421
421
  }
422
422
  function schemaToBlueprint(schema, allSchemas, options = {}) {
423
- const { customTypes = /* @__PURE__ */ new Map(), pluginEnums = /* @__PURE__ */ new Map(), locale } = options;
423
+ const { customTypes = /* @__PURE__ */ new Map(), pluginEnums = /* @__PURE__ */ new Map(), locale, excludeFKs } = options;
424
+ const schemaName = options.schemaName ?? schema.name;
424
425
  const compoundOptions = { locale, pluginEnums };
425
426
  const tableName = schema.options?.tableName ?? toTableName(schema.name);
426
427
  const columns = [];
@@ -490,7 +491,10 @@ function schemaToBlueprint(schema, allSchemas, options = {}) {
490
491
  if (!explicitColumnNames.has(fkResult.column.name)) {
491
492
  columns.push(fkResult.column);
492
493
  }
493
- foreignKeys.push(fkResult.foreignKey);
494
+ const fkKey = `${schemaName}.${propName}`;
495
+ if (!excludeFKs?.has(fkKey)) {
496
+ foreignKeys.push(fkResult.foreignKey);
497
+ }
494
498
  indexes.push(fkResult.index);
495
499
  }
496
500
  const polyResult = generatePolymorphicColumns(propName, property, allSchemas);
@@ -1157,6 +1161,143 @@ function extractDependencies(schema) {
1157
1161
  }
1158
1162
  return deps;
1159
1163
  }
1164
+ function detectCircularDependencies(schemas) {
1165
+ const circularFKs = /* @__PURE__ */ new Set();
1166
+ const graph = /* @__PURE__ */ new Map();
1167
+ for (const schema of Object.values(schemas)) {
1168
+ if (schema.kind === "enum" || !schema.properties) continue;
1169
+ const refs = /* @__PURE__ */ new Map();
1170
+ for (const [propName, property] of Object.entries(schema.properties)) {
1171
+ if (property.type !== "Association") continue;
1172
+ const assocProp = property;
1173
+ if ((assocProp.relation === "ManyToOne" || assocProp.relation === "OneToOne") && !assocProp.mappedBy && assocProp.target && assocProp.target !== schema.name) {
1174
+ refs.set(assocProp.target, propName);
1175
+ }
1176
+ }
1177
+ if (refs.size > 0) {
1178
+ graph.set(schema.name, refs);
1179
+ }
1180
+ }
1181
+ for (const [schemaA, refsA] of graph) {
1182
+ for (const [targetB, propNameA] of refsA) {
1183
+ if (targetB === schemaA) continue;
1184
+ const refsB = graph.get(targetB);
1185
+ if (refsB && refsB.has(schemaA)) {
1186
+ if (schemaA > targetB) {
1187
+ circularFKs.add(`${schemaA}.${propNameA}`);
1188
+ } else {
1189
+ const propNameB = refsB.get(schemaA);
1190
+ circularFKs.add(`${targetB}.${propNameB}`);
1191
+ }
1192
+ }
1193
+ }
1194
+ }
1195
+ return circularFKs;
1196
+ }
1197
+ function extractDeferredFKs(schemas, circularFKs) {
1198
+ const deferred = [];
1199
+ for (const schema of Object.values(schemas)) {
1200
+ if (schema.kind === "enum" || !schema.properties) continue;
1201
+ const tableName = schema.options?.tableName ?? toTableName(schema.name);
1202
+ for (const [propName, property] of Object.entries(schema.properties)) {
1203
+ const key = `${schema.name}.${propName}`;
1204
+ if (!circularFKs.has(key)) continue;
1205
+ const assocProp = property;
1206
+ if (!assocProp.target) continue;
1207
+ const targetSchema = schemas[assocProp.target];
1208
+ const targetTable = targetSchema?.options?.tableName ?? toTableName(assocProp.target);
1209
+ const columnName = toColumnName(propName) + "_id";
1210
+ deferred.push({
1211
+ tableName,
1212
+ columnName,
1213
+ targetTable,
1214
+ onDelete: assocProp.onDelete,
1215
+ onUpdate: assocProp.onUpdate
1216
+ });
1217
+ }
1218
+ }
1219
+ return deferred;
1220
+ }
1221
+ function generateDeferredFKMigration(deferredFKs, options = {}) {
1222
+ if (deferredFKs.length === 0) return null;
1223
+ const timestamp = options.timestamp ?? generateTimestamp();
1224
+ const fileName = `${timestamp}_add_circular_foreign_keys.php`;
1225
+ const connection = options.connection ? `
1226
+ protected $connection = '${options.connection}';
1227
+ ` : "";
1228
+ const byTable = /* @__PURE__ */ new Map();
1229
+ for (const fk of deferredFKs) {
1230
+ const existing = byTable.get(fk.tableName) ?? [];
1231
+ existing.push(fk);
1232
+ byTable.set(fk.tableName, existing);
1233
+ }
1234
+ const upStatements = [];
1235
+ const downStatements = [];
1236
+ for (const [tableName, fks] of byTable) {
1237
+ const fkLines = fks.map((fk) => {
1238
+ let line = ` $table->foreign('${fk.columnName}')->references('id')->on('${fk.targetTable}')`;
1239
+ if (fk.onDelete) line += `->onDelete('${fk.onDelete}')`;
1240
+ if (fk.onUpdate) line += `->onUpdate('${fk.onUpdate}')`;
1241
+ return line + ";";
1242
+ });
1243
+ upStatements.push(` Schema::table('${tableName}', function (Blueprint $table) {
1244
+ ${fkLines.join("\n")}
1245
+ });`);
1246
+ const dropLines = fks.map(
1247
+ (fk) => ` $table->dropForeign(['${fk.columnName}']);`
1248
+ );
1249
+ downStatements.push(` Schema::table('${tableName}', function (Blueprint $table) {
1250
+ ${dropLines.join("\n")}
1251
+ });`);
1252
+ }
1253
+ const content = `<?php
1254
+
1255
+ /**
1256
+ * \u26A0\uFE0F DO NOT EDIT THIS FILE! \u26A0\uFE0F
1257
+ * \u3053\u306E\u30D5\u30A1\u30A4\u30EB\u3092\u7DE8\u96C6\u3057\u306A\u3044\u3067\u304F\u3060\u3055\u3044\uFF01
1258
+ * KH\xD4NG \u0110\u01AF\u1EE2C S\u1EECA FILE N\xC0Y!
1259
+ *
1260
+ * This file is AUTO-GENERATED by Omnify.
1261
+ * Any manual changes will be OVERWRITTEN on next generation.
1262
+ *
1263
+ * This migration adds foreign key constraints for circular dependencies.
1264
+ * These constraints are deferred to run after all tables are created.
1265
+ *
1266
+ * @generated by @famgia/omnify-laravel
1267
+ */
1268
+
1269
+ use Illuminate\\Database\\Migrations\\Migration;
1270
+ use Illuminate\\Database\\Schema\\Blueprint;
1271
+ use Illuminate\\Support\\Facades\\Schema;
1272
+
1273
+ return new class extends Migration
1274
+ {${connection}
1275
+ /**
1276
+ * Run the migrations.
1277
+ */
1278
+ public function up(): void
1279
+ {
1280
+ ${upStatements.join("\n\n")}
1281
+ }
1282
+
1283
+ /**
1284
+ * Reverse the migrations.
1285
+ */
1286
+ public function down(): void
1287
+ {
1288
+ ${downStatements.join("\n\n")}
1289
+ }
1290
+ };
1291
+ `;
1292
+ const tables = [...byTable.keys()];
1293
+ return {
1294
+ fileName,
1295
+ className: "AddCircularForeignKeys",
1296
+ content,
1297
+ tables,
1298
+ type: "alter"
1299
+ };
1300
+ }
1160
1301
  function topologicalSort(schemas) {
1161
1302
  const schemaList = Object.values(schemas).filter((s) => s.kind !== "enum");
1162
1303
  const sorted = [];
@@ -1190,6 +1331,7 @@ function generateMigrations(schemas, options = {}) {
1190
1331
  const migrations = [];
1191
1332
  const pivotTablesGenerated = /* @__PURE__ */ new Set();
1192
1333
  let timestampOffset = 0;
1334
+ const circularFKs = detectCircularDependencies(schemas);
1193
1335
  const sortedSchemas = topologicalSort(schemas);
1194
1336
  const baseTimestamp = options.timestamp ?? generateTimestamp();
1195
1337
  for (const schema of sortedSchemas) {
@@ -1198,7 +1340,9 @@ function generateMigrations(schemas, options = {}) {
1198
1340
  const blueprint = schemaToBlueprint(schema, schemas, {
1199
1341
  customTypes: options.customTypes,
1200
1342
  pluginEnums: options.pluginEnums,
1201
- locale: options.locale
1343
+ locale: options.locale,
1344
+ excludeFKs: circularFKs,
1345
+ schemaName: schema.name
1202
1346
  });
1203
1347
  const migration = generateCreateMigration(blueprint, {
1204
1348
  ...options,
@@ -1238,6 +1382,19 @@ function generateMigrations(schemas, options = {}) {
1238
1382
  });
1239
1383
  }
1240
1384
  }
1385
+ if (circularFKs.size > 0) {
1386
+ const deferredFKs = extractDeferredFKs(schemas, circularFKs);
1387
+ if (deferredFKs.length > 0) {
1388
+ const offsetTimestamp = incrementTimestamp(baseTimestamp, timestampOffset);
1389
+ const deferredMigration = generateDeferredFKMigration(deferredFKs, {
1390
+ ...options,
1391
+ timestamp: offsetTimestamp
1392
+ });
1393
+ if (deferredMigration) {
1394
+ migrations.push(deferredMigration);
1395
+ }
1396
+ }
1397
+ }
1241
1398
  return migrations;
1242
1399
  }
1243
1400
  function incrementTimestamp(timestamp, seconds) {
@@ -1327,8 +1484,16 @@ function generateTimestamp2() {
1327
1484
  const seconds = String(now.getSeconds()).padStart(2, "0");
1328
1485
  return `${year}_${month}_${day}_${hours}${minutes}${seconds}`;
1329
1486
  }
1487
+ function shouldSkipAssociationColumn(prop) {
1488
+ if (prop.type !== "Association") return false;
1489
+ if (isAssociationWithFkColumn(prop)) return false;
1490
+ return true;
1491
+ }
1330
1492
  function formatAddColumn(columnName, prop) {
1331
1493
  const lines = [];
1494
+ if (shouldSkipAssociationColumn(prop)) {
1495
+ return lines;
1496
+ }
1332
1497
  if (isAssociationWithFkColumn(prop)) {
1333
1498
  const fkColumn = getAssociationFkColumnName(columnName);
1334
1499
  let code2 = `$table->unsignedBigInteger('${fkColumn}')`;
@@ -1368,6 +1533,9 @@ function formatAddColumn(columnName, prop) {
1368
1533
  }
1369
1534
  function formatDropColumn(columnName, prop) {
1370
1535
  const lines = [];
1536
+ if (prop && shouldSkipAssociationColumn(prop)) {
1537
+ return lines;
1538
+ }
1371
1539
  if (prop && isAssociationWithFkColumn(prop)) {
1372
1540
  const fkColumn = getAssociationFkColumnName(columnName);
1373
1541
  lines.push(`$table->dropForeign(['${fkColumn}']);`);
@@ -3017,9 +3185,26 @@ ${providerLine}
3017
3185
  }
3018
3186
 
3019
3187
  // src/factory/generator.ts
3188
+ function deriveFactoryNamespace(modelNamespace) {
3189
+ if (modelNamespace === "App\\Models") {
3190
+ return "Database\\Factories";
3191
+ }
3192
+ if (modelNamespace.endsWith("\\Models")) {
3193
+ const base = modelNamespace.slice(0, -7);
3194
+ return `${base}\\Database\\Factories`;
3195
+ }
3196
+ const parts = modelNamespace.split("\\");
3197
+ if (parts.length > 1 && parts[parts.length - 1] === "Models") {
3198
+ parts.pop();
3199
+ return [...parts, "Database", "Factories"].join("\\");
3200
+ }
3201
+ return "Database\\Factories";
3202
+ }
3020
3203
  function resolveOptions2(options) {
3204
+ const modelNamespace = options?.modelNamespace ?? "App\\Models";
3021
3205
  return {
3022
- modelNamespace: options?.modelNamespace ?? "App\\Models",
3206
+ modelNamespace,
3207
+ factoryNamespace: options?.factoryNamespace ?? deriveFactoryNamespace(modelNamespace),
3023
3208
  factoryPath: options?.factoryPath ?? "database/factories",
3024
3209
  fakerLocale: options?.fakerLocale ?? "en_US",
3025
3210
  customTypes: options?.customTypes ?? /* @__PURE__ */ new Map(),
@@ -3032,16 +3217,18 @@ function resolveSchemaOptions2(schema, globalOptions) {
3032
3217
  return globalOptions;
3033
3218
  }
3034
3219
  const base = pkgOutput.base;
3220
+ const modelNamespace = pkgOutput.modelsNamespace;
3035
3221
  return {
3036
3222
  ...globalOptions,
3037
- modelNamespace: pkgOutput.modelsNamespace,
3223
+ modelNamespace,
3224
+ factoryNamespace: pkgOutput.factoriesNamespace ?? deriveFactoryNamespace(modelNamespace),
3038
3225
  factoryPath: `${base}/${pkgOutput.factoriesPath ?? "database/factories"}`
3039
3226
  };
3040
3227
  }
3041
3228
  function getStubContent2() {
3042
3229
  return `<?php
3043
3230
 
3044
- namespace Database\\Factories;
3231
+ namespace {{FACTORY_NAMESPACE}};
3045
3232
 
3046
3233
  use {{MODEL_NAMESPACE}}\\{{MODEL_NAME}};
3047
3234
  use Illuminate\\Database\\Eloquent\\Factories\\Factory;
@@ -3338,6 +3525,7 @@ function generateFactory(schema, schemas, options, stubContent) {
3338
3525
  }
3339
3526
  }
3340
3527
  let content = stubContent;
3528
+ content = content.replace(/\{\{FACTORY_NAMESPACE\}\}/g, options.factoryNamespace);
3341
3529
  content = content.replace(/\{\{MODEL_NAMESPACE\}\}/g, options.modelNamespace);
3342
3530
  content = content.replace(/\{\{MODEL_NAME\}\}/g, modelName);
3343
3531
  const uniqueImports = [...new Set(imports)];
@@ -4958,9 +5146,10 @@ function laravelPlugin(options) {
4958
5146
  name: "laravel-migrations",
4959
5147
  description: "Generate Laravel migration files",
4960
5148
  generate: async (ctx) => {
5149
+ const baseTimestamp = resolved.timestamp ?? generateTimestamp();
4961
5150
  const migrationOptions = {
4962
5151
  connection: resolved.connection,
4963
- timestamp: resolved.timestamp,
5152
+ timestamp: baseTimestamp,
4964
5153
  customTypes: ctx.customTypes,
4965
5154
  pluginEnums: ctx.pluginEnums
4966
5155
  };
@@ -4982,6 +5171,7 @@ function laravelPlugin(options) {
4982
5171
  if (ctx.changes.length === 0) {
4983
5172
  return outputs;
4984
5173
  }
5174
+ let timestampOffset = 0;
4985
5175
  const addedSchemaNames = new Set(
4986
5176
  ctx.changes.filter((c) => c.changeType === "added").map((c) => c.schemaName)
4987
5177
  );
@@ -5006,15 +5196,17 @@ function laravelPlugin(options) {
5006
5196
  schemaName: migration.schemaName
5007
5197
  }
5008
5198
  });
5199
+ timestampOffset++;
5009
5200
  }
5010
5201
  }
5011
5202
  const alterChanges = ctx.changes.filter(
5012
5203
  (c) => c.changeType === "modified" || c.changeType === "removed"
5013
5204
  );
5014
5205
  if (alterChanges.length > 0) {
5206
+ const alterTimestamp = timestampOffset > 0 ? incrementTimestamp(baseTimestamp, timestampOffset) : baseTimestamp;
5015
5207
  const alterMigrations = generateMigrationsFromChanges(
5016
5208
  alterChanges,
5017
- migrationOptions
5209
+ { ...migrationOptions, timestamp: alterTimestamp }
5018
5210
  );
5019
5211
  for (const migration of alterMigrations) {
5020
5212
  outputs.push({
@@ -5259,4 +5451,4 @@ export {
5259
5451
  getFactoryPath,
5260
5452
  laravelPlugin
5261
5453
  };
5262
- //# sourceMappingURL=chunk-U3NDJ6S6.js.map
5454
+ //# sourceMappingURL=chunk-RSC5ATSV.js.map