@omnifyjp/omnify 0.2.2 → 0.2.4

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.
Files changed (34) hide show
  1. package/README.md +38 -25
  2. package/package.json +6 -6
  3. package/ts-dist/cli.js +108 -61
  4. package/ts-dist/php/base-model-generator.d.ts +7 -0
  5. package/ts-dist/php/base-model-generator.js +70 -0
  6. package/ts-dist/php/factory-generator.d.ts +7 -0
  7. package/ts-dist/php/factory-generator.js +95 -0
  8. package/ts-dist/php/faker-mapper.d.ts +12 -0
  9. package/ts-dist/php/faker-mapper.js +206 -0
  10. package/ts-dist/php/index.d.ts +18 -0
  11. package/ts-dist/php/index.js +40 -0
  12. package/ts-dist/php/locales-generator.d.ts +7 -0
  13. package/ts-dist/php/locales-generator.js +135 -0
  14. package/ts-dist/php/model-generator.d.ts +7 -0
  15. package/ts-dist/php/model-generator.js +396 -0
  16. package/ts-dist/php/naming-helper.d.ts +22 -0
  17. package/ts-dist/php/naming-helper.js +61 -0
  18. package/ts-dist/php/relation-builder.d.ts +12 -0
  19. package/ts-dist/php/relation-builder.js +147 -0
  20. package/ts-dist/php/request-generator.d.ts +7 -0
  21. package/ts-dist/php/request-generator.js +221 -0
  22. package/ts-dist/php/resource-generator.d.ts +7 -0
  23. package/ts-dist/php/resource-generator.js +178 -0
  24. package/ts-dist/php/schema-reader.d.ts +28 -0
  25. package/ts-dist/php/schema-reader.js +79 -0
  26. package/ts-dist/php/service-provider-generator.d.ts +7 -0
  27. package/ts-dist/php/service-provider-generator.js +64 -0
  28. package/ts-dist/php/trait-generator.d.ts +6 -0
  29. package/ts-dist/php/trait-generator.js +104 -0
  30. package/ts-dist/php/type-mapper.d.ts +25 -0
  31. package/ts-dist/php/type-mapper.js +217 -0
  32. package/ts-dist/php/types.d.ts +61 -0
  33. package/ts-dist/php/types.js +68 -0
  34. package/types/config.d.ts +50 -24
package/README.md CHANGED
@@ -14,6 +14,7 @@ Define your database schemas in YAML, generate Laravel PHP migrations, raw SQL,
14
14
  - **Laravel Migration Generation** — Complete PHP migration files with foreign keys, indexes, and comments
15
15
  - **Raw SQL Generation** — MySQL, PostgreSQL, and SQLite
16
16
  - **TypeScript Generation** — Interfaces, Zod schemas, and i18n types from `schemas.json`
17
+ - **PHP Codegen** — Eloquent models, form requests, API resources, and factories from `schemas.json`
17
18
  - **Incremental Generation** — Lock file tracks changes; only regenerates what's needed
18
19
  - **Compound Types** — Single property expands to multiple columns (e.g., `JapaneseName` → 4 columns)
19
20
  - **Associations** — ManyToOne, OneToMany, ManyToMany (pivot tables), MorphMany/MorphTo
@@ -34,11 +35,6 @@ This installs:
34
35
 
35
36
  Both are available in `node_modules/.bin/` after installation.
36
37
 
37
- > **Note:** You can also install Go binary directly:
38
- > ```bash
39
- > go install github.com/famgia/omnify-go/cmd/omnify@latest
40
- > ```
41
-
42
38
  ---
43
39
 
44
40
  ## Quick Start / クイックスタート
@@ -96,9 +92,10 @@ npx omnify generate
96
92
  This generates:
97
93
  - **Laravel migrations** → `database/migrations/omnify/`
98
94
  - **SQL files** → configured output path
