@famgia/omnify-laravel 0.0.17 → 0.0.19
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/README.md +130 -0
- package/dist/{chunk-52JAFQBQ.js → chunk-ILSNN7UV.js} +403 -14
- package/dist/chunk-ILSNN7UV.js.map +1 -0
- package/dist/index.cjs +401 -12
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +12 -2
- package/dist/index.d.ts +12 -2
- package/dist/index.js +1 -1
- package/dist/plugin.cjs +682 -12
- package/dist/plugin.cjs.map +1 -1
- package/dist/plugin.js +1 -1
- package/package.json +2 -2
- package/dist/chunk-52JAFQBQ.js.map +0 -1
package/dist/plugin.cjs
CHANGED
|
@@ -39,6 +39,7 @@ var TYPE_METHOD_MAP = {
|
|
|
39
39
|
LongText: "longText",
|
|
40
40
|
Date: "date",
|
|
41
41
|
Time: "time",
|
|
42
|
+
DateTime: "dateTime",
|
|
42
43
|
Timestamp: "timestamp",
|
|
43
44
|
Json: "json",
|
|
44
45
|
Email: "string",
|
|
@@ -111,6 +112,10 @@ function propertyToColumnMethod(propertyName, property) {
|
|
|
111
112
|
if (baseProp.unsigned && (method === "integer" || method === "bigInteger")) {
|
|
112
113
|
modifiers.push({ method: "unsigned" });
|
|
113
114
|
}
|
|
115
|
+
const displayName = property.displayName;
|
|
116
|
+
if (displayName) {
|
|
117
|
+
modifiers.push({ method: "comment", args: [displayName] });
|
|
118
|
+
}
|
|
114
119
|
return {
|
|
115
120
|
name: columnName,
|
|
116
121
|
method,
|
|
@@ -245,6 +250,9 @@ function generateForeignKey(propertyName, property, allSchemas) {
|
|
|
245
250
|
if (assocProp.default !== void 0 && assocProp.default !== null) {
|
|
246
251
|
modifiers.push({ method: "default", args: [assocProp.default] });
|
|
247
252
|
}
|
|
253
|
+
if (assocProp.displayName) {
|
|
254
|
+
modifiers.push({ method: "comment", args: [assocProp.displayName] });
|
|
255
|
+
}
|
|
248
256
|
const column = {
|
|
249
257
|
name: columnName,
|
|
250
258
|
method,
|
|
@@ -264,7 +272,65 @@ function generateForeignKey(propertyName, property, allSchemas) {
|
|
|
264
272
|
};
|
|
265
273
|
return { column, foreignKey, index };
|
|
266
274
|
}
|
|
267
|
-
function
|
|
275
|
+
function expandCompoundType(propName, property, customTypes) {
|
|
276
|
+
const typeDef = customTypes.get(property.type);
|
|
277
|
+
if (!typeDef || !typeDef.compound || !typeDef.expand) {
|
|
278
|
+
return null;
|
|
279
|
+
}
|
|
280
|
+
const expanded = [];
|
|
281
|
+
const baseProp = property;
|
|
282
|
+
for (const field of typeDef.expand) {
|
|
283
|
+
const suffixSnake = toColumnName(field.suffix);
|
|
284
|
+
const columnName = `${propName}_${suffixSnake}`;
|
|
285
|
+
const expandedProp = {
|
|
286
|
+
type: "String"
|
|
287
|
+
// Default type, will be overridden by sql definition
|
|
288
|
+
};
|
|
289
|
+
if (field.sql) {
|
|
290
|
+
const sqlType = field.sql.sqlType.toUpperCase();
|
|
291
|
+
if (sqlType === "VARCHAR" || sqlType === "CHAR" || sqlType === "STRING") {
|
|
292
|
+
expandedProp.type = "String";
|
|
293
|
+
if (field.sql.length) {
|
|
294
|
+
expandedProp.length = field.sql.length;
|
|
295
|
+
}
|
|
296
|
+
} else if (sqlType === "INT" || sqlType === "INTEGER") {
|
|
297
|
+
expandedProp.type = "Int";
|
|
298
|
+
} else if (sqlType === "BIGINT") {
|
|
299
|
+
expandedProp.type = "BigInt";
|
|
300
|
+
} else if (sqlType === "TEXT") {
|
|
301
|
+
expandedProp.type = "Text";
|
|
302
|
+
} else if (sqlType === "BOOLEAN" || sqlType === "BOOL") {
|
|
303
|
+
expandedProp.type = "Boolean";
|
|
304
|
+
} else if (sqlType === "DECIMAL") {
|
|
305
|
+
expandedProp.type = "Decimal";
|
|
306
|
+
if (field.sql.precision) expandedProp.precision = field.sql.precision;
|
|
307
|
+
if (field.sql.scale) expandedProp.scale = field.sql.scale;
|
|
308
|
+
} else if (sqlType === "DATE") {
|
|
309
|
+
expandedProp.type = "Date";
|
|
310
|
+
} else if (sqlType === "TIMESTAMP" || sqlType === "DATETIME") {
|
|
311
|
+
expandedProp.type = "Timestamp";
|
|
312
|
+
}
|
|
313
|
+
if (field.sql.nullable !== void 0) {
|
|
314
|
+
expandedProp.nullable = field.sql.nullable;
|
|
315
|
+
} else if (baseProp.nullable !== void 0) {
|
|
316
|
+
expandedProp.nullable = baseProp.nullable;
|
|
317
|
+
}
|
|
318
|
+
if (field.sql.default !== void 0) {
|
|
319
|
+
expandedProp.default = field.sql.default;
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
if (baseProp.displayName) {
|
|
323
|
+
expandedProp.displayName = `${baseProp.displayName} (${field.suffix})`;
|
|
324
|
+
}
|
|
325
|
+
expanded.push({
|
|
326
|
+
name: columnName,
|
|
327
|
+
property: expandedProp
|
|
328
|
+
});
|
|
329
|
+
}
|
|
330
|
+
return expanded;
|
|
331
|
+
}
|
|
332
|
+
function schemaToBlueprint(schema, allSchemas, options = {}) {
|
|
333
|
+
const { customTypes = /* @__PURE__ */ new Map() } = options;
|
|
268
334
|
const tableName = toTableName(schema.name);
|
|
269
335
|
const columns = [];
|
|
270
336
|
const foreignKeys = [];
|
|
@@ -275,6 +341,16 @@ function schemaToBlueprint(schema, allSchemas) {
|
|
|
275
341
|
}
|
|
276
342
|
if (schema.properties) {
|
|
277
343
|
for (const [propName, property] of Object.entries(schema.properties)) {
|
|
344
|
+
const expandedProps = expandCompoundType(propName, property, customTypes);
|
|
345
|
+
if (expandedProps) {
|
|
346
|
+
for (const { name: expandedName, property: expandedProp } of expandedProps) {
|
|
347
|
+
const columnMethod2 = propertyToColumnMethod(expandedName, expandedProp);
|
|
348
|
+
if (columnMethod2) {
|
|
349
|
+
columns.push(columnMethod2);
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
continue;
|
|
353
|
+
}
|
|
278
354
|
const columnMethod = propertyToColumnMethod(propName, property);
|
|
279
355
|
if (columnMethod) {
|
|
280
356
|
columns.push(columnMethod);
|
|
@@ -681,7 +757,9 @@ function generateMigrations(schemas, options = {}) {
|
|
|
681
757
|
const timestamp = options.timestamp ?? generateTimestamp();
|
|
682
758
|
const offsetTimestamp = incrementTimestamp(timestamp, timestampOffset);
|
|
683
759
|
timestampOffset++;
|
|
684
|
-
const blueprint = schemaToBlueprint(schema, schemas
|
|
760
|
+
const blueprint = schemaToBlueprint(schema, schemas, {
|
|
761
|
+
customTypes: options.customTypes
|
|
762
|
+
});
|
|
685
763
|
const migration = generateCreateMigration(blueprint, {
|
|
686
764
|
...options,
|
|
687
765
|
timestamp: offsetTimestamp
|
|
@@ -737,6 +815,291 @@ function getMigrationPath(migration, outputDir = "database/migrations") {
|
|
|
737
815
|
return `${outputDir}/${migration.fileName}`;
|
|
738
816
|
}
|
|
739
817
|
|
|
818
|
+
// src/migration/alter-generator.ts
|
|
819
|
+
var TYPE_METHOD_MAP2 = {
|
|
820
|
+
String: "string",
|
|
821
|
+
Int: "integer",
|
|
822
|
+
BigInt: "bigInteger",
|
|
823
|
+
Float: "double",
|
|
824
|
+
Decimal: "decimal",
|
|
825
|
+
Boolean: "boolean",
|
|
826
|
+
Text: "text",
|
|
827
|
+
LongText: "longText",
|
|
828
|
+
Date: "date",
|
|
829
|
+
Time: "time",
|
|
830
|
+
DateTime: "dateTime",
|
|
831
|
+
Timestamp: "timestamp",
|
|
832
|
+
Json: "json",
|
|
833
|
+
Email: "string",
|
|
834
|
+
Password: "string",
|
|
835
|
+
File: "string",
|
|
836
|
+
MultiFile: "json",
|
|
837
|
+
Enum: "enum",
|
|
838
|
+
Select: "string",
|
|
839
|
+
Lookup: "unsignedBigInteger"
|
|
840
|
+
};
|
|
841
|
+
function generateTimestamp2() {
|
|
842
|
+
const now = /* @__PURE__ */ new Date();
|
|
843
|
+
const year = now.getFullYear();
|
|
844
|
+
const month = String(now.getMonth() + 1).padStart(2, "0");
|
|
845
|
+
const day = String(now.getDate()).padStart(2, "0");
|
|
846
|
+
const hours = String(now.getHours()).padStart(2, "0");
|
|
847
|
+
const minutes = String(now.getMinutes()).padStart(2, "0");
|
|
848
|
+
const seconds = String(now.getSeconds()).padStart(2, "0");
|
|
849
|
+
return `${year}_${month}_${day}_${hours}${minutes}${seconds}`;
|
|
850
|
+
}
|
|
851
|
+
function formatAddColumn(columnName, prop) {
|
|
852
|
+
const snakeColumn = toColumnName(columnName);
|
|
853
|
+
const method = TYPE_METHOD_MAP2[prop.type] ?? "string";
|
|
854
|
+
let code;
|
|
855
|
+
if (prop.type === "Decimal") {
|
|
856
|
+
const precision = prop.precision ?? 8;
|
|
857
|
+
const scale = prop.scale ?? 2;
|
|
858
|
+
code = `$table->${method}('${snakeColumn}', ${precision}, ${scale})`;
|
|
859
|
+
} else {
|
|
860
|
+
code = `$table->${method}('${snakeColumn}')`;
|
|
861
|
+
}
|
|
862
|
+
if (prop.nullable) code += "->nullable()";
|
|
863
|
+
if (prop.unique) code += "->unique()";
|
|
864
|
+
if (prop.default !== void 0) {
|
|
865
|
+
const defaultValue = typeof prop.default === "string" ? `'${prop.default}'` : JSON.stringify(prop.default);
|
|
866
|
+
code += `->default(${defaultValue})`;
|
|
867
|
+
}
|
|
868
|
+
return code + ";";
|
|
869
|
+
}
|
|
870
|
+
function formatDropColumn(columnName) {
|
|
871
|
+
const snakeColumn = toColumnName(columnName);
|
|
872
|
+
return `$table->dropColumn('${snakeColumn}');`;
|
|
873
|
+
}
|
|
874
|
+
function formatRenameColumn(oldName, newName) {
|
|
875
|
+
const oldSnake = toColumnName(oldName);
|
|
876
|
+
const newSnake = toColumnName(newName);
|
|
877
|
+
return `$table->renameColumn('${oldSnake}', '${newSnake}');`;
|
|
878
|
+
}
|
|
879
|
+
function formatModifyColumn(columnName, _prevProp, currProp) {
|
|
880
|
+
const snakeColumn = toColumnName(columnName);
|
|
881
|
+
const method = TYPE_METHOD_MAP2[currProp.type] ?? "string";
|
|
882
|
+
let code;
|
|
883
|
+
if (currProp.type === "Decimal") {
|
|
884
|
+
const precision = currProp.precision ?? 8;
|
|
885
|
+
const scale = currProp.scale ?? 2;
|
|
886
|
+
code = `$table->${method}('${snakeColumn}', ${precision}, ${scale})`;
|
|
887
|
+
} else {
|
|
888
|
+
code = `$table->${method}('${snakeColumn}')`;
|
|
889
|
+
}
|
|
890
|
+
if (currProp.nullable) code += "->nullable()";
|
|
891
|
+
if (currProp.unique) code += "->unique()";
|
|
892
|
+
if (currProp.default !== void 0) {
|
|
893
|
+
const defaultValue = typeof currProp.default === "string" ? `'${currProp.default}'` : JSON.stringify(currProp.default);
|
|
894
|
+
code += `->default(${defaultValue})`;
|
|
895
|
+
}
|
|
896
|
+
return code + "->change();";
|
|
897
|
+
}
|
|
898
|
+
function formatAddIndex(columns, unique) {
|
|
899
|
+
const snakeColumns = columns.map(toColumnName);
|
|
900
|
+
const method = unique ? "unique" : "index";
|
|
901
|
+
const colsArg = snakeColumns.length === 1 ? `'${snakeColumns[0]}'` : `[${snakeColumns.map((c) => `'${c}'`).join(", ")}]`;
|
|
902
|
+
return `$table->${method}(${colsArg});`;
|
|
903
|
+
}
|
|
904
|
+
function formatDropIndex(tableName, columns, unique) {
|
|
905
|
+
const snakeColumns = columns.map(toColumnName);
|
|
906
|
+
const method = unique ? "dropUnique" : "dropIndex";
|
|
907
|
+
const suffix = unique ? "unique" : "index";
|
|
908
|
+
const indexName = `${tableName}_${snakeColumns.join("_")}_${suffix}`;
|
|
909
|
+
return `$table->${method}('${indexName}');`;
|
|
910
|
+
}
|
|
911
|
+
function generateAlterMigrationContent(tableName, change, options = {}) {
|
|
912
|
+
const upLines = [];
|
|
913
|
+
const downLines = [];
|
|
914
|
+
if (change.columnChanges) {
|
|
915
|
+
for (const col of change.columnChanges) {
|
|
916
|
+
if (col.changeType === "added" && col.currentDef) {
|
|
917
|
+
upLines.push(` ${formatAddColumn(col.column, col.currentDef)}`);
|
|
918
|
+
downLines.push(` ${formatDropColumn(col.column)}`);
|
|
919
|
+
} else if (col.changeType === "removed" && col.previousDef) {
|
|
920
|
+
upLines.push(` ${formatDropColumn(col.column)}`);
|
|
921
|
+
downLines.push(` ${formatAddColumn(col.column, col.previousDef)}`);
|
|
922
|
+
} else if (col.changeType === "modified" && col.previousDef && col.currentDef) {
|
|
923
|
+
upLines.push(` ${formatModifyColumn(col.column, col.previousDef, col.currentDef)}`);
|
|
924
|
+
downLines.push(` ${formatModifyColumn(col.column, col.currentDef, col.previousDef)}`);
|
|
925
|
+
} else if (col.changeType === "renamed" && col.previousColumn) {
|
|
926
|
+
upLines.push(` ${formatRenameColumn(col.previousColumn, col.column)}`);
|
|
927
|
+
downLines.push(` ${formatRenameColumn(col.column, col.previousColumn)}`);
|
|
928
|
+
if (col.modifications && col.modifications.length > 0 && col.previousDef && col.currentDef) {
|
|
929
|
+
upLines.push(` ${formatModifyColumn(col.column, col.previousDef, col.currentDef)}`);
|
|
930
|
+
downLines.push(` ${formatModifyColumn(col.column, col.currentDef, col.previousDef)}`);
|
|
931
|
+
}
|
|
932
|
+
}
|
|
933
|
+
}
|
|
934
|
+
}
|
|
935
|
+
if (change.indexChanges) {
|
|
936
|
+
for (const idx of change.indexChanges) {
|
|
937
|
+
if (idx.changeType === "added") {
|
|
938
|
+
upLines.push(` ${formatAddIndex(idx.index.columns, idx.index.unique)}`);
|
|
939
|
+
downLines.push(` ${formatDropIndex(tableName, idx.index.columns, idx.index.unique)}`);
|
|
940
|
+
} else {
|
|
941
|
+
upLines.push(` ${formatDropIndex(tableName, idx.index.columns, idx.index.unique)}`);
|
|
942
|
+
downLines.push(` ${formatAddIndex(idx.index.columns, idx.index.unique)}`);
|
|
943
|
+
}
|
|
944
|
+
}
|
|
945
|
+
}
|
|
946
|
+
if (change.optionChanges) {
|
|
947
|
+
if (change.optionChanges.timestamps) {
|
|
948
|
+
const { from, to } = change.optionChanges.timestamps;
|
|
949
|
+
if (to && !from) {
|
|
950
|
+
upLines.push(` $table->timestamps();`);
|
|
951
|
+
downLines.push(` $table->dropTimestamps();`);
|
|
952
|
+
} else if (from && !to) {
|
|
953
|
+
upLines.push(` $table->dropTimestamps();`);
|
|
954
|
+
downLines.push(` $table->timestamps();`);
|
|
955
|
+
}
|
|
956
|
+
}
|
|
957
|
+
if (change.optionChanges.softDelete) {
|
|
958
|
+
const { from, to } = change.optionChanges.softDelete;
|
|
959
|
+
if (to && !from) {
|
|
960
|
+
upLines.push(` $table->softDeletes();`);
|
|
961
|
+
downLines.push(` $table->dropSoftDeletes();`);
|
|
962
|
+
} else if (from && !to) {
|
|
963
|
+
upLines.push(` $table->dropSoftDeletes();`);
|
|
964
|
+
downLines.push(` $table->softDeletes();`);
|
|
965
|
+
}
|
|
966
|
+
}
|
|
967
|
+
}
|
|
968
|
+
const connection = options.connection ? `
|
|
969
|
+
protected $connection = '${options.connection}';
|
|
970
|
+
` : "";
|
|
971
|
+
return `<?php
|
|
972
|
+
|
|
973
|
+
use Illuminate\\Database\\Migrations\\Migration;
|
|
974
|
+
use Illuminate\\Database\\Schema\\Blueprint;
|
|
975
|
+
use Illuminate\\Support\\Facades\\Schema;
|
|
976
|
+
|
|
977
|
+
return new class extends Migration
|
|
978
|
+
{${connection}
|
|
979
|
+
/**
|
|
980
|
+
* Run the migrations.
|
|
981
|
+
*/
|
|
982
|
+
public function up(): void
|
|
983
|
+
{
|
|
984
|
+
Schema::table('${tableName}', function (Blueprint $table) {
|
|
985
|
+
${upLines.join("\n")}
|
|
986
|
+
});
|
|
987
|
+
}
|
|
988
|
+
|
|
989
|
+
/**
|
|
990
|
+
* Reverse the migrations.
|
|
991
|
+
*/
|
|
992
|
+
public function down(): void
|
|
993
|
+
{
|
|
994
|
+
Schema::table('${tableName}', function (Blueprint $table) {
|
|
995
|
+
${downLines.join("\n")}
|
|
996
|
+
});
|
|
997
|
+
}
|
|
998
|
+
};
|
|
999
|
+
`;
|
|
1000
|
+
}
|
|
1001
|
+
function generateAlterMigration(change, options = {}) {
|
|
1002
|
+
if (change.changeType !== "modified") {
|
|
1003
|
+
return null;
|
|
1004
|
+
}
|
|
1005
|
+
const hasChanges = change.columnChanges && change.columnChanges.length > 0 || change.indexChanges && change.indexChanges.length > 0 || change.optionChanges && (change.optionChanges.timestamps || change.optionChanges.softDelete);
|
|
1006
|
+
if (!hasChanges) {
|
|
1007
|
+
return null;
|
|
1008
|
+
}
|
|
1009
|
+
const tableName = toTableName(change.schemaName);
|
|
1010
|
+
const timestamp = options.timestamp ?? generateTimestamp2();
|
|
1011
|
+
const fileName = `${timestamp}_update_${tableName}_table.php`;
|
|
1012
|
+
const content = generateAlterMigrationContent(tableName, change, options);
|
|
1013
|
+
return {
|
|
1014
|
+
fileName,
|
|
1015
|
+
className: `Update${change.schemaName}Table`,
|
|
1016
|
+
content,
|
|
1017
|
+
tables: [tableName],
|
|
1018
|
+
type: "alter"
|
|
1019
|
+
};
|
|
1020
|
+
}
|
|
1021
|
+
function generateDropTableMigration(schemaName, options = {}) {
|
|
1022
|
+
const tableName = toTableName(schemaName);
|
|
1023
|
+
const timestamp = options.timestamp ?? generateTimestamp2();
|
|
1024
|
+
const fileName = `${timestamp}_drop_${tableName}_table.php`;
|
|
1025
|
+
const connection = options.connection ? `
|
|
1026
|
+
protected $connection = '${options.connection}';
|
|
1027
|
+
` : "";
|
|
1028
|
+
const content = `<?php
|
|
1029
|
+
|
|
1030
|
+
use Illuminate\\Database\\Migrations\\Migration;
|
|
1031
|
+
use Illuminate\\Database\\Schema\\Blueprint;
|
|
1032
|
+
use Illuminate\\Support\\Facades\\Schema;
|
|
1033
|
+
|
|
1034
|
+
return new class extends Migration
|
|
1035
|
+
{${connection}
|
|
1036
|
+
/**
|
|
1037
|
+
* Run the migrations.
|
|
1038
|
+
*/
|
|
1039
|
+
public function up(): void
|
|
1040
|
+
{
|
|
1041
|
+
Schema::dropIfExists('${tableName}');
|
|
1042
|
+
}
|
|
1043
|
+
|
|
1044
|
+
/**
|
|
1045
|
+
* Reverse the migrations.
|
|
1046
|
+
*/
|
|
1047
|
+
public function down(): void
|
|
1048
|
+
{
|
|
1049
|
+
// Cannot recreate table without full schema
|
|
1050
|
+
// Consider restoring from backup if needed
|
|
1051
|
+
}
|
|
1052
|
+
};
|
|
1053
|
+
`;
|
|
1054
|
+
return {
|
|
1055
|
+
fileName,
|
|
1056
|
+
className: `Drop${schemaName}Table`,
|
|
1057
|
+
content,
|
|
1058
|
+
tables: [tableName],
|
|
1059
|
+
type: "drop"
|
|
1060
|
+
};
|
|
1061
|
+
}
|
|
1062
|
+
function generateMigrationsFromChanges(changes, options = {}) {
|
|
1063
|
+
const migrations = [];
|
|
1064
|
+
let timestampOffset = 0;
|
|
1065
|
+
const getNextTimestamp = () => {
|
|
1066
|
+
const ts = options.timestamp ?? generateTimestamp2();
|
|
1067
|
+
const offset = timestampOffset++;
|
|
1068
|
+
if (offset === 0) return ts;
|
|
1069
|
+
const parts = ts.split("_");
|
|
1070
|
+
if (parts.length >= 4) {
|
|
1071
|
+
const timePart = parts[3] ?? "000000";
|
|
1072
|
+
const secs = parseInt(timePart.substring(4, 6), 10) + offset;
|
|
1073
|
+
const newSecs = String(secs % 60).padStart(2, "0");
|
|
1074
|
+
parts[3] = timePart.substring(0, 4) + newSecs;
|
|
1075
|
+
return parts.join("_");
|
|
1076
|
+
}
|
|
1077
|
+
return ts;
|
|
1078
|
+
};
|
|
1079
|
+
for (const change of changes) {
|
|
1080
|
+
if (change.changeType === "modified") {
|
|
1081
|
+
const migration = generateAlterMigration(change, {
|
|
1082
|
+
...options,
|
|
1083
|
+
timestamp: getNextTimestamp()
|
|
1084
|
+
});
|
|
1085
|
+
if (migration) {
|
|
1086
|
+
migrations.push(migration);
|
|
1087
|
+
}
|
|
1088
|
+
} else if (change.changeType === "removed") {
|
|
1089
|
+
migrations.push(
|
|
1090
|
+
generateDropTableMigration(change.schemaName, {
|
|
1091
|
+
...options,
|
|
1092
|
+
timestamp: getNextTimestamp()
|
|
1093
|
+
})
|
|
1094
|
+
);
|
|
1095
|
+
}
|
|
1096
|
+
}
|
|
1097
|
+
return migrations;
|
|
1098
|
+
}
|
|
1099
|
+
|
|
1100
|
+
// src/model/generator.ts
|
|
1101
|
+
var import_omnify_types = require("@famgia/omnify-types");
|
|
1102
|
+
|
|
740
1103
|
// src/utils.ts
|
|
741
1104
|
function toSnakeCase(str) {
|
|
742
1105
|
return str.replace(/([A-Z])/g, "_$1").replace(/^_/, "").toLowerCase();
|
|
@@ -766,6 +1129,45 @@ var DEFAULT_OPTIONS = {
|
|
|
766
1129
|
baseModelPath: "app/Models/OmnifyBase",
|
|
767
1130
|
modelPath: "app/Models"
|
|
768
1131
|
};
|
|
1132
|
+
function generateLocalizedDisplayNames(displayName, indent = " ") {
|
|
1133
|
+
if (displayName === void 0) {
|
|
1134
|
+
return "";
|
|
1135
|
+
}
|
|
1136
|
+
if (typeof displayName === "string") {
|
|
1137
|
+
return `${indent}'en' => '${escapePhpString(displayName)}',`;
|
|
1138
|
+
}
|
|
1139
|
+
if ((0, import_omnify_types.isLocaleMap)(displayName)) {
|
|
1140
|
+
const entries = Object.entries(displayName).map(([locale, value]) => `${indent}'${locale}' => '${escapePhpString(value)}',`).join("\n");
|
|
1141
|
+
return entries;
|
|
1142
|
+
}
|
|
1143
|
+
return "";
|
|
1144
|
+
}
|
|
1145
|
+
function generatePropertyLocalizedDisplayNames(schema, indent = " ") {
|
|
1146
|
+
const properties = schema.properties ?? {};
|
|
1147
|
+
const entries = [];
|
|
1148
|
+
for (const [propName, propDef] of Object.entries(properties)) {
|
|
1149
|
+
const snakeName = toSnakeCase(propName);
|
|
1150
|
+
const displayName = propDef.displayName;
|
|
1151
|
+
if (displayName === void 0) {
|
|
1152
|
+
continue;
|
|
1153
|
+
}
|
|
1154
|
+
const innerIndent = indent + " ";
|
|
1155
|
+
if (typeof displayName === "string") {
|
|
1156
|
+
entries.push(`${indent}'${snakeName}' => [
|
|
1157
|
+
${innerIndent}'en' => '${escapePhpString(displayName)}',
|
|
1158
|
+
${indent}],`);
|
|
1159
|
+
} else if ((0, import_omnify_types.isLocaleMap)(displayName)) {
|
|
1160
|
+
const localeEntries = Object.entries(displayName).map(([locale, value]) => `${innerIndent}'${locale}' => '${escapePhpString(value)}',`).join("\n");
|
|
1161
|
+
entries.push(`${indent}'${snakeName}' => [
|
|
1162
|
+
${localeEntries}
|
|
1163
|
+
${indent}],`);
|
|
1164
|
+
}
|
|
1165
|
+
}
|
|
1166
|
+
return entries.join("\n");
|
|
1167
|
+
}
|
|
1168
|
+
function escapePhpString(str) {
|
|
1169
|
+
return str.replace(/\\/g, "\\\\").replace(/'/g, "\\'");
|
|
1170
|
+
}
|
|
769
1171
|
function resolveOptions(options) {
|
|
770
1172
|
return {
|
|
771
1173
|
baseModelNamespace: options?.baseModelNamespace ?? DEFAULT_OPTIONS.baseModelNamespace,
|
|
@@ -1148,11 +1550,14 @@ use Illuminate\\Database\\Eloquent\\Relations\\MorphOne;
|
|
|
1148
1550
|
use Illuminate\\Database\\Eloquent\\Relations\\MorphMany;
|
|
1149
1551
|
use Illuminate\\Database\\Eloquent\\Relations\\MorphToMany;
|
|
1150
1552
|
use Illuminate\\Database\\Eloquent\\Collection as EloquentCollection;
|
|
1553
|
+
use {{BASE_MODEL_NAMESPACE}}\\Traits\\HasLocalizedDisplayName;
|
|
1554
|
+
use {{BASE_MODEL_NAMESPACE}}\\Locales\\{{CLASS_NAME}}Locales;
|
|
1151
1555
|
{{IMPORTS}}
|
|
1152
1556
|
|
|
1153
1557
|
{{DOC_COMMENT}}
|
|
1154
1558
|
class {{CLASS_NAME}}BaseModel extends {{BASE_MODEL_CLASS}}
|
|
1155
1559
|
{
|
|
1560
|
+
use HasLocalizedDisplayName;
|
|
1156
1561
|
{{TRAITS}}
|
|
1157
1562
|
/**
|
|
1158
1563
|
* The table associated with the model.
|
|
@@ -1171,6 +1576,20 @@ class {{CLASS_NAME}}BaseModel extends {{BASE_MODEL_CLASS}}
|
|
|
1171
1576
|
*/
|
|
1172
1577
|
public $timestamps = {{TIMESTAMPS}};
|
|
1173
1578
|
|
|
1579
|
+
/**
|
|
1580
|
+
* Localized display names for this model.
|
|
1581
|
+
*
|
|
1582
|
+
* @var array<string, string>
|
|
1583
|
+
*/
|
|
1584
|
+
protected static array $localizedDisplayNames = {{CLASS_NAME}}Locales::DISPLAY_NAMES;
|
|
1585
|
+
|
|
1586
|
+
/**
|
|
1587
|
+
* Localized display names for properties.
|
|
1588
|
+
*
|
|
1589
|
+
* @var array<string, array<string, string>>
|
|
1590
|
+
*/
|
|
1591
|
+
protected static array $localizedPropertyDisplayNames = {{CLASS_NAME}}Locales::PROPERTY_DISPLAY_NAMES;
|
|
1592
|
+
|
|
1174
1593
|
/**
|
|
1175
1594
|
* The attributes that are mass assignable.
|
|
1176
1595
|
*/
|
|
@@ -1227,12 +1646,15 @@ use Illuminate\\Database\\Eloquent\\Relations\\MorphMany;
|
|
|
1227
1646
|
use Illuminate\\Database\\Eloquent\\Relations\\MorphToMany;
|
|
1228
1647
|
use Illuminate\\Database\\Eloquent\\Collection as EloquentCollection;
|
|
1229
1648
|
use Illuminate\\Notifications\\Notifiable;
|
|
1649
|
+
use {{BASE_MODEL_NAMESPACE}}\\Traits\\HasLocalizedDisplayName;
|
|
1650
|
+
use {{BASE_MODEL_NAMESPACE}}\\Locales\\{{CLASS_NAME}}Locales;
|
|
1230
1651
|
{{IMPORTS}}
|
|
1231
1652
|
|
|
1232
1653
|
{{DOC_COMMENT}}
|
|
1233
1654
|
class {{CLASS_NAME}}BaseModel extends Authenticatable
|
|
1234
1655
|
{
|
|
1235
1656
|
use Notifiable;
|
|
1657
|
+
use HasLocalizedDisplayName;
|
|
1236
1658
|
{{TRAITS}}
|
|
1237
1659
|
/**
|
|
1238
1660
|
* The table associated with the model.
|
|
@@ -1251,6 +1673,20 @@ class {{CLASS_NAME}}BaseModel extends Authenticatable
|
|
|
1251
1673
|
*/
|
|
1252
1674
|
public $timestamps = {{TIMESTAMPS}};
|
|
1253
1675
|
|
|
1676
|
+
/**
|
|
1677
|
+
* Localized display names for this model.
|
|
1678
|
+
*
|
|
1679
|
+
* @var array<string, string>
|
|
1680
|
+
*/
|
|
1681
|
+
protected static array $localizedDisplayNames = {{CLASS_NAME}}Locales::DISPLAY_NAMES;
|
|
1682
|
+
|
|
1683
|
+
/**
|
|
1684
|
+
* Localized display names for properties.
|
|
1685
|
+
*
|
|
1686
|
+
* @var array<string, array<string, string>>
|
|
1687
|
+
*/
|
|
1688
|
+
protected static array $localizedPropertyDisplayNames = {{CLASS_NAME}}Locales::PROPERTY_DISPLAY_NAMES;
|
|
1689
|
+
|
|
1254
1690
|
/**
|
|
1255
1691
|
* The attributes that are mass assignable.
|
|
1256
1692
|
*/
|
|
@@ -1355,6 +1791,132 @@ class OmnifyServiceProvider extends ServiceProvider
|
|
|
1355
1791
|
]);
|
|
1356
1792
|
}
|
|
1357
1793
|
}
|
|
1794
|
+
`,
|
|
1795
|
+
"has-localized-display-name": `<?php
|
|
1796
|
+
|
|
1797
|
+
namespace {{BASE_MODEL_NAMESPACE}}\\Traits;
|
|
1798
|
+
|
|
1799
|
+
/**
|
|
1800
|
+
* Trait for localized display names.
|
|
1801
|
+
* Uses Laravel's app()->getLocale() for locale resolution.
|
|
1802
|
+
*
|
|
1803
|
+
* DO NOT EDIT - This file is auto-generated by Omnify.
|
|
1804
|
+
* Any changes will be overwritten on next generation.
|
|
1805
|
+
*
|
|
1806
|
+
* @generated by @famgia/omnify-laravel
|
|
1807
|
+
*/
|
|
1808
|
+
trait HasLocalizedDisplayName
|
|
1809
|
+
{
|
|
1810
|
+
/**
|
|
1811
|
+
* Get the localized display name for this model.
|
|
1812
|
+
*
|
|
1813
|
+
* @param string|null $locale Locale code (defaults to app locale)
|
|
1814
|
+
* @return string
|
|
1815
|
+
*/
|
|
1816
|
+
public static function displayName(?string $locale = null): string
|
|
1817
|
+
{
|
|
1818
|
+
$locale = $locale ?? app()->getLocale();
|
|
1819
|
+
$displayNames = static::$localizedDisplayNames ?? [];
|
|
1820
|
+
|
|
1821
|
+
return $displayNames[$locale]
|
|
1822
|
+
?? $displayNames[config('app.fallback_locale', 'en')]
|
|
1823
|
+
?? $displayNames[array_key_first($displayNames) ?? 'en']
|
|
1824
|
+
?? class_basename(static::class);
|
|
1825
|
+
}
|
|
1826
|
+
|
|
1827
|
+
/**
|
|
1828
|
+
* Get all localized display names for this model.
|
|
1829
|
+
*
|
|
1830
|
+
* @return array<string, string>
|
|
1831
|
+
*/
|
|
1832
|
+
public static function allDisplayNames(): array
|
|
1833
|
+
{
|
|
1834
|
+
return static::$localizedDisplayNames ?? [];
|
|
1835
|
+
}
|
|
1836
|
+
|
|
1837
|
+
/**
|
|
1838
|
+
* Get the localized display name for a property.
|
|
1839
|
+
*
|
|
1840
|
+
* @param string $property Property name
|
|
1841
|
+
* @param string|null $locale Locale code (defaults to app locale)
|
|
1842
|
+
* @return string
|
|
1843
|
+
*/
|
|
1844
|
+
public static function propertyDisplayName(string $property, ?string $locale = null): string
|
|
1845
|
+
{
|
|
1846
|
+
$locale = $locale ?? app()->getLocale();
|
|
1847
|
+
$displayNames = static::$localizedPropertyDisplayNames[$property] ?? [];
|
|
1848
|
+
|
|
1849
|
+
return $displayNames[$locale]
|
|
1850
|
+
?? $displayNames[config('app.fallback_locale', 'en')]
|
|
1851
|
+
?? $displayNames[array_key_first($displayNames) ?? 'en']
|
|
1852
|
+
?? $property;
|
|
1853
|
+
}
|
|
1854
|
+
|
|
1855
|
+
/**
|
|
1856
|
+
* Get all localized display names for a property.
|
|
1857
|
+
*
|
|
1858
|
+
* @param string $property Property name
|
|
1859
|
+
* @return array<string, string>
|
|
1860
|
+
*/
|
|
1861
|
+
public static function allPropertyDisplayNames(string $property): array
|
|
1862
|
+
{
|
|
1863
|
+
return static::$localizedPropertyDisplayNames[$property] ?? [];
|
|
1864
|
+
}
|
|
1865
|
+
|
|
1866
|
+
/**
|
|
1867
|
+
* Get all property display names for a given locale.
|
|
1868
|
+
*
|
|
1869
|
+
* @param string|null $locale Locale code (defaults to app locale)
|
|
1870
|
+
* @return array<string, string>
|
|
1871
|
+
*/
|
|
1872
|
+
public static function allPropertyDisplayNamesForLocale(?string $locale = null): array
|
|
1873
|
+
{
|
|
1874
|
+
$locale = $locale ?? app()->getLocale();
|
|
1875
|
+
$result = [];
|
|
1876
|
+
|
|
1877
|
+
foreach (static::$localizedPropertyDisplayNames ?? [] as $property => $displayNames) {
|
|
1878
|
+
$result[$property] = $displayNames[$locale]
|
|
1879
|
+
?? $displayNames[config('app.fallback_locale', 'en')]
|
|
1880
|
+
?? $displayNames[array_key_first($displayNames) ?? 'en']
|
|
1881
|
+
?? $property;
|
|
1882
|
+
}
|
|
1883
|
+
|
|
1884
|
+
return $result;
|
|
1885
|
+
}
|
|
1886
|
+
}
|
|
1887
|
+
`,
|
|
1888
|
+
"locales": `<?php
|
|
1889
|
+
|
|
1890
|
+
namespace {{BASE_MODEL_NAMESPACE}}\\Locales;
|
|
1891
|
+
|
|
1892
|
+
/**
|
|
1893
|
+
* Localized display names for {{CLASS_NAME}}.
|
|
1894
|
+
*
|
|
1895
|
+
* DO NOT EDIT - This file is auto-generated by Omnify.
|
|
1896
|
+
* Any changes will be overwritten on next generation.
|
|
1897
|
+
*
|
|
1898
|
+
* @generated by @famgia/omnify-laravel
|
|
1899
|
+
*/
|
|
1900
|
+
class {{CLASS_NAME}}Locales
|
|
1901
|
+
{
|
|
1902
|
+
/**
|
|
1903
|
+
* Localized display names for the model.
|
|
1904
|
+
*
|
|
1905
|
+
* @var array<string, string>
|
|
1906
|
+
*/
|
|
1907
|
+
public const DISPLAY_NAMES = [
|
|
1908
|
+
{{LOCALIZED_DISPLAY_NAMES}}
|
|
1909
|
+
];
|
|
1910
|
+
|
|
1911
|
+
/**
|
|
1912
|
+
* Localized display names for properties.
|
|
1913
|
+
*
|
|
1914
|
+
* @var array<string, array<string, string>>
|
|
1915
|
+
*/
|
|
1916
|
+
public const PROPERTY_DISPLAY_NAMES = [
|
|
1917
|
+
{{LOCALIZED_PROPERTY_DISPLAY_NAMES}}
|
|
1918
|
+
];
|
|
1919
|
+
}
|
|
1358
1920
|
`
|
|
1359
1921
|
};
|
|
1360
1922
|
return stubs[stubName] ?? "";
|
|
@@ -1374,15 +1936,42 @@ function generateServiceProvider(schemas, options, stubContent) {
|
|
|
1374
1936
|
schemaName: "__service_provider__"
|
|
1375
1937
|
};
|
|
1376
1938
|
}
|
|
1939
|
+
function generateLocalizedDisplayNameTrait(options, stubContent) {
|
|
1940
|
+
const content = stubContent.replace(/\{\{BASE_MODEL_NAMESPACE\}\}/g, options.baseModelNamespace);
|
|
1941
|
+
return {
|
|
1942
|
+
path: `${options.baseModelPath}/Traits/HasLocalizedDisplayName.php`,
|
|
1943
|
+
content,
|
|
1944
|
+
type: "trait",
|
|
1945
|
+
overwrite: true,
|
|
1946
|
+
// Always overwrite trait
|
|
1947
|
+
schemaName: "__trait__"
|
|
1948
|
+
};
|
|
1949
|
+
}
|
|
1950
|
+
function generateLocalesClass(schema, options, stubContent) {
|
|
1951
|
+
const className = toPascalCase(schema.name);
|
|
1952
|
+
const localizedDisplayNames = generateLocalizedDisplayNames(schema.displayName);
|
|
1953
|
+
const localizedPropertyDisplayNames = generatePropertyLocalizedDisplayNames(schema);
|
|
1954
|
+
const content = stubContent.replace(/\{\{BASE_MODEL_NAMESPACE\}\}/g, options.baseModelNamespace).replace(/\{\{CLASS_NAME\}\}/g, className).replace(/\{\{LOCALIZED_DISPLAY_NAMES\}\}/g, localizedDisplayNames).replace(/\{\{LOCALIZED_PROPERTY_DISPLAY_NAMES\}\}/g, localizedPropertyDisplayNames);
|
|
1955
|
+
return {
|
|
1956
|
+
path: `${options.baseModelPath}/Locales/${className}Locales.php`,
|
|
1957
|
+
content,
|
|
1958
|
+
type: "locales",
|
|
1959
|
+
overwrite: true,
|
|
1960
|
+
// Always overwrite locales
|
|
1961
|
+
schemaName: schema.name
|
|
1962
|
+
};
|
|
1963
|
+
}
|
|
1377
1964
|
function generateModels(schemas, options) {
|
|
1378
1965
|
const resolved = resolveOptions(options);
|
|
1379
1966
|
const models = [];
|
|
1380
1967
|
models.push(generateBaseModel(schemas, resolved, getStubContent("base-model")));
|
|
1968
|
+
models.push(generateLocalizedDisplayNameTrait(resolved, getStubContent("has-localized-display-name")));
|
|
1381
1969
|
models.push(generateServiceProvider(schemas, resolved, getStubContent("service-provider")));
|
|
1382
1970
|
for (const schema of Object.values(schemas)) {
|
|
1383
1971
|
if (schema.kind === "enum") {
|
|
1384
1972
|
continue;
|
|
1385
1973
|
}
|
|
1974
|
+
models.push(generateLocalesClass(schema, resolved, getStubContent("locales")));
|
|
1386
1975
|
models.push(generateEntityBaseModel(
|
|
1387
1976
|
schema,
|
|
1388
1977
|
schemas,
|
|
@@ -1748,6 +2337,24 @@ function getFactoryPath(factory) {
|
|
|
1748
2337
|
}
|
|
1749
2338
|
|
|
1750
2339
|
// src/plugin.ts
|
|
2340
|
+
function getExistingMigrationTables(migrationsDir) {
|
|
2341
|
+
const existingTables = /* @__PURE__ */ new Set();
|
|
2342
|
+
if (!(0, import_node_fs.existsSync)(migrationsDir)) {
|
|
2343
|
+
return existingTables;
|
|
2344
|
+
}
|
|
2345
|
+
try {
|
|
2346
|
+
const files = (0, import_node_fs.readdirSync)(migrationsDir);
|
|
2347
|
+
const createMigrationPattern = /^\d{4}_\d{2}_\d{2}_\d{6}_create_(.+)_table\.php$/;
|
|
2348
|
+
for (const file of files) {
|
|
2349
|
+
const match = file.match(createMigrationPattern);
|
|
2350
|
+
if (match) {
|
|
2351
|
+
existingTables.add(match[1]);
|
|
2352
|
+
}
|
|
2353
|
+
}
|
|
2354
|
+
} catch {
|
|
2355
|
+
}
|
|
2356
|
+
return existingTables;
|
|
2357
|
+
}
|
|
1751
2358
|
var LARAVEL_CONFIG_SCHEMA = {
|
|
1752
2359
|
fields: [
|
|
1753
2360
|
{
|
|
@@ -1831,18 +2438,81 @@ function laravelPlugin(options) {
|
|
|
1831
2438
|
generate: async (ctx) => {
|
|
1832
2439
|
const migrationOptions = {
|
|
1833
2440
|
connection: resolved.connection,
|
|
1834
|
-
timestamp: resolved.timestamp
|
|
2441
|
+
timestamp: resolved.timestamp,
|
|
2442
|
+
customTypes: ctx.customTypes
|
|
1835
2443
|
};
|
|
1836
|
-
const
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
tableName: migration.tables[0],
|
|
1843
|
-
migrationType: migration.type
|
|
2444
|
+
const outputs = [];
|
|
2445
|
+
const migrationsDir = (0, import_node_path.join)(ctx.cwd, resolved.migrationsPath);
|
|
2446
|
+
const existingTables = getExistingMigrationTables(migrationsDir);
|
|
2447
|
+
if (ctx.changes !== void 0) {
|
|
2448
|
+
if (ctx.changes.length === 0) {
|
|
2449
|
+
return outputs;
|
|
1844
2450
|
}
|
|
1845
|
-
|
|
2451
|
+
const addedSchemaNames = new Set(
|
|
2452
|
+
ctx.changes.filter((c) => c.changeType === "added").map((c) => c.schemaName)
|
|
2453
|
+
);
|
|
2454
|
+
if (addedSchemaNames.size > 0) {
|
|
2455
|
+
const addedSchemas = Object.fromEntries(
|
|
2456
|
+
Object.entries(ctx.schemas).filter(([name]) => addedSchemaNames.has(name))
|
|
2457
|
+
);
|
|
2458
|
+
const createMigrations = generateMigrations(addedSchemas, migrationOptions);
|
|
2459
|
+
for (const migration of createMigrations) {
|
|
2460
|
+
const tableName = migration.tables[0];
|
|
2461
|
+
if (existingTables.has(tableName)) {
|
|
2462
|
+
ctx.logger.debug(`Skipping CREATE for ${tableName} (already exists)`);
|
|
2463
|
+
continue;
|
|
2464
|
+
}
|
|
2465
|
+
outputs.push({
|
|
2466
|
+
path: getMigrationPath(migration, resolved.migrationsPath),
|
|
2467
|
+
content: migration.content,
|
|
2468
|
+
type: "migration",
|
|
2469
|
+
metadata: {
|
|
2470
|
+
tableName,
|
|
2471
|
+
migrationType: "create"
|
|
2472
|
+
}
|
|
2473
|
+
});
|
|
2474
|
+
}
|
|
2475
|
+
}
|
|
2476
|
+
const alterChanges = ctx.changes.filter(
|
|
2477
|
+
(c) => c.changeType === "modified" || c.changeType === "removed"
|
|
2478
|
+
);
|
|
2479
|
+
if (alterChanges.length > 0) {
|
|
2480
|
+
const alterMigrations = generateMigrationsFromChanges(
|
|
2481
|
+
alterChanges,
|
|
2482
|
+
migrationOptions
|
|
2483
|
+
);
|
|
2484
|
+
for (const migration of alterMigrations) {
|
|
2485
|
+
outputs.push({
|
|
2486
|
+
path: getMigrationPath(migration, resolved.migrationsPath),
|
|
2487
|
+
content: migration.content,
|
|
2488
|
+
type: "migration",
|
|
2489
|
+
metadata: {
|
|
2490
|
+
tableName: migration.tables[0],
|
|
2491
|
+
migrationType: migration.type
|
|
2492
|
+
}
|
|
2493
|
+
});
|
|
2494
|
+
}
|
|
2495
|
+
}
|
|
2496
|
+
} else {
|
|
2497
|
+
const migrations = generateMigrations(ctx.schemas, migrationOptions);
|
|
2498
|
+
for (const migration of migrations) {
|
|
2499
|
+
const tableName = migration.tables[0];
|
|
2500
|
+
if (migration.type === "create" && existingTables.has(tableName)) {
|
|
2501
|
+
ctx.logger.debug(`Skipping migration for ${tableName} (already exists)`);
|
|
2502
|
+
continue;
|
|
2503
|
+
}
|
|
2504
|
+
outputs.push({
|
|
2505
|
+
path: getMigrationPath(migration, resolved.migrationsPath),
|
|
2506
|
+
content: migration.content,
|
|
2507
|
+
type: "migration",
|
|
2508
|
+
metadata: {
|
|
2509
|
+
tableName,
|
|
2510
|
+
migrationType: migration.type
|
|
2511
|
+
}
|
|
2512
|
+
});
|
|
2513
|
+
}
|
|
2514
|
+
}
|
|
2515
|
+
return outputs;
|
|
1846
2516
|
}
|
|
1847
2517
|
};
|
|
1848
2518
|
const modelGenerator = {
|