@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/index.cjs
CHANGED
|
@@ -121,9 +121,8 @@ function propertyToColumnMethod(propertyName, property) {
|
|
|
121
121
|
if (baseProp.unique) {
|
|
122
122
|
modifiers.push({ method: "unique" });
|
|
123
123
|
}
|
|
124
|
-
if (baseProp.default !== void 0) {
|
|
125
|
-
|
|
126
|
-
modifiers.push({ method: "default", args: [defaultValue] });
|
|
124
|
+
if (baseProp.default !== void 0 && baseProp.default !== null) {
|
|
125
|
+
modifiers.push({ method: "default", args: [baseProp.default] });
|
|
127
126
|
}
|
|
128
127
|
if (baseProp.unsigned && (method === "integer" || method === "bigInteger")) {
|
|
129
128
|
modifiers.push({ method: "unsigned" });
|
|
@@ -255,11 +254,18 @@ function generateForeignKey(propertyName, property, allSchemas) {
|
|
|
255
254
|
} else if (targetPkType === "String") {
|
|
256
255
|
method = "string";
|
|
257
256
|
}
|
|
257
|
+
const modifiers = [];
|
|
258
|
+
if (assocProp.nullable || assocProp.relation === "ManyToOne") {
|
|
259
|
+
modifiers.push({ method: "nullable" });
|
|
260
|
+
}
|
|
261
|
+
if (assocProp.default !== void 0 && assocProp.default !== null) {
|
|
262
|
+
modifiers.push({ method: "default", args: [assocProp.default] });
|
|
263
|
+
}
|
|
258
264
|
const column = {
|
|
259
265
|
name: columnName,
|
|
260
266
|
method,
|
|
261
267
|
args: [columnName],
|
|
262
|
-
modifiers
|
|
268
|
+
modifiers
|
|
263
269
|
};
|
|
264
270
|
const foreignKey = {
|
|
265
271
|
columns: [columnName],
|
|
@@ -359,6 +365,12 @@ function formatColumnMethod(column) {
|
|
|
359
365
|
if (typeof arg === "string") {
|
|
360
366
|
return `'${arg}'`;
|
|
361
367
|
}
|
|
368
|
+
if (typeof arg === "boolean") {
|
|
369
|
+
return arg ? "true" : "false";
|
|
370
|
+
}
|
|
371
|
+
if (typeof arg === "number") {
|
|
372
|
+
return String(arg);
|
|
373
|
+
}
|
|
362
374
|
return String(arg);
|
|
363
375
|
}).join(", ");
|
|
364
376
|
code += `->${modifier.method}(${modArgs})`;
|
|
@@ -466,6 +478,7 @@ function generatePivotTableBlueprint(pivot) {
|
|
|
466
478
|
args: [pivot.targetColumn],
|
|
467
479
|
modifiers: []
|
|
468
480
|
});
|
|
481
|
+
columns.push(...generateTimestampColumns());
|
|
469
482
|
foreignKeys.push({
|
|
470
483
|
columns: [pivot.sourceColumn],
|
|
471
484
|
references: "id",
|
|
@@ -1040,6 +1053,10 @@ function generateMigrationsFromChanges(changes, options = {}) {
|
|
|
1040
1053
|
return migrations;
|
|
1041
1054
|
}
|
|
1042
1055
|
|
|
1056
|
+
// src/plugin.ts
|
|
1057
|
+
var import_node_fs = require("fs");
|
|
1058
|
+
var import_node_path = require("path");
|
|
1059
|
+
|
|
1043
1060
|
// src/utils.ts
|
|
1044
1061
|
function toSnakeCase(str) {
|
|
1045
1062
|
return str.replace(/([A-Z])/g, "_$1").replace(/^_/, "").toLowerCase();
|
|
@@ -1195,7 +1212,7 @@ function generateEntityBaseModel(schema, schemas, options, stubContent, authStub
|
|
|
1195
1212
|
if (assoc.target) {
|
|
1196
1213
|
imports.push(`use ${options.modelNamespace}\\${toPascalCase(assoc.target)};`);
|
|
1197
1214
|
}
|
|
1198
|
-
relations.push(generateRelation(propName, assoc, options));
|
|
1215
|
+
relations.push(generateRelation(propName, assoc, schema, schemas, options));
|
|
1199
1216
|
if (assoc.relation === "ManyToOne" || assoc.relation === "OneToOne") {
|
|
1200
1217
|
if (!assoc.mappedBy) {
|
|
1201
1218
|
const fkName = toSnakeCase(propName) + "_id";
|
|
@@ -1252,7 +1269,22 @@ ${docProperties.join("\n")}
|
|
|
1252
1269
|
schemaName: schema.name
|
|
1253
1270
|
};
|
|
1254
1271
|
}
|
|
1255
|
-
function
|
|
1272
|
+
function findInverseRelation(currentSchemaName, targetSchemaName, schemas) {
|
|
1273
|
+
const targetSchema = schemas[targetSchemaName];
|
|
1274
|
+
if (!targetSchema || !targetSchema.properties) {
|
|
1275
|
+
return null;
|
|
1276
|
+
}
|
|
1277
|
+
for (const [propName, propDef] of Object.entries(targetSchema.properties)) {
|
|
1278
|
+
if (propDef.type === "Association") {
|
|
1279
|
+
const assoc = propDef;
|
|
1280
|
+
if (assoc.relation === "ManyToOne" && assoc.target === currentSchemaName) {
|
|
1281
|
+
return propName;
|
|
1282
|
+
}
|
|
1283
|
+
}
|
|
1284
|
+
}
|
|
1285
|
+
return null;
|
|
1286
|
+
}
|
|
1287
|
+
function generateRelation(propName, assoc, schema, schemas, options) {
|
|
1256
1288
|
const methodName = toCamelCase(propName);
|
|
1257
1289
|
const targetClass = assoc.target ? toPascalCase(assoc.target) : "";
|
|
1258
1290
|
const fkName = toSnakeCase(propName) + "_id";
|
|
@@ -1282,14 +1314,28 @@ function generateRelation(propName, assoc, options) {
|
|
|
1282
1314
|
{
|
|
1283
1315
|
return $this->belongsTo(${targetClass}::class, '${fkName}');
|
|
1284
1316
|
}`;
|
|
1285
|
-
case "OneToMany":
|
|
1317
|
+
case "OneToMany": {
|
|
1318
|
+
let foreignKey;
|
|
1319
|
+
if (assoc.inversedBy) {
|
|
1320
|
+
foreignKey = toSnakeCase(assoc.inversedBy) + "_id";
|
|
1321
|
+
} else if (assoc.target) {
|
|
1322
|
+
const inverseRelation = findInverseRelation(schema.name, assoc.target, schemas);
|
|
1323
|
+
if (inverseRelation) {
|
|
1324
|
+
foreignKey = toSnakeCase(inverseRelation) + "_id";
|
|
1325
|
+
} else {
|
|
1326
|
+
foreignKey = toSnakeCase(schema.name) + "_id";
|
|
1327
|
+
}
|
|
1328
|
+
} else {
|
|
1329
|
+
foreignKey = toSnakeCase(propName) + "_id";
|
|
1330
|
+
}
|
|
1286
1331
|
return ` /**
|
|
1287
1332
|
* Get the ${propName} for this model.
|
|
1288
1333
|
*/
|
|
1289
1334
|
public function ${methodName}(): HasMany
|
|
1290
1335
|
{
|
|
1291
|
-
return $this->hasMany(${targetClass}::class, '${
|
|
1336
|
+
return $this->hasMany(${targetClass}::class, '${foreignKey}');
|
|
1292
1337
|
}`;
|
|
1338
|
+
}
|
|
1293
1339
|
case "ManyToMany": {
|
|
1294
1340
|
const pivotTable = assoc.joinTable ?? `${toSnakeCase(propName)}_pivot`;
|
|
1295
1341
|
return ` /**
|
|
@@ -1586,14 +1632,73 @@ class {{CLASS_NAME}} extends {{CLASS_NAME}}BaseModel
|
|
|
1586
1632
|
|
|
1587
1633
|
// Add your custom methods here
|
|
1588
1634
|
}
|
|
1635
|
+
`,
|
|
1636
|
+
"service-provider": `<?php
|
|
1637
|
+
|
|
1638
|
+
namespace App\\Providers;
|
|
1639
|
+
|
|
1640
|
+
use Illuminate\\Database\\Eloquent\\Relations\\Relation;
|
|
1641
|
+
use Illuminate\\Support\\ServiceProvider;
|
|
1642
|
+
|
|
1643
|
+
/**
|
|
1644
|
+
* Omnify Service Provider
|
|
1645
|
+
*
|
|
1646
|
+
* DO NOT EDIT - This file is auto-generated by Omnify.
|
|
1647
|
+
* Any changes will be overwritten on next generation.
|
|
1648
|
+
*
|
|
1649
|
+
* - Loads Omnify migrations from database/migrations/omnify
|
|
1650
|
+
* - Registers morph map for polymorphic relationships
|
|
1651
|
+
*
|
|
1652
|
+
* @generated by @famgia/omnify-laravel
|
|
1653
|
+
*/
|
|
1654
|
+
class OmnifyServiceProvider extends ServiceProvider
|
|
1655
|
+
{
|
|
1656
|
+
/**
|
|
1657
|
+
* Register any application services.
|
|
1658
|
+
*/
|
|
1659
|
+
public function register(): void
|
|
1660
|
+
{
|
|
1661
|
+
//
|
|
1662
|
+
}
|
|
1663
|
+
|
|
1664
|
+
/**
|
|
1665
|
+
* Bootstrap any application services.
|
|
1666
|
+
*/
|
|
1667
|
+
public function boot(): void
|
|
1668
|
+
{
|
|
1669
|
+
// Load Omnify migrations from custom directory
|
|
1670
|
+
$this->loadMigrationsFrom(database_path('migrations/omnify'));
|
|
1671
|
+
|
|
1672
|
+
// Register morph map for polymorphic relationships
|
|
1673
|
+
Relation::enforceMorphMap([
|
|
1674
|
+
{{MORPH_MAP}}
|
|
1675
|
+
]);
|
|
1676
|
+
}
|
|
1677
|
+
}
|
|
1589
1678
|
`
|
|
1590
1679
|
};
|
|
1591
1680
|
return stubs[stubName] ?? "";
|
|
1592
1681
|
}
|
|
1682
|
+
function generateServiceProvider(schemas, options, stubContent) {
|
|
1683
|
+
const morphMap = Object.values(schemas).filter((s) => s.kind !== "enum").map((s) => {
|
|
1684
|
+
const className = toPascalCase(s.name);
|
|
1685
|
+
return ` '${s.name}' => \\${options.modelNamespace}\\${className}::class,`;
|
|
1686
|
+
}).join("\n");
|
|
1687
|
+
const content = stubContent.replace(/\{\{MORPH_MAP\}\}/g, morphMap);
|
|
1688
|
+
return {
|
|
1689
|
+
path: "app/Providers/OmnifyServiceProvider.php",
|
|
1690
|
+
content,
|
|
1691
|
+
type: "service-provider",
|
|
1692
|
+
overwrite: true,
|
|
1693
|
+
// Always overwrite to keep morph map in sync
|
|
1694
|
+
schemaName: "__service_provider__"
|
|
1695
|
+
};
|
|
1696
|
+
}
|
|
1593
1697
|
function generateModels(schemas, options) {
|
|
1594
1698
|
const resolved = resolveOptions(options);
|
|
1595
1699
|
const models = [];
|
|
1596
1700
|
models.push(generateBaseModel(schemas, resolved, getStubContent("base-model")));
|
|
1701
|
+
models.push(generateServiceProvider(schemas, resolved, getStubContent("service-provider")));
|
|
1597
1702
|
for (const schema of Object.values(schemas)) {
|
|
1598
1703
|
if (schema.kind === "enum") {
|
|
1599
1704
|
continue;
|
|
@@ -1612,6 +1717,355 @@ function generateModels(schemas, options) {
|
|
|
1612
1717
|
function getModelPath(model) {
|
|
1613
1718
|
return model.path;
|
|
1614
1719
|
}
|
|
1720
|
+
function generateProviderRegistration(existingContent, laravelVersion) {
|
|
1721
|
+
const providerClass = "App\\Providers\\OmnifyServiceProvider::class";
|
|
1722
|
+
const providerLine = ` ${providerClass},`;
|
|
1723
|
+
if (existingContent && existingContent.includes("OmnifyServiceProvider")) {
|
|
1724
|
+
return {
|
|
1725
|
+
path: laravelVersion === "laravel11+" ? "bootstrap/providers.php" : "config/app.php",
|
|
1726
|
+
content: existingContent,
|
|
1727
|
+
laravelVersion,
|
|
1728
|
+
alreadyRegistered: true
|
|
1729
|
+
};
|
|
1730
|
+
}
|
|
1731
|
+
if (laravelVersion === "laravel11+") {
|
|
1732
|
+
if (existingContent) {
|
|
1733
|
+
const lines = existingContent.split("\n");
|
|
1734
|
+
const result = [];
|
|
1735
|
+
let inserted = false;
|
|
1736
|
+
for (let i = 0; i < lines.length; i++) {
|
|
1737
|
+
const line = lines[i];
|
|
1738
|
+
if (!inserted && line.trim() === "];") {
|
|
1739
|
+
result.push(providerLine);
|
|
1740
|
+
inserted = true;
|
|
1741
|
+
}
|
|
1742
|
+
result.push(line);
|
|
1743
|
+
}
|
|
1744
|
+
return {
|
|
1745
|
+
path: "bootstrap/providers.php",
|
|
1746
|
+
content: result.join("\n"),
|
|
1747
|
+
laravelVersion,
|
|
1748
|
+
alreadyRegistered: false
|
|
1749
|
+
};
|
|
1750
|
+
} else {
|
|
1751
|
+
return {
|
|
1752
|
+
path: "bootstrap/providers.php",
|
|
1753
|
+
content: `<?php
|
|
1754
|
+
|
|
1755
|
+
return [
|
|
1756
|
+
App\\Providers\\AppServiceProvider::class,
|
|
1757
|
+
${providerLine}
|
|
1758
|
+
];
|
|
1759
|
+
`,
|
|
1760
|
+
laravelVersion,
|
|
1761
|
+
alreadyRegistered: false
|
|
1762
|
+
};
|
|
1763
|
+
}
|
|
1764
|
+
} else {
|
|
1765
|
+
if (existingContent) {
|
|
1766
|
+
const providersSectionRegex = /'providers'\s*=>\s*\[[\s\S]*?\n(\s*)\]/m;
|
|
1767
|
+
const match = existingContent.match(providersSectionRegex);
|
|
1768
|
+
if (match) {
|
|
1769
|
+
const providersStart = existingContent.indexOf("'providers'");
|
|
1770
|
+
if (providersStart === -1) {
|
|
1771
|
+
return null;
|
|
1772
|
+
}
|
|
1773
|
+
let depth = 0;
|
|
1774
|
+
let foundStart = false;
|
|
1775
|
+
let insertPos = -1;
|
|
1776
|
+
for (let i = providersStart; i < existingContent.length; i++) {
|
|
1777
|
+
const char = existingContent[i];
|
|
1778
|
+
if (char === "[") {
|
|
1779
|
+
foundStart = true;
|
|
1780
|
+
depth++;
|
|
1781
|
+
} else if (char === "]") {
|
|
1782
|
+
depth--;
|
|
1783
|
+
if (foundStart && depth === 0) {
|
|
1784
|
+
insertPos = i;
|
|
1785
|
+
break;
|
|
1786
|
+
}
|
|
1787
|
+
}
|
|
1788
|
+
}
|
|
1789
|
+
if (insertPos !== -1) {
|
|
1790
|
+
const beforeClose = existingContent.substring(0, insertPos);
|
|
1791
|
+
const lastNewline = beforeClose.lastIndexOf("\n");
|
|
1792
|
+
const content = existingContent.substring(0, lastNewline + 1) + providerLine + "\n" + existingContent.substring(lastNewline + 1);
|
|
1793
|
+
return {
|
|
1794
|
+
path: "config/app.php",
|
|
1795
|
+
content,
|
|
1796
|
+
laravelVersion,
|
|
1797
|
+
alreadyRegistered: false
|
|
1798
|
+
};
|
|
1799
|
+
}
|
|
1800
|
+
}
|
|
1801
|
+
return null;
|
|
1802
|
+
} else {
|
|
1803
|
+
return null;
|
|
1804
|
+
}
|
|
1805
|
+
}
|
|
1806
|
+
}
|
|
1807
|
+
|
|
1808
|
+
// src/factory/generator.ts
|
|
1809
|
+
function resolveOptions2(options) {
|
|
1810
|
+
return {
|
|
1811
|
+
modelNamespace: options?.modelNamespace ?? "App\\Models",
|
|
1812
|
+
factoryPath: options?.factoryPath ?? "database/factories",
|
|
1813
|
+
fakerLocale: options?.fakerLocale ?? "en_US"
|
|
1814
|
+
};
|
|
1815
|
+
}
|
|
1816
|
+
function getStubContent2() {
|
|
1817
|
+
return `<?php
|
|
1818
|
+
|
|
1819
|
+
namespace Database\\Factories;
|
|
1820
|
+
|
|
1821
|
+
use {{MODEL_NAMESPACE}}\\{{MODEL_NAME}};
|
|
1822
|
+
use Illuminate\\Database\\Eloquent\\Factories\\Factory;
|
|
1823
|
+
{{IMPORTS}}
|
|
1824
|
+
|
|
1825
|
+
/**
|
|
1826
|
+
* @extends Factory<{{MODEL_NAME}}>
|
|
1827
|
+
*/
|
|
1828
|
+
class {{MODEL_NAME}}Factory extends Factory
|
|
1829
|
+
{
|
|
1830
|
+
protected $model = {{MODEL_NAME}}::class;
|
|
1831
|
+
|
|
1832
|
+
/**
|
|
1833
|
+
* Define the model's default state.
|
|
1834
|
+
*
|
|
1835
|
+
* @return array<string, mixed>
|
|
1836
|
+
*/
|
|
1837
|
+
public function definition(): array
|
|
1838
|
+
{
|
|
1839
|
+
return [
|
|
1840
|
+
{{ATTRIBUTES}}
|
|
1841
|
+
];
|
|
1842
|
+
}
|
|
1843
|
+
}
|
|
1844
|
+
`;
|
|
1845
|
+
}
|
|
1846
|
+
function generateFakeData(propertyName, property, schema, schemas) {
|
|
1847
|
+
const type = property.type;
|
|
1848
|
+
if (["deleted_at", "created_at", "updated_at"].includes(propertyName)) {
|
|
1849
|
+
return null;
|
|
1850
|
+
}
|
|
1851
|
+
if (type === "Association") {
|
|
1852
|
+
return null;
|
|
1853
|
+
}
|
|
1854
|
+
switch (type) {
|
|
1855
|
+
case "String":
|
|
1856
|
+
return generateStringFake(propertyName, property);
|
|
1857
|
+
case "Email":
|
|
1858
|
+
return `'${propertyName}' => fake()->unique()->safeEmail(),`;
|
|
1859
|
+
case "Password":
|
|
1860
|
+
return `'${propertyName}' => bcrypt('password'),`;
|
|
1861
|
+
case "Int":
|
|
1862
|
+
case "BigInt":
|
|
1863
|
+
return generateIntFake(propertyName, property);
|
|
1864
|
+
case "Float":
|
|
1865
|
+
case "Decimal":
|
|
1866
|
+
return `'${propertyName}' => fake()->randomFloat(2, 1, 10000),`;
|
|
1867
|
+
case "Boolean":
|
|
1868
|
+
return `'${propertyName}' => fake()->boolean(),`;
|
|
1869
|
+
case "Text":
|
|
1870
|
+
return `'${propertyName}' => fake()->paragraphs(3, true),`;
|
|
1871
|
+
case "LongText":
|
|
1872
|
+
return `'${propertyName}' => fake()->paragraphs(5, true),`;
|
|
1873
|
+
case "Date":
|
|
1874
|
+
return `'${propertyName}' => fake()->date(),`;
|
|
1875
|
+
case "Time":
|
|
1876
|
+
return `'${propertyName}' => fake()->time(),`;
|
|
1877
|
+
case "Timestamp":
|
|
1878
|
+
case "DateTime":
|
|
1879
|
+
return `'${propertyName}' => fake()->dateTime(),`;
|
|
1880
|
+
case "Json":
|
|
1881
|
+
return `'${propertyName}' => [],`;
|
|
1882
|
+
case "Enum":
|
|
1883
|
+
return generateEnumFake(propertyName, property);
|
|
1884
|
+
case "EnumRef":
|
|
1885
|
+
return generateEnumRefFake(propertyName, property, schemas);
|
|
1886
|
+
default:
|
|
1887
|
+
return `'${propertyName}' => fake()->sentence(),`;
|
|
1888
|
+
}
|
|
1889
|
+
}
|
|
1890
|
+
function generateStringFake(propertyName, property) {
|
|
1891
|
+
if (propertyName === "slug") {
|
|
1892
|
+
return `'${propertyName}' => fake()->unique()->slug(),`;
|
|
1893
|
+
}
|
|
1894
|
+
if (propertyName === "uuid" || propertyName === "uid") {
|
|
1895
|
+
return `'${propertyName}' => (string) \\Illuminate\\Support\\Str::uuid(),`;
|
|
1896
|
+
}
|
|
1897
|
+
if (propertyName.includes("email")) {
|
|
1898
|
+
return `'${propertyName}' => fake()->unique()->safeEmail(),`;
|
|
1899
|
+
}
|
|
1900
|
+
if (propertyName.includes("phone")) {
|
|
1901
|
+
return `'${propertyName}' => fake()->phoneNumber(),`;
|
|
1902
|
+
}
|
|
1903
|
+
if (propertyName.includes("image") || propertyName.includes("photo") || propertyName.includes("avatar")) {
|
|
1904
|
+
return `'${propertyName}' => fake()->imageUrl(),`;
|
|
1905
|
+
}
|
|
1906
|
+
if (propertyName.includes("url") || propertyName.includes("website")) {
|
|
1907
|
+
return `'${propertyName}' => fake()->url(),`;
|
|
1908
|
+
}
|
|
1909
|
+
if (propertyName.includes("path") || propertyName.includes("file")) {
|
|
1910
|
+
return `'${propertyName}' => 'uploads/' . fake()->uuid() . '.jpg',`;
|
|
1911
|
+
}
|
|
1912
|
+
if (propertyName === "name" || propertyName === "title") {
|
|
1913
|
+
return `'${propertyName}' => fake()->sentence(3),`;
|
|
1914
|
+
}
|
|
1915
|
+
if (propertyName.includes("name")) {
|
|
1916
|
+
return `'${propertyName}' => fake()->name(),`;
|
|
1917
|
+
}
|
|
1918
|
+
if (propertyName.includes("address")) {
|
|
1919
|
+
return `'${propertyName}' => fake()->address(),`;
|
|
1920
|
+
}
|
|
1921
|
+
if (propertyName.includes("city")) {
|
|
1922
|
+
return `'${propertyName}' => fake()->city(),`;
|
|
1923
|
+
}
|
|
1924
|
+
if (propertyName.includes("country")) {
|
|
1925
|
+
return `'${propertyName}' => fake()->country(),`;
|
|
1926
|
+
}
|
|
1927
|
+
if (propertyName.includes("zip") || propertyName.includes("postal")) {
|
|
1928
|
+
return `'${propertyName}' => fake()->postcode(),`;
|
|
1929
|
+
}
|
|
1930
|
+
if (propertyName.includes("color")) {
|
|
1931
|
+
return `'${propertyName}' => fake()->hexColor(),`;
|
|
1932
|
+
}
|
|
1933
|
+
if (propertyName.includes("token") || propertyName.includes("secret") || propertyName.includes("key")) {
|
|
1934
|
+
return `'${propertyName}' => \\Illuminate\\Support\\Str::random(32),`;
|
|
1935
|
+
}
|
|
1936
|
+
if (propertyName.includes("code")) {
|
|
1937
|
+
return `'${propertyName}' => fake()->unique()->regexify('[A-Z0-9]{8}'),`;
|
|
1938
|
+
}
|
|
1939
|
+
const length = property.length;
|
|
1940
|
+
if (length && length <= 50) {
|
|
1941
|
+
return `'${propertyName}' => fake()->words(3, true),`;
|
|
1942
|
+
}
|
|
1943
|
+
return `'${propertyName}' => fake()->sentence(),`;
|
|
1944
|
+
}
|
|
1945
|
+
function generateIntFake(propertyName, property) {
|
|
1946
|
+
if (propertyName.includes("count") || propertyName.includes("quantity")) {
|
|
1947
|
+
return `'${propertyName}' => fake()->numberBetween(0, 100),`;
|
|
1948
|
+
}
|
|
1949
|
+
if (propertyName.includes("price") || propertyName.includes("amount") || propertyName.includes("cost")) {
|
|
1950
|
+
return `'${propertyName}' => fake()->numberBetween(100, 10000),`;
|
|
1951
|
+
}
|
|
1952
|
+
if (propertyName.includes("order") || propertyName.includes("sort") || propertyName.includes("position")) {
|
|
1953
|
+
return `'${propertyName}' => fake()->numberBetween(1, 100),`;
|
|
1954
|
+
}
|
|
1955
|
+
if (propertyName.includes("age")) {
|
|
1956
|
+
return `'${propertyName}' => fake()->numberBetween(18, 80),`;
|
|
1957
|
+
}
|
|
1958
|
+
if (propertyName.includes("year")) {
|
|
1959
|
+
return `'${propertyName}' => fake()->year(),`;
|
|
1960
|
+
}
|
|
1961
|
+
return `'${propertyName}' => fake()->numberBetween(1, 1000),`;
|
|
1962
|
+
}
|
|
1963
|
+
function generateEnumFake(propertyName, property) {
|
|
1964
|
+
const enumValues = property.enum;
|
|
1965
|
+
if (!enumValues || enumValues.length === 0) {
|
|
1966
|
+
return `'${propertyName}' => null,`;
|
|
1967
|
+
}
|
|
1968
|
+
const values = enumValues.map((v) => typeof v === "string" ? v : v.value);
|
|
1969
|
+
const valuesStr = values.map((v) => `'${v}'`).join(", ");
|
|
1970
|
+
return `'${propertyName}' => fake()->randomElement([${valuesStr}]),`;
|
|
1971
|
+
}
|
|
1972
|
+
function generateEnumRefFake(propertyName, property, schemas) {
|
|
1973
|
+
const enumName = property.enum;
|
|
1974
|
+
if (!enumName) {
|
|
1975
|
+
return `'${propertyName}' => null,`;
|
|
1976
|
+
}
|
|
1977
|
+
const enumSchema = schemas[enumName];
|
|
1978
|
+
if (!enumSchema || enumSchema.kind !== "enum" || !enumSchema.values) {
|
|
1979
|
+
return `'${propertyName}' => null,`;
|
|
1980
|
+
}
|
|
1981
|
+
const valuesStr = enumSchema.values.map((v) => `'${v}'`).join(", ");
|
|
1982
|
+
return `'${propertyName}' => fake()->randomElement([${valuesStr}]),`;
|
|
1983
|
+
}
|
|
1984
|
+
function generateAssociationFake(propertyName, property, schema, schemas, modelNamespace) {
|
|
1985
|
+
if (property.type !== "Association") {
|
|
1986
|
+
return null;
|
|
1987
|
+
}
|
|
1988
|
+
const relation = property.relation;
|
|
1989
|
+
const target = property.target;
|
|
1990
|
+
if (relation !== "ManyToOne" || !target) {
|
|
1991
|
+
return null;
|
|
1992
|
+
}
|
|
1993
|
+
const foreignKey = `${toSnakeCase(propertyName)}_id`;
|
|
1994
|
+
const isNullable2 = property.nullable ?? false;
|
|
1995
|
+
const targetSchema = schemas[target];
|
|
1996
|
+
if (!targetSchema) {
|
|
1997
|
+
return null;
|
|
1998
|
+
}
|
|
1999
|
+
let fake;
|
|
2000
|
+
if (isNullable2) {
|
|
2001
|
+
fake = `'${foreignKey}' => ${target}::query()->inRandomOrder()->first()?->id,`;
|
|
2002
|
+
} else {
|
|
2003
|
+
fake = `'${foreignKey}' => ${target}::query()->inRandomOrder()->first()?->id ?? ${target}::factory()->create()->id,`;
|
|
2004
|
+
}
|
|
2005
|
+
let importStatement;
|
|
2006
|
+
if (target !== schema.name) {
|
|
2007
|
+
importStatement = `use ${modelNamespace}\\${target};`;
|
|
2008
|
+
}
|
|
2009
|
+
return { fake, import: importStatement };
|
|
2010
|
+
}
|
|
2011
|
+
function generateFactory(schema, schemas, options, stubContent) {
|
|
2012
|
+
if (schema.kind === "enum") {
|
|
2013
|
+
return null;
|
|
2014
|
+
}
|
|
2015
|
+
const modelName = toPascalCase(schema.name);
|
|
2016
|
+
const factoryName = `${modelName}Factory`;
|
|
2017
|
+
const attributes = [];
|
|
2018
|
+
const imports = [];
|
|
2019
|
+
if (schema.properties) {
|
|
2020
|
+
for (const [propName, prop] of Object.entries(schema.properties)) {
|
|
2021
|
+
if (prop.type === "Association") {
|
|
2022
|
+
const assocResult = generateAssociationFake(propName, prop, schema, schemas, options.modelNamespace);
|
|
2023
|
+
if (assocResult) {
|
|
2024
|
+
attributes.push(assocResult.fake);
|
|
2025
|
+
if (assocResult.import) {
|
|
2026
|
+
imports.push(assocResult.import);
|
|
2027
|
+
}
|
|
2028
|
+
}
|
|
2029
|
+
continue;
|
|
2030
|
+
}
|
|
2031
|
+
const fake = generateFakeData(propName, prop, schema, schemas);
|
|
2032
|
+
if (fake) {
|
|
2033
|
+
attributes.push(fake);
|
|
2034
|
+
}
|
|
2035
|
+
}
|
|
2036
|
+
}
|
|
2037
|
+
let content = stubContent;
|
|
2038
|
+
content = content.replace(/\{\{MODEL_NAMESPACE\}\}/g, options.modelNamespace);
|
|
2039
|
+
content = content.replace(/\{\{MODEL_NAME\}\}/g, modelName);
|
|
2040
|
+
const uniqueImports = [...new Set(imports)];
|
|
2041
|
+
const importsStr = uniqueImports.length > 0 ? "\n" + uniqueImports.join("\n") : "";
|
|
2042
|
+
content = content.replace(/\{\{IMPORTS\}\}/g, importsStr);
|
|
2043
|
+
const attributesStr = attributes.length > 0 ? attributes.map((a) => ` ${a}`).join("\n") : "";
|
|
2044
|
+
content = content.replace(/\{\{ATTRIBUTES\}\}/g, attributesStr);
|
|
2045
|
+
return {
|
|
2046
|
+
name: factoryName,
|
|
2047
|
+
schemaName: schema.name,
|
|
2048
|
+
path: `${options.factoryPath}/${factoryName}.php`,
|
|
2049
|
+
content,
|
|
2050
|
+
overwrite: false
|
|
2051
|
+
// Factories should not overwrite existing files
|
|
2052
|
+
};
|
|
2053
|
+
}
|
|
2054
|
+
function generateFactories(schemas, options) {
|
|
2055
|
+
const resolved = resolveOptions2(options);
|
|
2056
|
+
const stubContent = getStubContent2();
|
|
2057
|
+
const factories = [];
|
|
2058
|
+
for (const schema of Object.values(schemas)) {
|
|
2059
|
+
const factory = generateFactory(schema, schemas, resolved, stubContent);
|
|
2060
|
+
if (factory) {
|
|
2061
|
+
factories.push(factory);
|
|
2062
|
+
}
|
|
2063
|
+
}
|
|
2064
|
+
return factories;
|
|
2065
|
+
}
|
|
2066
|
+
function getFactoryPath(factory) {
|
|
2067
|
+
return factory.path;
|
|
2068
|
+
}
|
|
1615
2069
|
|
|
1616
2070
|
// src/plugin.ts
|
|
1617
2071
|
var LARAVEL_CONFIG_SCHEMA = {
|
|
@@ -1620,8 +2074,8 @@ var LARAVEL_CONFIG_SCHEMA = {
|
|
|
1620
2074
|
key: "migrationsPath",
|
|
1621
2075
|
type: "path",
|
|
1622
2076
|
label: "Migrations Path",
|
|
1623
|
-
description: "Directory for Laravel migration files",
|
|
1624
|
-
default: "database/migrations",
|
|
2077
|
+
description: "Directory for Laravel migration files (loaded via OmnifyServiceProvider)",
|
|
2078
|
+
default: "database/migrations/omnify",
|
|
1625
2079
|
group: "output"
|
|
1626
2080
|
},
|
|
1627
2081
|
{
|
|
@@ -1648,6 +2102,22 @@ var LARAVEL_CONFIG_SCHEMA = {
|
|
|
1648
2102
|
default: true,
|
|
1649
2103
|
group: "options"
|
|
1650
2104
|
},
|
|
2105
|
+
{
|
|
2106
|
+
key: "factoriesPath",
|
|
2107
|
+
type: "path",
|
|
2108
|
+
label: "Factories Path",
|
|
2109
|
+
description: "Directory for Laravel factory files",
|
|
2110
|
+
default: "database/factories",
|
|
2111
|
+
group: "output"
|
|
2112
|
+
},
|
|
2113
|
+
{
|
|
2114
|
+
key: "generateFactories",
|
|
2115
|
+
type: "boolean",
|
|
2116
|
+
label: "Generate Factories",
|
|
2117
|
+
description: "Generate Laravel factory classes for testing",
|
|
2118
|
+
default: true,
|
|
2119
|
+
group: "options"
|
|
2120
|
+
},
|
|
1651
2121
|
{
|
|
1652
2122
|
key: "connection",
|
|
1653
2123
|
type: "string",
|
|
@@ -1658,20 +2128,23 @@ var LARAVEL_CONFIG_SCHEMA = {
|
|
|
1658
2128
|
}
|
|
1659
2129
|
]
|
|
1660
2130
|
};
|
|
1661
|
-
function
|
|
2131
|
+
function resolveOptions3(options) {
|
|
1662
2132
|
return {
|
|
1663
|
-
migrationsPath: options?.migrationsPath ?? "database/migrations",
|
|
2133
|
+
migrationsPath: options?.migrationsPath ?? "database/migrations/omnify",
|
|
1664
2134
|
modelsPath: options?.modelsPath ?? "app/Models",
|
|
1665
2135
|
baseModelsPath: options?.baseModelsPath ?? "app/Models/OmnifyBase",
|
|
1666
2136
|
modelNamespace: options?.modelNamespace ?? "App\\Models",
|
|
1667
2137
|
baseModelNamespace: options?.baseModelNamespace ?? "App\\Models\\OmnifyBase",
|
|
1668
2138
|
generateModels: options?.generateModels ?? true,
|
|
2139
|
+
factoriesPath: options?.factoriesPath ?? "database/factories",
|
|
2140
|
+
generateFactories: options?.generateFactories ?? true,
|
|
2141
|
+
fakerLocale: options?.fakerLocale ?? "en_US",
|
|
1669
2142
|
connection: options?.connection,
|
|
1670
2143
|
timestamp: options?.timestamp
|
|
1671
2144
|
};
|
|
1672
2145
|
}
|
|
1673
2146
|
function laravelPlugin(options) {
|
|
1674
|
-
const resolved =
|
|
2147
|
+
const resolved = resolveOptions3(options);
|
|
1675
2148
|
const migrationGenerator = {
|
|
1676
2149
|
name: "laravel-migrations",
|
|
1677
2150
|
description: "Generate Laravel migration files",
|
|
@@ -1703,7 +2176,7 @@ function laravelPlugin(options) {
|
|
|
1703
2176
|
baseModelPath: resolved.baseModelsPath
|
|
1704
2177
|
};
|
|
1705
2178
|
const models = generateModels(ctx.schemas, modelOptions);
|
|
1706
|
-
|
|
2179
|
+
const outputs = models.map((model) => ({
|
|
1707
2180
|
path: getModelPath(model),
|
|
1708
2181
|
content: model.content,
|
|
1709
2182
|
type: "model",
|
|
@@ -1714,13 +2187,82 @@ function laravelPlugin(options) {
|
|
|
1714
2187
|
schemaName: model.schemaName
|
|
1715
2188
|
}
|
|
1716
2189
|
}));
|
|
2190
|
+
const bootstrapProvidersPath = (0, import_node_path.join)(ctx.cwd, "bootstrap/providers.php");
|
|
2191
|
+
const configAppPath = (0, import_node_path.join)(ctx.cwd, "config/app.php");
|
|
2192
|
+
let existingContent = null;
|
|
2193
|
+
let laravelVersion;
|
|
2194
|
+
if ((0, import_node_fs.existsSync)(bootstrapProvidersPath)) {
|
|
2195
|
+
laravelVersion = "laravel11+";
|
|
2196
|
+
try {
|
|
2197
|
+
existingContent = (0, import_node_fs.readFileSync)(bootstrapProvidersPath, "utf-8");
|
|
2198
|
+
} catch {
|
|
2199
|
+
existingContent = null;
|
|
2200
|
+
}
|
|
2201
|
+
} else if ((0, import_node_fs.existsSync)(configAppPath)) {
|
|
2202
|
+
laravelVersion = "laravel10-";
|
|
2203
|
+
try {
|
|
2204
|
+
existingContent = (0, import_node_fs.readFileSync)(configAppPath, "utf-8");
|
|
2205
|
+
} catch {
|
|
2206
|
+
existingContent = null;
|
|
2207
|
+
}
|
|
2208
|
+
} else {
|
|
2209
|
+
laravelVersion = "laravel11+";
|
|
2210
|
+
}
|
|
2211
|
+
const registration = generateProviderRegistration(existingContent, laravelVersion);
|
|
2212
|
+
if (registration && !registration.alreadyRegistered) {
|
|
2213
|
+
outputs.push({
|
|
2214
|
+
path: registration.path,
|
|
2215
|
+
content: registration.content,
|
|
2216
|
+
type: "other",
|
|
2217
|
+
skipIfExists: false,
|
|
2218
|
+
// We want to modify the file
|
|
2219
|
+
metadata: {
|
|
2220
|
+
registrationType: "provider-registration",
|
|
2221
|
+
laravelVersion: registration.laravelVersion
|
|
2222
|
+
}
|
|
2223
|
+
});
|
|
2224
|
+
ctx.logger.info(`OmnifyServiceProvider will be registered in ${registration.path}`);
|
|
2225
|
+
} else if (registration?.alreadyRegistered) {
|
|
2226
|
+
ctx.logger.info("OmnifyServiceProvider is already registered");
|
|
2227
|
+
}
|
|
2228
|
+
return outputs;
|
|
2229
|
+
}
|
|
2230
|
+
};
|
|
2231
|
+
const factoryGenerator = {
|
|
2232
|
+
name: "laravel-factories",
|
|
2233
|
+
description: "Generate Laravel factory classes for testing",
|
|
2234
|
+
generate: async (ctx) => {
|
|
2235
|
+
const factoryOptions = {
|
|
2236
|
+
modelNamespace: resolved.modelNamespace,
|
|
2237
|
+
factoryPath: resolved.factoriesPath,
|
|
2238
|
+
fakerLocale: resolved.fakerLocale
|
|
2239
|
+
};
|
|
2240
|
+
const factories = generateFactories(ctx.schemas, factoryOptions);
|
|
2241
|
+
return factories.map((factory) => ({
|
|
2242
|
+
path: getFactoryPath(factory),
|
|
2243
|
+
content: factory.content,
|
|
2244
|
+
type: "factory",
|
|
2245
|
+
// Skip writing factories if they already exist (allow customization)
|
|
2246
|
+
skipIfExists: !factory.overwrite,
|
|
2247
|
+
metadata: {
|
|
2248
|
+
factoryName: factory.name,
|
|
2249
|
+
schemaName: factory.schemaName
|
|
2250
|
+
}
|
|
2251
|
+
}));
|
|
1717
2252
|
}
|
|
1718
2253
|
};
|
|
2254
|
+
const generators = [migrationGenerator];
|
|
2255
|
+
if (resolved.generateModels) {
|
|
2256
|
+
generators.push(modelGenerator);
|
|
2257
|
+
}
|
|
2258
|
+
if (resolved.generateFactories) {
|
|
2259
|
+
generators.push(factoryGenerator);
|
|
2260
|
+
}
|
|
1719
2261
|
return {
|
|
1720
2262
|
name: "@famgia/omnify-laravel",
|
|
1721
2263
|
version: "0.0.14",
|
|
1722
2264
|
configSchema: LARAVEL_CONFIG_SCHEMA,
|
|
1723
|
-
generators
|
|
2265
|
+
generators
|
|
1724
2266
|
};
|
|
1725
2267
|
}
|
|
1726
2268
|
// Annotate the CommonJS export names for ESM import in node:
|