99
- - **TypeScript types** → configured `typescript.path` (interfaces, Zod schemas, i18n)
95
+ - **TypeScript types** → if `codegen.typescript` configured (interfaces, Zod schemas, i18n)
96
+ - **PHP codegen** → if `codegen.laravel` configured (Eloquent models, requests, resources, factories)
100
97
  - **schemas.json** → serialized schema data
101
- - **Lock file** → `.omnify.lock` (tracks schema state)
98
+ - **Lock file** → `.omnify/lock.json` (tracks schema state)
102
99
 
103
100
  ---
104
101
 
@@ -108,20 +105,18 @@ Create `omnify.yaml` in your project root:
108
105
 
109
106
  ```yaml
110
107
  schemasDir: ./schemas
111
- lockFilePath: .omnify.lock
108
+ lockFilePath: .omnify/lock.json
112
109
 
113
110
  connections:
114
111
  default:
115
112
  driver: mysql
116
- output:
117
- laravel:
118
- migrationsPath: database/migrations/omnify
113
+ migrations:
114
+ - type: laravel
115
+ path: database/migrations/omnify
119
116
  schemasPath: database/omnify/schemas.json
120
- sql:
117
+ - type: sql
121
118
  path: migrations/sql
122
119
  dialect: mysql
123
- typescript:
124
- path: resources/js/types/models # TypeScript types output directory
125
120
 
126
121
  default: default
127
122
 
@@ -132,35 +127,53 @@ locale:
132
127
 
133
128
  compoundTypes:
134
129
  - japan # Enables JapaneseName, JapaneseAddress, JapaneseBankAccount
130
+
131
+ codegen:
132
+ typescript:
133
+ enable: true
134
+ modelsPath: resources/js/types/models # TS interfaces, Zod schemas, i18n
135
+ laravel:
136
+ enable: true
137
+ model: # Eloquent models
138
+ path: app/Models/Omnify
139
+ namespace: App\Models\Omnify
135
140
  ```
136
141
 
137
- ### TypeScript Auto-Generation / TypeScript自動生成
142
+ ### Codegen / コード自動生成
138
143
 
139
- When `typescript.path` is configured, `omnify generate` automatically generates TypeScript types after writing `schemas.json`. No separate step needed.
144
+ When `codegen` is configured, `omnify generate` automatically generates code after writing `schemas.json`. No separate step needed.
140
145
 
141
- `typescript.path`を設定すると、`omnify generate`実行時にTypeScript型が自動生成されます。別途コマンドを実行する必要はありません。
146
+ `codegen`を設定すると、`omnify generate`実行時にコードが自動生成されます。
142
147
 
143
- Generated files:
148
+ #### TypeScript (`codegen.typescript`)
144
149
  - `base/` — Auto-generated interfaces and Zod schemas (overwritten on each generation)
145
150
  - `enum/` — Enum type definitions
146
151
  - Model files — User-editable model files (created once, never overwritten)
147
152
 
153
+ #### Laravel PHP (`codegen.laravel`)
154
+ - Eloquent base models (`Base/`) — always overwritten
155
+ - User models — created once, never overwritten
156
+ - Form requests (Store + Update) — base + user files
157
+ - API resources — base + user files
158
+ - Factories — created once
159
+ - Service provider, traits, locales
160
+
148
161
  ### Multiple Connections / 複数接続
149
162
 
150
163
  ```yaml
151
164
  connections:
152
165
  default:
153
166
  driver: mysql
154
- output:
155
- laravel:
156
- migrationsPath: database/migrations/omnify
167
+ migrations:
168
+ - type: laravel
169
+ path: database/migrations/omnify
157
170
 
158
171
  product-db:
159
172
  driver: pgsql
160
- output:
161
- laravel:
162
- migrationsPath: database/migrations/product
163
- sql:
173
+ migrations:
174
+ - type: laravel
175
+ path: database/migrations/product
176
+ - type: sql
164
177
  path: output/sql/product
165
178
  dialect: postgresql
166
179
  ```
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@omnifyjp/omnify",
3
- "version": "0.2.2",
3
+ "version": "0.2.4",
4
4
  "description": "Schema-driven code generation for Laravel, TypeScript, and SQL",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -36,10 +36,10 @@
