@famgia/omnify-laravel 0.0.14 → 0.0.16
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/{chunk-UVF7W2J2.js → chunk-CAWNYSF3.js} +558 -16
- package/dist/chunk-CAWNYSF3.js.map +1 -0
- package/dist/index.cjs +557 -15
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +1 -1
- package/dist/plugin.cjs +555 -15
- package/dist/plugin.cjs.map +1 -1
- package/dist/plugin.d.cts +16 -1
- package/dist/plugin.d.ts +16 -1
- package/dist/plugin.js +1 -1
- package/package.json +3 -3
- package/dist/chunk-UVF7W2J2.js.map +0 -1
package/dist/plugin.cjs
CHANGED
|
@@ -24,6 +24,8 @@ __export(plugin_exports, {
|
|
|
24
24
|
laravelPlugin: () => laravelPlugin
|
|
25
25
|
});
|
|
26
26
|
module.exports = __toCommonJS(plugin_exports);
|
|
27
|
+
var import_node_fs = require("fs");
|
|
28
|
+
var import_node_path = require("path");
|
|
27
29
|
|
|
28
30
|
// src/migration/schema-builder.ts
|
|
29
31
|
var TYPE_METHOD_MAP = {
|
|
@@ -103,9 +105,8 @@ function propertyToColumnMethod(propertyName, property) {
|
|
|
103
105
|
if (baseProp.unique) {
|
|
104
106
|
modifiers.push({ method: "unique" });
|
|
105
107
|
}
|
|
106
|
-
if (baseProp.default !== void 0) {
|
|
107
|
-
|
|
108
|
-
modifiers.push({ method: "default", args: [defaultValue] });
|
|
108
|
+
if (baseProp.default !== void 0 && baseProp.default !== null) {
|
|
109
|
+
modifiers.push({ method: "default", args: [baseProp.default] });
|
|
109
110
|
}
|
|
110
111
|
if (baseProp.unsigned && (method === "integer" || method === "bigInteger")) {
|
|
111
112
|
modifiers.push({ method: "unsigned" });
|
|
@@ -237,11 +238,18 @@ function generateForeignKey(propertyName, property, allSchemas) {
|
|
|
237
238
|
} else if (targetPkType === "String") {
|
|
238
239
|
method = "string";
|
|
239
240
|
}
|
|
241
|
+
const modifiers = [];
|
|
242
|
+
if (assocProp.nullable || assocProp.relation === "ManyToOne") {
|
|
243
|
+
modifiers.push({ method: "nullable" });
|
|
244
|
+
}
|
|
245
|
+
if (assocProp.default !== void 0 && assocProp.default !== null) {
|
|
246
|
+
modifiers.push({ method: "default", args: [assocProp.default] });
|
|
247
|
+
}
|
|
240
248
|
const column = {
|
|
241
249
|
name: columnName,
|
|
242
250
|
method,
|
|
243
251
|
args: [columnName],
|
|
244
|
-
modifiers
|
|
252
|
+
modifiers
|
|
245
253
|
};
|
|
246
254
|
const foreignKey = {
|
|
247
255
|
columns: [columnName],
|
|
@@ -341,6 +349,12 @@ function formatColumnMethod(column) {
|
|
|
341
349
|
if (typeof arg === "string") {
|
|
342
350
|
return `'${arg}'`;
|
|
343
351
|
}
|
|
352
|
+
if (typeof arg === "boolean") {
|
|
353
|
+
return arg ? "true" : "false";
|
|
354
|
+
}
|
|
355
|
+
if (typeof arg === "number") {
|
|
356
|
+
return String(arg);
|
|
357
|
+
}
|
|
344
358
|
return String(arg);
|
|
345
359
|
}).join(", ");
|
|
346
360
|
code += `->${modifier.method}(${modArgs})`;
|
|
@@ -448,6 +462,7 @@ function generatePivotTableBlueprint(pivot) {
|
|
|
448
462
|
args: [pivot.targetColumn],
|
|
449
463
|
modifiers: []
|
|
450
464
|
});
|
|
465
|
+
columns.push(...generateTimestampColumns());
|
|
451
466
|
foreignKeys.push({
|
|
452
467
|
columns: [pivot.sourceColumn],
|
|
453
468
|
references: "id",
|
|
@@ -846,7 +861,7 @@ function generateEntityBaseModel(schema, schemas, options, stubContent, authStub
|
|
|
846
861
|
if (assoc.target) {
|
|
847
862
|
imports.push(`use ${options.modelNamespace}\\${toPascalCase(assoc.target)};`);
|
|
848
863
|
}
|
|
849
|
-
relations.push(generateRelation(propName, assoc, options));
|
|
864
|
+
relations.push(generateRelation(propName, assoc, schema, schemas, options));
|
|
850
865
|
if (assoc.relation === "ManyToOne" || assoc.relation === "OneToOne") {
|
|
851
866
|
if (!assoc.mappedBy) {
|
|
852
867
|
const fkName = toSnakeCase(propName) + "_id";
|
|
@@ -903,7 +918,22 @@ ${docProperties.join("\n")}
|
|
|
903
918
|
schemaName: schema.name
|
|
904
919
|
};
|
|
905
920
|
}
|
|
906
|
-
function
|
|
921
|
+
function findInverseRelation(currentSchemaName, targetSchemaName, schemas) {
|
|
922
|
+
const targetSchema = schemas[targetSchemaName];
|
|
923
|
+
if (!targetSchema || !targetSchema.properties) {
|
|
924
|
+
return null;
|
|
925
|
+
}
|
|
926
|
+
for (const [propName, propDef] of Object.entries(targetSchema.properties)) {
|
|
927
|
+
if (propDef.type === "Association") {
|
|
928
|
+
const assoc = propDef;
|
|
929
|
+
if (assoc.relation === "ManyToOne" && assoc.target === currentSchemaName) {
|
|
930
|
+
return propName;
|
|
931
|
+
}
|
|
932
|
+
}
|
|
933
|
+
}
|
|
934
|
+
return null;
|
|
935
|
+
}
|
|
936
|
+
function generateRelation(propName, assoc, schema, schemas, options) {
|
|
907
937
|
const methodName = toCamelCase(propName);
|
|
908
938
|
const targetClass = assoc.target ? toPascalCase(assoc.target) : "";
|
|
909
939
|
const fkName = toSnakeCase(propName) + "_id";
|
|
@@ -933,14 +963,28 @@ function generateRelation(propName, assoc, options) {
|
|
|
933
963
|
{
|
|
934
964
|
return $this->belongsTo(${targetClass}::class, '${fkName}');
|
|
935
965
|
}`;
|
|
936
|
-
case "OneToMany":
|
|
966
|
+
case "OneToMany": {
|
|
967
|
+
let foreignKey;
|
|
968
|
+
if (assoc.inversedBy) {
|
|
969
|
+
foreignKey = toSnakeCase(assoc.inversedBy) + "_id";
|
|
970
|
+
} else if (assoc.target) {
|
|
971
|
+
const inverseRelation = findInverseRelation(schema.name, assoc.target, schemas);
|
|
972
|
+
if (inverseRelation) {
|
|
973
|
+
foreignKey = toSnakeCase(inverseRelation) + "_id";
|
|
974
|
+
} else {
|
|
975
|
+
foreignKey = toSnakeCase(schema.name) + "_id";
|
|
976
|
+
}
|
|
977
|
+
} else {
|
|
978
|
+
foreignKey = toSnakeCase(propName) + "_id";
|
|
979
|
+
}
|
|
937
980
|
return ` /**
|
|
938
981
|
* Get the ${propName} for this model.
|
|
939
982
|
*/
|
|
940
983
|
public function ${methodName}(): HasMany
|
|
941
984
|
{
|
|
942
|
-
return $this->hasMany(${targetClass}::class, '${
|
|
985
|
+
return $this->hasMany(${targetClass}::class, '${foreignKey}');
|
|
943
986
|
}`;
|
|
987
|
+
}
|
|
944
988
|
case "ManyToMany": {
|
|
945
989
|
const pivotTable = assoc.joinTable ?? `${toSnakeCase(propName)}_pivot`;
|
|
946
990
|
return ` /**
|
|
@@ -1237,14 +1281,73 @@ class {{CLASS_NAME}} extends {{CLASS_NAME}}BaseModel
|
|
|
1237
1281
|
|
|
1238
1282
|
// Add your custom methods here
|
|
1239
1283
|
}
|
|
1284
|
+
`,
|
|
1285
|
+
"service-provider": `<?php
|
|
1286
|
+
|
|
1287
|
+
namespace App\\Providers;
|
|
1288
|
+
|
|
1289
|
+
use Illuminate\\Database\\Eloquent\\Relations\\Relation;
|
|
1290
|
+
use Illuminate\\Support\\ServiceProvider;
|
|
1291
|
+
|
|
1292
|
+
/**
|
|
1293
|
+
* Omnify Service Provider
|
|
1294
|
+
*
|
|
1295
|
+
* DO NOT EDIT - This file is auto-generated by Omnify.
|
|
1296
|
+
* Any changes will be overwritten on next generation.
|
|
1297
|
+
*
|
|
1298
|
+
* - Loads Omnify migrations from database/migrations/omnify
|
|
1299
|
+
* - Registers morph map for polymorphic relationships
|
|
1300
|
+
*
|
|
1301
|
+
* @generated by @famgia/omnify-laravel
|
|
1302
|
+
*/
|
|
1303
|
+
class OmnifyServiceProvider extends ServiceProvider
|
|
1304
|
+
{
|
|
1305
|
+
/**
|
|
1306
|
+
* Register any application services.
|
|
1307
|
+
*/
|
|
1308
|
+
public function register(): void
|
|
1309
|
+
{
|
|
1310
|
+
//
|
|
1311
|
+
}
|
|
1312
|
+
|
|
1313
|
+
/**
|
|
1314
|
+
* Bootstrap any application services.
|
|
1315
|
+
*/
|
|
1316
|
+
public function boot(): void
|
|
1317
|
+
{
|
|
1318
|
+
// Load Omnify migrations from custom directory
|
|
1319
|
+
$this->loadMigrationsFrom(database_path('migrations/omnify'));
|
|
1320
|
+
|
|
1321
|
+
// Register morph map for polymorphic relationships
|
|
1322
|
+
Relation::enforceMorphMap([
|
|
1323
|
+
{{MORPH_MAP}}
|
|
1324
|
+
]);
|
|
1325
|
+
}
|
|
1326
|
+
}
|
|
1240
1327
|
`
|
|
1241
1328
|
};
|
|
1242
1329
|
return stubs[stubName] ?? "";
|
|
1243
1330
|
}
|
|
1331
|
+
function generateServiceProvider(schemas, options, stubContent) {
|
|
1332
|
+
const morphMap = Object.values(schemas).filter((s) => s.kind !== "enum").map((s) => {
|
|
1333
|
+
const className = toPascalCase(s.name);
|
|
1334
|
+
return ` '${s.name}' => \\${options.modelNamespace}\\${className}::class,`;
|
|
1335
|
+
}).join("\n");
|
|
1336
|
+
const content = stubContent.replace(/\{\{MORPH_MAP\}\}/g, morphMap);
|
|
1337
|
+
return {
|
|
1338
|
+
path: "app/Providers/OmnifyServiceProvider.php",
|
|
1339
|
+
content,
|
|
1340
|
+
type: "service-provider",
|
|
1341
|
+
overwrite: true,
|
|
1342
|
+
// Always overwrite to keep morph map in sync
|
|
1343
|
+
schemaName: "__service_provider__"
|
|
1344
|
+
};
|
|
1345
|
+
}
|
|
1244
1346
|
function generateModels(schemas, options) {
|
|
1245
1347
|
const resolved = resolveOptions(options);
|
|
1246
1348
|
const models = [];
|
|
1247
1349
|
models.push(generateBaseModel(schemas, resolved, getStubContent("base-model")));
|
|
1350
|
+
models.push(generateServiceProvider(schemas, resolved, getStubContent("service-provider")));
|
|
1248
1351
|
for (const schema of Object.values(schemas)) {
|
|
1249
1352
|
if (schema.kind === "enum") {
|
|
1250
1353
|
continue;
|
|
@@ -1263,6 +1366,355 @@ function generateModels(schemas, options) {
|
|
|
1263
1366
|
function getModelPath(model) {
|
|
1264
1367
|
return model.path;
|
|
1265
1368
|
}
|
|
1369
|
+
function generateProviderRegistration(existingContent, laravelVersion) {
|
|
1370
|
+
const providerClass = "App\\Providers\\OmnifyServiceProvider::class";
|
|
1371
|
+
const providerLine = ` ${providerClass},`;
|
|
1372
|
+
if (existingContent && existingContent.includes("OmnifyServiceProvider")) {
|
|
1373
|
+
return {
|
|
1374
|
+
path: laravelVersion === "laravel11+" ? "bootstrap/providers.php" : "config/app.php",
|
|
1375
|
+
content: existingContent,
|
|
1376
|
+
laravelVersion,
|
|
1377
|
+
alreadyRegistered: true
|
|
1378
|
+
};
|
|
1379
|
+
}
|
|
1380
|
+
if (laravelVersion === "laravel11+") {
|
|
1381
|
+
if (existingContent) {
|
|
1382
|
+
const lines = existingContent.split("\n");
|
|
1383
|
+
const result = [];
|
|
1384
|
+
let inserted = false;
|
|
1385
|
+
for (let i = 0; i < lines.length; i++) {
|
|
1386
|
+
const line = lines[i];
|
|
1387
|
+
if (!inserted && line.trim() === "];") {
|
|
1388
|
+
result.push(providerLine);
|
|
1389
|
+
inserted = true;
|
|
1390
|
+
}
|
|
1391
|
+
result.push(line);
|
|
1392
|
+
}
|
|
1393
|
+
return {
|
|
1394
|
+
path: "bootstrap/providers.php",
|
|
1395
|
+
content: result.join("\n"),
|
|
1396
|
+
laravelVersion,
|
|
1397
|
+
alreadyRegistered: false
|
|
1398
|
+
};
|
|
1399
|
+
} else {
|
|
1400
|
+
return {
|
|
1401
|
+
path: "bootstrap/providers.php",
|
|
1402
|
+
content: `<?php
|
|
1403
|
+
|
|
1404
|
+
return [
|
|
1405
|
+
App\\Providers\\AppServiceProvider::class,
|
|
1406
|
+
${providerLine}
|
|
1407
|
+
];
|
|
1408
|
+
`,
|
|
1409
|
+
laravelVersion,
|
|
1410
|
+
alreadyRegistered: false
|
|
1411
|
+
};
|
|
1412
|
+
}
|
|
1413
|
+
} else {
|
|
1414
|
+
if (existingContent) {
|
|
1415
|
+
const providersSectionRegex = /'providers'\s*=>\s*\[[\s\S]*?\n(\s*)\]/m;
|
|
1416
|
+
const match = existingContent.match(providersSectionRegex);
|
|
1417
|
+
if (match) {
|
|
1418
|
+
const providersStart = existingContent.indexOf("'providers'");
|
|
1419
|
+
if (providersStart === -1) {
|
|
1420
|
+
return null;
|
|
1421
|
+
}
|
|
1422
|
+
let depth = 0;
|
|
1423
|
+
let foundStart = false;
|
|
1424
|
+
let insertPos = -1;
|
|
1425
|
+
for (let i = providersStart; i < existingContent.length; i++) {
|
|
1426
|
+
const char = existingContent[i];
|
|
1427
|
+
if (char === "[") {
|
|
1428
|
+
foundStart = true;
|
|
1429
|
+
depth++;
|
|
1430
|
+
} else if (char === "]") {
|
|
1431
|
+
depth--;
|
|
1432
|
+
if (foundStart && depth === 0) {
|
|
1433
|
+
insertPos = i;
|
|
1434
|
+
break;
|
|
1435
|
+
}
|
|
1436
|
+
}
|
|
1437
|
+
}
|
|
1438
|
+
if (insertPos !== -1) {
|
|
1439
|
+
const beforeClose = existingContent.substring(0, insertPos);
|
|
1440
|
+
const lastNewline = beforeClose.lastIndexOf("\n");
|
|
1441
|
+
const content = existingContent.substring(0, lastNewline + 1) + providerLine + "\n" + existingContent.substring(lastNewline + 1);
|
|
1442
|
+
return {
|
|
1443
|
+
path: "config/app.php",
|
|
1444
|
+
content,
|
|
1445
|
+
laravelVersion,
|
|
1446
|
+
alreadyRegistered: false
|
|
1447
|
+
};
|
|
1448
|
+
}
|
|
1449
|
+
}
|
|
1450
|
+
return null;
|
|
1451
|
+
} else {
|
|
1452
|
+
return null;
|
|
1453
|
+
}
|
|
1454
|
+
}
|
|
1455
|
+
}
|
|
1456
|
+
|
|
1457
|
+
// src/factory/generator.ts
|
|
1458
|
+
function resolveOptions2(options) {
|
|
1459
|
+
return {
|
|
1460
|
+
modelNamespace: options?.modelNamespace ?? "App\\Models",
|
|
1461
|
+
factoryPath: options?.factoryPath ?? "database/factories",
|
|
1462
|
+
fakerLocale: options?.fakerLocale ?? "en_US"
|
|
1463
|
+
};
|
|
1464
|
+
}
|
|
1465
|
+
function getStubContent2() {
|
|
1466
|
+
return `<?php
|
|
1467
|
+
|
|
1468
|
+
namespace Database\\Factories;
|
|
1469
|
+
|
|
1470
|
+
use {{MODEL_NAMESPACE}}\\{{MODEL_NAME}};
|
|
1471
|
+
use Illuminate\\Database\\Eloquent\\Factories\\Factory;
|
|
1472
|
+
{{IMPORTS}}
|
|
1473
|
+
|
|
1474
|
+
/**
|
|
1475
|
+
* @extends Factory<{{MODEL_NAME}}>
|
|
1476
|
+
*/
|
|
1477
|
+
class {{MODEL_NAME}}Factory extends Factory
|
|
1478
|
+
{
|
|
1479
|
+
protected $model = {{MODEL_NAME}}::class;
|
|
1480
|
+
|
|
1481
|
+
/**
|
|
1482
|
+
* Define the model's default state.
|
|
1483
|
+
*
|
|
1484
|
+
* @return array<string, mixed>
|
|
1485
|
+
*/
|
|
1486
|
+
public function definition(): array
|
|
1487
|
+
{
|
|
1488
|
+
return [
|
|
1489
|
+
{{ATTRIBUTES}}
|
|
1490
|
+
];
|
|
1491
|
+
}
|
|
1492
|
+
}
|
|
1493
|
+
`;
|
|
1494
|
+
}
|
|
1495
|
+
function generateFakeData(propertyName, property, schema, schemas) {
|
|
1496
|
+
const type = property.type;
|
|
1497
|
+
if (["deleted_at", "created_at", "updated_at"].includes(propertyName)) {
|
|
1498
|
+
return null;
|
|
1499
|
+
}
|
|
1500
|
+
if (type === "Association") {
|
|
1501
|
+
return null;
|
|
1502
|
+
}
|
|
1503
|
+
switch (type) {
|
|
1504
|
+
case "String":
|
|
1505
|
+
return generateStringFake(propertyName, property);
|
|
1506
|
+
case "Email":
|
|
1507
|
+
return `'${propertyName}' => fake()->unique()->safeEmail(),`;
|
|
1508
|
+
case "Password":
|
|
1509
|
+
return `'${propertyName}' => bcrypt('password'),`;
|
|
1510
|
+
case "Int":
|
|
1511
|
+
case "BigInt":
|
|
1512
|
+
return generateIntFake(propertyName, property);
|
|
1513
|
+
case "Float":
|
|
1514
|
+
case "Decimal":
|
|
1515
|
+
return `'${propertyName}' => fake()->randomFloat(2, 1, 10000),`;
|
|
1516
|
+
case "Boolean":
|
|
1517
|
+
return `'${propertyName}' => fake()->boolean(),`;
|
|
1518
|
+
case "Text":
|
|
1519
|
+
return `'${propertyName}' => fake()->paragraphs(3, true),`;
|
|
1520
|
+
case "LongText":
|
|
1521
|
+
return `'${propertyName}' => fake()->paragraphs(5, true),`;
|
|
1522
|
+
case "Date":
|
|
1523
|
+
return `'${propertyName}' => fake()->date(),`;
|
|
1524
|
+
case "Time":
|
|
1525
|
+
return `'${propertyName}' => fake()->time(),`;
|
|
1526
|
+
case "Timestamp":
|
|
1527
|
+
case "DateTime":
|
|
1528
|
+
return `'${propertyName}' => fake()->dateTime(),`;
|
|
1529
|
+
case "Json":
|
|
1530
|
+
return `'${propertyName}' => [],`;
|
|
1531
|
+
case "Enum":
|
|
1532
|
+
return generateEnumFake(propertyName, property);
|
|
1533
|
+
case "EnumRef":
|
|
1534
|
+
return generateEnumRefFake(propertyName, property, schemas);
|
|
1535
|
+
default:
|
|
1536
|
+
return `'${propertyName}' => fake()->sentence(),`;
|
|
1537
|
+
}
|
|
1538
|
+
}
|
|
1539
|
+
function generateStringFake(propertyName, property) {
|
|
1540
|
+
if (propertyName === "slug") {
|
|
1541
|
+
return `'${propertyName}' => fake()->unique()->slug(),`;
|
|
1542
|
+
}
|
|
1543
|
+
if (propertyName === "uuid" || propertyName === "uid") {
|
|
1544
|
+
return `'${propertyName}' => (string) \\Illuminate\\Support\\Str::uuid(),`;
|
|
1545
|
+
}
|
|
1546
|
+
if (propertyName.includes("email")) {
|
|
1547
|
+
return `'${propertyName}' => fake()->unique()->safeEmail(),`;
|
|
1548
|
+
}
|
|
1549
|
+
if (propertyName.includes("phone")) {
|
|
1550
|
+
return `'${propertyName}' => fake()->phoneNumber(),`;
|
|
1551
|
+
}
|
|
1552
|
+
if (propertyName.includes("image") || propertyName.includes("photo") || propertyName.includes("avatar")) {
|
|
1553
|
+
return `'${propertyName}' => fake()->imageUrl(),`;
|
|
1554
|
+
}
|
|
1555
|
+
if (propertyName.includes("url") || propertyName.includes("website")) {
|
|
1556
|
+
return `'${propertyName}' => fake()->url(),`;
|
|
1557
|
+
}
|
|
1558
|
+
if (propertyName.includes("path") || propertyName.includes("file")) {
|
|
1559
|
+
return `'${propertyName}' => 'uploads/' . fake()->uuid() . '.jpg',`;
|
|
1560
|
+
}
|
|
1561
|
+
if (propertyName === "name" || propertyName === "title") {
|
|
1562
|
+
return `'${propertyName}' => fake()->sentence(3),`;
|
|
1563
|
+
}
|
|
1564
|
+
if (propertyName.includes("name")) {
|
|
1565
|
+
return `'${propertyName}' => fake()->name(),`;
|
|
1566
|
+
}
|
|
1567
|
+
if (propertyName.includes("address")) {
|
|
1568
|
+
return `'${propertyName}' => fake()->address(),`;
|
|
1569
|
+
}
|
|
1570
|
+
if (propertyName.includes("city")) {
|
|
1571
|
+
return `'${propertyName}' => fake()->city(),`;
|
|
1572
|
+
}
|
|
1573
|
+
if (propertyName.includes("country")) {
|
|
1574
|
+
return `'${propertyName}' => fake()->country(),`;
|
|
1575
|
+
}
|
|
1576
|
+
if (propertyName.includes("zip") || propertyName.includes("postal")) {
|
|
1577
|
+
return `'${propertyName}' => fake()->postcode(),`;
|
|
1578
|
+
}
|
|
1579
|
+
if (propertyName.includes("color")) {
|
|
1580
|
+
return `'${propertyName}' => fake()->hexColor(),`;
|
|
1581
|
+
}
|
|
1582
|
+
if (propertyName.includes("token") || propertyName.includes("secret") || propertyName.includes("key")) {
|
|
1583
|
+
return `'${propertyName}' => \\Illuminate\\Support\\Str::random(32),`;
|
|
1584
|
+
}
|
|
1585
|
+
if (propertyName.includes("code")) {
|
|
1586
|
+
return `'${propertyName}' => fake()->unique()->regexify('[A-Z0-9]{8}'),`;
|
|
1587
|
+
}
|
|
1588
|
+
const length = property.length;
|
|
1589
|
+
if (length && length <= 50) {
|
|
1590
|
+
return `'${propertyName}' => fake()->words(3, true),`;
|
|
1591
|
+
}
|
|
1592
|
+
return `'${propertyName}' => fake()->sentence(),`;
|
|
1593
|
+
}
|
|
1594
|
+
function generateIntFake(propertyName, property) {
|
|
1595
|
+
if (propertyName.includes("count") || propertyName.includes("quantity")) {
|
|
1596
|
+
return `'${propertyName}' => fake()->numberBetween(0, 100),`;
|
|
1597
|
+
}
|
|
1598
|
+
if (propertyName.includes("price") || propertyName.includes("amount") || propertyName.includes("cost")) {
|
|
1599
|
+
return `'${propertyName}' => fake()->numberBetween(100, 10000),`;
|
|
1600
|
+
}
|
|
1601
|
+
if (propertyName.includes("order") || propertyName.includes("sort") || propertyName.includes("position")) {
|
|
1602
|
+
return `'${propertyName}' => fake()->numberBetween(1, 100),`;
|
|
1603
|
+
}
|
|
1604
|
+
if (propertyName.includes("age")) {
|
|
1605
|
+
return `'${propertyName}' => fake()->numberBetween(18, 80),`;
|
|
1606
|
+
}
|
|
1607
|
+
if (propertyName.includes("year")) {
|
|
1608
|
+
return `'${propertyName}' => fake()->year(),`;
|
|
1609
|
+
}
|
|
1610
|
+
return `'${propertyName}' => fake()->numberBetween(1, 1000),`;
|
|
1611
|
+
}
|
|
1612
|
+
function generateEnumFake(propertyName, property) {
|
|
1613
|
+
const enumValues = property.enum;
|
|
1614
|
+
if (!enumValues || enumValues.length === 0) {
|
|
1615
|
+
return `'${propertyName}' => null,`;
|
|
1616
|
+
}
|
|
1617
|
+
const values = enumValues.map((v) => typeof v === "string" ? v : v.value);
|
|
1618
|
+
const valuesStr = values.map((v) => `'${v}'`).join(", ");
|
|
1619
|
+
return `'${propertyName}' => fake()->randomElement([${valuesStr}]),`;
|
|
1620
|
+
}
|
|
1621
|
+
function generateEnumRefFake(propertyName, property, schemas) {
|
|
1622
|
+
const enumName = property.enum;
|
|
1623
|
+
if (!enumName) {
|
|
1624
|
+
return `'${propertyName}' => null,`;
|
|
1625
|
+
}
|
|
1626
|
+
const enumSchema = schemas[enumName];
|
|
1627
|
+
if (!enumSchema || enumSchema.kind !== "enum" || !enumSchema.values) {
|
|
1628
|
+
return `'${propertyName}' => null,`;
|
|
1629
|
+
}
|
|
1630
|
+
const valuesStr = enumSchema.values.map((v) => `'${v}'`).join(", ");
|
|
1631
|
+
return `'${propertyName}' => fake()->randomElement([${valuesStr}]),`;
|
|
1632
|
+
}
|
|
1633
|
+
function generateAssociationFake(propertyName, property, schema, schemas, modelNamespace) {
|
|
1634
|
+
if (property.type !== "Association") {
|
|
1635
|
+
return null;
|
|
1636
|
+
}
|
|
1637
|
+
const relation = property.relation;
|
|
1638
|
+
const target = property.target;
|
|
1639
|
+
if (relation !== "ManyToOne" || !target) {
|
|
1640
|
+
return null;
|
|
1641
|
+
}
|
|
1642
|
+
const foreignKey = `${toSnakeCase(propertyName)}_id`;
|
|
1643
|
+
const isNullable2 = property.nullable ?? false;
|
|
1644
|
+
const targetSchema = schemas[target];
|
|
1645
|
+
if (!targetSchema) {
|
|
1646
|
+
return null;
|
|
1647
|
+
}
|
|
1648
|
+
let fake;
|
|
1649
|
+
if (isNullable2) {
|
|
1650
|
+
fake = `'${foreignKey}' => ${target}::query()->inRandomOrder()->first()?->id,`;
|
|
1651
|
+
} else {
|
|
1652
|
+
fake = `'${foreignKey}' => ${target}::query()->inRandomOrder()->first()?->id ?? ${target}::factory()->create()->id,`;
|
|
1653
|
+
}
|
|
1654
|
+
let importStatement;
|
|
1655
|
+
if (target !== schema.name) {
|
|
1656
|
+
importStatement = `use ${modelNamespace}\\${target};`;
|
|
1657
|
+
}
|
|
1658
|
+
return { fake, import: importStatement };
|
|
1659
|
+
}
|
|
1660
|
+
function generateFactory(schema, schemas, options, stubContent) {
|
|
1661
|
+
if (schema.kind === "enum") {
|
|
1662
|
+
return null;
|
|
1663
|
+
}
|
|
1664
|
+
const modelName = toPascalCase(schema.name);
|
|
1665
|
+
const factoryName = `${modelName}Factory`;
|
|
1666
|
+
const attributes = [];
|
|
1667
|
+
const imports = [];
|
|
1668
|
+
if (schema.properties) {
|
|
1669
|
+
for (const [propName, prop] of Object.entries(schema.properties)) {
|
|
1670
|
+
if (prop.type === "Association") {
|
|
1671
|
+
const assocResult = generateAssociationFake(propName, prop, schema, schemas, options.modelNamespace);
|
|
1672
|
+
if (assocResult) {
|
|
1673
|
+
attributes.push(assocResult.fake);
|
|
1674
|
+
if (assocResult.import) {
|
|
1675
|
+
imports.push(assocResult.import);
|
|
1676
|
+
}
|
|
1677
|
+
}
|
|
1678
|
+
continue;
|
|
1679
|
+
}
|
|
1680
|
+
const fake = generateFakeData(propName, prop, schema, schemas);
|
|
1681
|
+
if (fake) {
|
|
1682
|
+
attributes.push(fake);
|
|
1683
|
+
}
|
|
1684
|
+
}
|
|
1685
|
+
}
|
|
1686
|
+
let content = stubContent;
|
|
1687
|
+
content = content.replace(/\{\{MODEL_NAMESPACE\}\}/g, options.modelNamespace);
|
|
1688
|
+
content = content.replace(/\{\{MODEL_NAME\}\}/g, modelName);
|
|
1689
|
+
const uniqueImports = [...new Set(imports)];
|
|
1690
|
+
const importsStr = uniqueImports.length > 0 ? "\n" + uniqueImports.join("\n") : "";
|
|
1691
|
+
content = content.replace(/\{\{IMPORTS\}\}/g, importsStr);
|
|
1692
|
+
const attributesStr = attributes.length > 0 ? attributes.map((a) => ` ${a}`).join("\n") : "";
|
|
1693
|
+
content = content.replace(/\{\{ATTRIBUTES\}\}/g, attributesStr);
|
|
1694
|
+
return {
|
|
1695
|
+
name: factoryName,
|
|
1696
|
+
schemaName: schema.name,
|
|
1697
|
+
path: `${options.factoryPath}/${factoryName}.php`,
|
|
1698
|
+
content,
|
|
1699
|
+
overwrite: false
|
|
1700
|
+
// Factories should not overwrite existing files
|
|
1701
|
+
};
|
|
1702
|
+
}
|
|
1703
|
+
function generateFactories(schemas, options) {
|
|
1704
|
+
const resolved = resolveOptions2(options);
|
|
1705
|
+
const stubContent = getStubContent2();
|
|
1706
|
+
const factories = [];
|
|
1707
|
+
for (const schema of Object.values(schemas)) {
|
|
1708
|
+
const factory = generateFactory(schema, schemas, resolved, stubContent);
|
|
1709
|
+
if (factory) {
|
|
1710
|
+
factories.push(factory);
|
|
1711
|
+
}
|
|
1712
|
+
}
|
|
1713
|
+
return factories;
|
|
1714
|
+
}
|
|
1715
|
+
function getFactoryPath(factory) {
|
|
1716
|
+
return factory.path;
|
|
1717
|
+
}
|
|
1266
1718
|
|
|
1267
1719
|
// src/plugin.ts
|
|
1268
1720
|
var LARAVEL_CONFIG_SCHEMA = {
|
|
@@ -1271,8 +1723,8 @@ var LARAVEL_CONFIG_SCHEMA = {
|
|
|
1271
1723
|
key: "migrationsPath",
|
|
1272
1724
|
type: "path",
|
|
1273
1725
|
label: "Migrations Path",
|
|
1274
|
-
description: "Directory for Laravel migration files",
|
|
1275
|
-
default: "database/migrations",
|
|
1726
|
+
description: "Directory for Laravel migration files (loaded via OmnifyServiceProvider)",
|
|
1727
|
+
default: "database/migrations/omnify",
|
|
1276
1728
|
group: "output"
|
|
1277
1729
|
},
|
|
1278
1730
|
{
|
|
@@ -1299,6 +1751,22 @@ var LARAVEL_CONFIG_SCHEMA = {
|
|
|
1299
1751
|
default: true,
|
|
1300
1752
|
group: "options"
|
|
1301
1753
|
},
|
|
1754
|
+
{
|
|
1755
|
+
key: "factoriesPath",
|
|
1756
|
+
type: "path",
|
|
1757
|
+
label: "Factories Path",
|
|
1758
|
+
description: "Directory for Laravel factory files",
|
|
1759
|
+
default: "database/factories",
|
|
1760
|
+
group: "output"
|
|
1761
|
+
},
|
|
1762
|
+
{
|
|
1763
|
+
key: "generateFactories",
|
|
1764
|
+
type: "boolean",
|
|
1765
|
+
label: "Generate Factories",
|
|
1766
|
+
description: "Generate Laravel factory classes for testing",
|
|
1767
|
+
default: true,
|
|
1768
|
+
group: "options"
|
|
1769
|
+
},
|
|
1302
1770
|
{
|
|
1303
1771
|
key: "connection",
|
|
1304
1772
|
type: "string",
|
|
@@ -1309,20 +1777,23 @@ var LARAVEL_CONFIG_SCHEMA = {
|
|
|
1309
1777
|
}
|
|
1310
1778
|
]
|
|
1311
1779
|
};
|
|
1312
|
-
function
|
|
1780
|
+
function resolveOptions3(options) {
|
|
1313
1781
|
return {
|
|
1314
|
-
migrationsPath: options?.migrationsPath ?? "database/migrations",
|
|
1782
|
+
migrationsPath: options?.migrationsPath ?? "database/migrations/omnify",
|
|
1315
1783
|
modelsPath: options?.modelsPath ?? "app/Models",
|
|
1316
1784
|
baseModelsPath: options?.baseModelsPath ?? "app/Models/OmnifyBase",
|
|
1317
1785
|
modelNamespace: options?.modelNamespace ?? "App\\Models",
|
|
1318
1786
|
baseModelNamespace: options?.baseModelNamespace ?? "App\\Models\\OmnifyBase",
|
|
1319
1787
|
generateModels: options?.generateModels ?? true,
|
|
1788
|
+
factoriesPath: options?.factoriesPath ?? "database/factories",
|
|
1789
|
+
generateFactories: options?.generateFactories ?? true,
|
|
1790
|
+
fakerLocale: options?.fakerLocale ?? "en_US",
|
|
1320
1791
|
connection: options?.connection,
|
|
1321
1792
|
timestamp: options?.timestamp
|
|
1322
1793
|
};
|
|
1323
1794
|
}
|
|
1324
1795
|
function laravelPlugin(options) {
|
|
1325
|
-
const resolved =
|
|
1796
|
+
const resolved = resolveOptions3(options);
|
|
1326
1797
|
const migrationGenerator = {
|
|
1327
1798
|
name: "laravel-migrations",
|
|
1328
1799
|
description: "Generate Laravel migration files",
|
|
@@ -1354,7 +1825,7 @@ function laravelPlugin(options) {
|
|
|
1354
1825
|
baseModelPath: resolved.baseModelsPath
|
|
1355
1826
|
};
|
|
1356
1827
|
const models = generateModels(ctx.schemas, modelOptions);
|
|
1357
|
-
|
|
1828
|
+
const outputs = models.map((model) => ({
|
|
1358
1829
|
path: getModelPath(model),
|
|
1359
1830
|
content: model.content,
|
|
1360
1831
|
type: "model",
|
|
@@ -1365,13 +1836,82 @@ function laravelPlugin(options) {
|
|
|
1365
1836
|
schemaName: model.schemaName
|
|
1366
1837
|
}
|
|
1367
1838
|
}));
|
|
1839
|
+
const bootstrapProvidersPath = (0, import_node_path.join)(ctx.cwd, "bootstrap/providers.php");
|
|
1840
|
+
const configAppPath = (0, import_node_path.join)(ctx.cwd, "config/app.php");
|
|
1841
|
+
let existingContent = null;
|
|
1842
|
+
let laravelVersion;
|
|
1843
|
+
if ((0, import_node_fs.existsSync)(bootstrapProvidersPath)) {
|
|
1844
|
+
laravelVersion = "laravel11+";
|
|
1845
|
+
try {
|
|
1846
|
+
existingContent = (0, import_node_fs.readFileSync)(bootstrapProvidersPath, "utf-8");
|
|
1847
|
+
} catch {
|
|
1848
|
+
existingContent = null;
|
|
1849
|
+
}
|
|
1850
|
+
} else if ((0, import_node_fs.existsSync)(configAppPath)) {
|
|
1851
|
+
laravelVersion = "laravel10-";
|
|
1852
|
+
try {
|
|
1853
|
+
existingContent = (0, import_node_fs.readFileSync)(configAppPath, "utf-8");
|
|
1854
|
+
} catch {
|
|
1855
|
+
existingContent = null;
|
|
1856
|
+
}
|
|
1857
|
+
} else {
|
|
1858
|
+
laravelVersion = "laravel11+";
|
|
1859
|
+
}
|
|
1860
|
+
const registration = generateProviderRegistration(existingContent, laravelVersion);
|
|
1861
|
+
if (registration && !registration.alreadyRegistered) {
|
|
1862
|
+
outputs.push({
|
|
1863
|
+
path: registration.path,
|
|
1864
|
+
content: registration.content,
|
|
1865
|
+
type: "other",
|
|
1866
|
+
skipIfExists: false,
|
|
1867
|
+
// We want to modify the file
|
|
1868
|
+
metadata: {
|
|
1869
|
+
registrationType: "provider-registration",
|
|
1870
|
+
laravelVersion: registration.laravelVersion
|
|
1871
|
+
}
|
|
1872
|
+
});
|
|
1873
|
+
ctx.logger.info(`OmnifyServiceProvider will be registered in ${registration.path}`);
|
|
1874
|
+
} else if (registration?.alreadyRegistered) {
|
|
1875
|
+
ctx.logger.info("OmnifyServiceProvider is already registered");
|
|
1876
|
+
}
|
|
1877
|
+
return outputs;
|
|
1878
|
+
}
|
|
1879
|
+
};
|
|
1880
|
+
const factoryGenerator = {
|
|
1881
|
+
name: "laravel-factories",
|
|
1882
|
+
description: "Generate Laravel factory classes for testing",
|
|
1883
|
+
generate: async (ctx) => {
|
|
1884
|
+
const factoryOptions = {
|
|
1885
|
+
modelNamespace: resolved.modelNamespace,
|
|
1886
|
+
factoryPath: resolved.factoriesPath,
|
|
1887
|
+
fakerLocale: resolved.fakerLocale
|
|
1888
|
+
};
|
|
1889
|
+
const factories = generateFactories(ctx.schemas, factoryOptions);
|
|
1890
|
+
return factories.map((factory) => ({
|
|
1891
|
+
path: getFactoryPath(factory),
|
|
1892
|
+
content: factory.content,
|
|
1893
|
+
type: "factory",
|
|
1894
|
+
// Skip writing factories if they already exist (allow customization)
|
|
1895
|
+
skipIfExists: !factory.overwrite,
|
|
1896
|
+
metadata: {
|
|
1897
|
+
factoryName: factory.name,
|
|
1898
|
+
schemaName: factory.schemaName
|
|
1899
|
+
}
|
|
1900
|
+
}));
|
|
1368
1901
|
}
|
|
1369
1902
|
};
|
|
1903
|
+
const generators = [migrationGenerator];
|
|
1904
|
+
if (resolved.generateModels) {
|
|
1905
|
+
generators.push(modelGenerator);
|
|
1906
|
+
}
|
|
1907
|
+
if (resolved.generateFactories) {
|
|
1908
|
+
generators.push(factoryGenerator);
|
|
1909
|
+
}
|
|
1370
1910
|
return {
|
|
1371
1911
|
name: "@famgia/omnify-laravel",
|
|
1372
1912
|
version: "0.0.14",
|
|
1373
1913
|
configSchema: LARAVEL_CONFIG_SCHEMA,
|
|
1374
|
-
generators
|
|
1914
|
+
generators
|
|
1375
1915
|
};
|
|
1376
1916
|
}
|
|
1377
1917
|
// Annotate the CommonJS export names for ESM import in node:
|