36
36
  "zod": "^3.24.0"
37
37
  },
38
38
  "optionalDependencies": {
39
- "@omnifyjp/omnify-darwin-arm64": "0.2.0",
40
- "@omnifyjp/omnify-darwin-x64": "0.2.0",
41
- "@omnifyjp/omnify-linux-x64": "0.2.0",
42
- "@omnifyjp/omnify-linux-arm64": "0.2.0",
43
- "@omnifyjp/omnify-win32-x64": "0.2.0"
39
+ "@omnifyjp/omnify-darwin-arm64": "0.2.4",
40
+ "@omnifyjp/omnify-darwin-x64": "0.2.4",
41
+ "@omnifyjp/omnify-linux-x64": "0.2.4",
42
+ "@omnifyjp/omnify-linux-arm64": "0.2.4",
43
+ "@omnifyjp/omnify-win32-x64": "0.2.4"
44
44
  }
45
45
  }
package/ts-dist/cli.js CHANGED
@@ -15,6 +15,7 @@ import { resolve, dirname, join } from 'node:path';
15
15
  import { Command } from 'commander';
16
16
  import { parse as parseYaml } from 'yaml';
17
17
  import { generateTypeScript } from './generator.js';
18
+ import { generatePhp } from './php/index.js';
18
19
  function resolveFromConfig(configPath) {
19
20
  const raw = readFileSync(configPath, 'utf-8');
20
21
  const config = parseYaml(raw);
@@ -26,27 +27,38 @@ function resolveFromConfig(configPath) {
26
27
  if (!conn) {
27
28
  throw new Error(`Connection "${defaultName}" not found in ${configPath}`);
28
29
  }
29
- // Resolve schemas.json input path
30
- const schemasPath = conn.output?.laravel?.schemasPath;
31
- if (!schemasPath) {
32
- throw new Error(`No output.laravel.schemasPath found for connection "${defaultName}" in ${configPath}.\n` +
33
- `Either add schemasPath to your config, or use --input explicitly.`);
30
+ // Resolve schemas.json input path from migrations[type=laravel].schemasPath
31
+ let schemasPath;
32
+ for (const m of conn.migrations ?? []) {
33
+ if (m.type === 'laravel' && m.schemasPath) {
34
+ schemasPath = m.schemasPath;
35
+ break;
36
+ }
34
37
  }
35
- // Resolve typescript output path
36
- const tsPath = conn.output?.typescript?.path;
37
- if (!tsPath) {
38
- throw new Error(`No output.typescript.path found for connection "${defaultName}" in ${configPath}.\n` +
39
- `Add this to your omnify.yaml:\n\n` +
40
- ` connections:\n` +
41
- ` ${defaultName}:\n` +
42
- ` output:\n` +
43
- ` typescript:\n` +
44
- ` path: resources/js/types/models\n\n` +
45
- `Or use --output explicitly.`);
38
+ if (!schemasPath) {
39
+ throw new Error(`No schemasPath found in laravel migration for connection "${defaultName}" in ${configPath}.\n` +
40
+ `Either add schemasPath to your laravel migration config, or use --input explicitly.`);
46
41
  }
42
+ // TypeScript config (enable defaults to false)
43
+ const tsConfig = config.codegen?.typescript;
44
+ const tsEnabled = tsConfig?.enable === true && !!tsConfig?.modelsPath;
45
+ const tsOutput = tsEnabled ? resolve(configDir, tsConfig.modelsPath) : undefined;
46
+ // Laravel config (enable defaults to false)
47
+ const laravelConfig = config.codegen?.laravel;
48
+ const laravelEnabled = laravelConfig?.enable === true;
49
+ const laravelOverrides = laravelEnabled ? {
50
+ model: laravelConfig?.model,
51
+ request: laravelConfig?.request,
52
+ resource: laravelConfig?.resource,
53
+ factory: laravelConfig?.factory,
54
+ provider: laravelConfig?.provider,
55
+ } : undefined;
47
56
  return {
48
57
  input: resolve(configDir, schemasPath),
49
- output: resolve(configDir, tsPath),
58
+ tsEnabled,
59
+ tsOutput,
60
+ laravelEnabled,
61
+ laravelOverrides,
50
62
  };
51
63
  }
52
64
  // ============================================================================
@@ -55,22 +67,27 @@ function resolveFromConfig(configPath) {
55
67
  const program = new Command();
56
68
  program
57
69
  .name('omnify-ts')
58
- .description('Generate TypeScript types from Omnify schemas.json')
70
+ .description('Generate TypeScript types and Laravel PHP code from Omnify schemas.json')
59
71
  .option('-c, --config <path>', 'Path to omnify.yaml (default: ./omnify.yaml)')
60
72
  .option('-i, --input <path>', 'Path to schemas.json (overrides config)')
61
- .option('-o, --output <path>', 'Output directory (overrides config)')
73
+ .option('-o, --output <path>', 'Output directory for TypeScript (overrides config)')
62
74
  .option('--force', 'Overwrite user-editable model files', false)
63
75
  .action((opts) => {
64
76
  let inputPath;
65
- let outputDir;
77
+ let tsEnabled = true;
78
+ let tsOutput;
79
+ let laravelEnabled = false;
80
+ let laravelOverrides;
81
+ let configDir = process.cwd();
66
82
  if (opts.input && opts.output) {
67
- // Explicit flags — skip config
83
+ // Explicit flags — skip config, only TS generation
68
84
  inputPath = resolve(opts.input);
69
- outputDir = resolve(opts.output);
85
+ tsOutput = resolve(opts.output);
70
86
  }
71
87
  else {
72
88
  // Read from omnify.yaml
73
89
  const configPath = resolve(opts.config ?? 'omnify.yaml');
90
+ configDir = dirname(configPath);
74
91
  if (!existsSync(configPath)) {
75
92
  console.error(`Error: Config not found: ${configPath}\n` +
76
93
  `Run from a directory with omnify.yaml, or use --config / --input + --output.`);
@@ -78,7 +95,10 @@ program
78
95
  }
79
96
  const resolved = resolveFromConfig(configPath);
80
97
  inputPath = opts.input ? resolve(opts.input) : resolved.input;
81
- outputDir = opts.output ? resolve(opts.output) : resolved.output;
98
+ tsEnabled = resolved.tsEnabled;
99
+ tsOutput = opts.output ? resolve(opts.output) : resolved.tsOutput;
100
+ laravelEnabled = resolved.laravelEnabled;
101
+ laravelOverrides = resolved.laravelOverrides;
82
102
  }
83
103
  // Read schemas.json
84
104
  if (!existsSync(inputPath)) {
@@ -88,46 +108,73 @@ program
88
108
  const raw = readFileSync(inputPath, 'utf-8');
89
109
  const input = JSON.parse(raw);
90
110
  console.log(`Reading schemas from ${inputPath}`);
91
- console.log(`Output directory: ${outputDir}`);
92
- // Generate files
93
- const files = generateTypeScript(input);
94
- // Ensure output directories exist
95
- mkdirSync(join(outputDir, 'base'), { recursive: true });
96
- mkdirSync(join(outputDir, 'enum'), { recursive: true });
97
- // Write files
98
- let created = 0;
99
- let overwritten = 0;
100
- let skipped = 0;
101
- for (const file of files) {
102
- // Route files to correct subdirectory
103
- let filePath;
104
- if (file.category === 'enum' || file.category === 'plugin-enum') {
105
- filePath = join(outputDir, 'enum', file.filePath);
106
- }
107
- else {
108
- filePath = join(outputDir, file.filePath);
109
- }
110
- // Ensure parent directory exists
111
- mkdirSync(dirname(filePath), { recursive: true });
112
- // Skip user-editable files if they already exist (unless --force)
113
- if (!file.overwrite && existsSync(filePath) && !opts.force) {
114
- skipped++;
115
- continue;
111
+ // ---- TypeScript generation ----
112
+ if (tsEnabled && tsOutput) {
113
+ console.log(`\n[TypeScript] Output: ${tsOutput}`);
114
+ const files = generateTypeScript(input);
115
+ mkdirSync(join(tsOutput, 'base'), { recursive: true });
116
+ mkdirSync(join(tsOutput, 'enum'), { recursive: true });
117
+ let tsCreated = 0;
118
+ let tsOverwritten = 0;
119
+ let tsSkipped = 0;
120
+ for (const file of files) {
121
+ let filePath;
122
+ if (file.category === 'enum' || file.category === 'plugin-enum') {
123
+ filePath = join(tsOutput, 'enum', file.filePath);
124
+ }
125
+ else {
126
+ filePath = join(tsOutput, file.filePath);
127
+ }
128
+ mkdirSync(dirname(filePath), { recursive: true });
129
+ if (!file.overwrite && existsSync(filePath) && !opts.force) {
130
+ tsSkipped++;
131
+ continue;
132
+ }
133
+ writeFileSync(filePath, file.content, 'utf-8');
134
+ if (file.overwrite || !existsSync(filePath)) {
135
+ tsOverwritten++;
136
+ }
137
+ else {
138
+ tsCreated++;
139
+ }
116
140
  }
117
- writeFileSync(filePath, file.content, 'utf-8');
118
- if (file.overwrite || !existsSync(filePath)) {
119
- overwritten++;
120
- }
121
- else {
122
- created++;
141
+ console.log(` ${tsOverwritten} files written (auto-generated)`);
142
+ if (tsCreated > 0)
143
+ console.log(` ${tsCreated} files created (user-editable)`);
144
+ if (tsSkipped > 0)
145
+ console.log(` ${tsSkipped} files skipped (already exist)`);
146
+ }
147
+ else {
148
+ console.log(`\n[TypeScript] Skipped (disabled)`);
149
+ }
150
+ // ---- PHP/Laravel generation ----
151
+ if (laravelEnabled) {
152
+ console.log(`\n[Laravel] Generating PHP files`);
153
+ const phpFiles = generatePhp(input, laravelOverrides);
154
+ let phpCreated = 0;
155
+ let phpOverwritten = 0;
156
+ let phpSkipped = 0;
157
+ for (const file of phpFiles) {
158
+ const filePath = resolve(configDir, file.path);
159
+ mkdirSync(dirname(filePath), { recursive: true });
160
+ if (!file.overwrite && existsSync(filePath) && !opts.force) {
161
+ phpSkipped++;
162
+ continue;
163
+ }
164
+ writeFileSync(filePath, file.content, 'utf-8');
165
+ if (file.overwrite || !existsSync(filePath)) {
166
+ phpOverwritten++;
167
+ }
168
+ else {
169
+ phpCreated++;
170
+ }
123
171
  }
172
+ console.log(` ${phpOverwritten} files written (auto-generated)`);
173
+ if (phpCreated > 0)
174
+ console.log(` ${phpCreated} files created (user-editable)`);
175
+ if (phpSkipped > 0)
176
+ console.log(` ${phpSkipped} files skipped (already exist)`);
124
177
  }
125
- console.log(`\nGeneration complete:`);
126
- console.log(` ${overwritten} files written (auto-generated)`);
127
- if (created > 0)
128
- console.log(` ${created} files created (user-editable)`);
129
- if (skipped > 0)
130
- console.log(` ${skipped} files skipped (already exist)`);
131
- console.log(`\nTotal: ${files.length} files`);
178
+ console.log(`\nGeneration complete.`);
132
179
  });
133
180
  program.parse();
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Port of BaseModelGenerator.php — generates abstract BaseModel with morph map.
3
+ */
4
+ import { SchemaReader } from './schema-reader.js';
5
+ import type { GeneratedFile, PhpConfig } from './types.js';
6
+ /** Generate the abstract BaseModel class with morph map. */
7
+ export declare function generateBaseModel(reader: SchemaReader, config: PhpConfig): GeneratedFile[];
@@ -0,0 +1,70 @@
1
+ /**
2
+ * Port of BaseModelGenerator.php — generates abstract BaseModel with morph map.
3
+ */
4
+ import { toPascalCase } from './naming-helper.js';
5
+ import { baseFile } from './types.js';
6
+ /** Generate the abstract BaseModel class with morph map. */
7
+ export function generateBaseModel(reader, config) {
8
+ const baseNamespace = config.models.baseNamespace;
9
+ const modelNamespace = config.models.namespace;
10
+ // Build morph map entries from all visible object schemas
11
+ const morphMap = {};
12
+ for (const name of Object.keys(reader.getVisibleObjectSchemas())) {
13
+ const modelName = toPascalCase(name);
14
+ morphMap[modelName] = `\\${modelNamespace}\\${modelName}::class`;
15
+ }
16
+ // Sort for consistent output
17
+ const sortedKeys = Object.keys(morphMap).sort();
18
+ const morphEntries = sortedKeys
19
+ .map(k => ` '${k}' => ${morphMap[k]},`)
20
+ .join('\n');
21
+ const content = `<?php
22
+
23
+ namespace ${baseNamespace};
24
+
25
+ /**
26
+ * Base model class for all Omnify-generated models.
27
+ * Contains model mapping for polymorphic relations.
28
+ *
29
+ * DO NOT EDIT - This file is auto-generated by Omnify.
30
+ * Any changes will be overwritten on next generation.
31
+ *
32
+ * @generated by omnify
33
+ */
34
+
35
+ use Illuminate\\Database\\Eloquent\\Model;
36
+ use Illuminate\\Database\\Eloquent\\Relations\\Relation;
37
+
38
+ abstract class BaseModel extends Model
39
+ {
40
+ /**
41
+ * Model class map for polymorphic relations.
42
+ */
43
+ protected static array $modelMap = [
44
+ ${morphEntries}
45
+ ];
46
+
47
+ /**
48
+ * Boot the model and register morph map.
49
+ */
50
+ protected static function boot(): void
51
+ {
52
+ parent::boot();
53
+
54
+ // Register morph map for polymorphic relations
55
+ Relation::enforceMorphMap(static::$modelMap);
56
+ }
57
+
58
+ /**
59
+ * Get the model class for a given morph type.
60
+ */
61
+ public static function getModelClass(string $morphType): ?string
62
+ {
63
+ return static::$modelMap[$morphType] ?? null;
64
+ }
65
+ }
66
+ `;
67
+ return [
68
+ baseFile(`${config.models.basePath}/BaseModel.php`, content),
69
+ ];
70
+ }
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Port of FactoryGenerator.php — generates Model Factory classes.
3
+ */
4
+ import { SchemaReader } from './schema-reader.js';
5
+ import type { GeneratedFile, PhpConfig } from './types.js';
6
+ /** Generate Factory classes for all visible object schemas. */
7
+ export declare function generateFactories(reader: SchemaReader, config: PhpConfig): GeneratedFile[];
@@ -0,0 +1,95 @@
1
+ /**
2
+ * Port of FactoryGenerator.php — generates Model Factory classes.
3
+ */
4
+ import { toPascalCase } from './naming-helper.js';
5
+ import { toFaker, compoundFaker, associationFaker } from './faker-mapper.js';
6
+ import { userFile } from './types.js';
7
+ /** Generate Factory classes for all visible object schemas. */
8
+ export function generateFactories(reader, config) {
9
+ const files = [];
10
+ const schemas = reader.getSchemas();
11
+ for (const [name, schema] of Object.entries(reader.getVisibleObjectSchemas())) {
12
+ const file = generateFactory(name, schema, reader, schemas, config);
13
+ if (file)
14
+ files.push(file);
15
+ }
16
+ return files;
17
+ }
18
+ function generateFactory(name, schema, reader, schemas, config) {
19
+ const modelName = toPascalCase(name);
20
+ const modelNamespace = config.models.namespace;
21
+ const factoryNamespace = config.factories.namespace;
22
+ const properties = (schema.properties ?? {});
23
+ const expandedProperties = reader.getExpandedProperties(name);
24
+ const propertyOrder = reader.getPropertyOrder(name);
25
+ const attributes = [];
26
+ const imports = [];
27
+ for (const propName of propertyOrder) {
28
+ const prop = properties[propName];
29
+ if (!prop)
30
+ continue;
31
+ const type = prop['type'] ?? 'String';
32
+ // Handle associations (foreign keys)
33
+ if (type === 'Association') {
34
+ const result = associationFaker(propName, prop, modelNamespace, name);
35
+ if (result) {
36
+ attributes.push(result.fake);
37
+ if (result.import)
38
+ imports.push(result.import);
39
+ }
40
+ continue;
41
+ }
42
+ // Handle compound types
43
+ if (expandedProperties[propName]) {
44
+ const expansion = expandedProperties[propName];
45
+ const sourceType = expansion.sourceType ?? '';
46
+ const fakes = compoundFaker(propName, expansion, sourceType);
47
+ attributes.push(...fakes);
48
+ continue;
49
+ }
50
+ // Handle regular properties
51
+ const fake = toFaker(propName, prop, schemas);
52
+ if (fake)
53
+ attributes.push(fake);
54
+ }
55
+ const attributesStr = attributes.length > 0
56
+ ? attributes.map(a => ` ${a}`).join('\n')
57
+ : '';
58
+ const uniqueImports = [...new Set(imports)];
59
+ const importsStr = uniqueImports.length > 0
60
+ ? '\n' + uniqueImports.join('\n')
61
+ : '';
62
+ const content = `<?php
63
+
64
+ namespace ${factoryNamespace};
65
+
66
+ use ${modelNamespace}\\${modelName};
67
+ use Illuminate\\Database\\Eloquent\\Factories\\Factory;
68
+ ${importsStr}
69
+
70
+ /**
71
+ * ${modelName} Factory
72
+ *
73
+ * SAFE TO EDIT - This file is never overwritten by Omnify.
74
+ *
75
+ * @extends Factory<${modelName}>
76
+ */
77
+ class ${modelName}Factory extends Factory
78
+ {
79
+ protected $model = ${modelName}::class;
80
+
81
+ /**
82
+ * Define the model's default state.
83
+ *
84
+ * @return array<string, mixed>
85
+ */
86
+ public function definition(): array
87
+ {
88
+ return [
89
+ ${attributesStr}
90
+ ];
91
+ }
92
+ }
93
+ `;
94
+ return userFile(`${config.factories.path}/${modelName}Factory.php`, content);
95
+ }
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Port of FakerMapper.php — generates Faker expressions for factories.
3
+ */
4
+ /** Generate faker expression for a property. */
5
+ export declare function toFaker(propName: string, property: Record<string, unknown>, schemas?: Record<string, Record<string, unknown>>): string | null;
6
+ /** Generate faker expressions for compound type expanded columns. */
7
+ export declare function compoundFaker(propName: string, expansion: Record<string, unknown>, sourceType: string): string[];
8
+ /** Generate faker for association (foreign key). */
9
+ export declare function associationFaker(propName: string, property: Record<string, unknown>, modelNamespace: string, currentSchema: string): {
10
+ fake: string;
11
+ import: string | null;
12
+ } | null;