@omnifyjp/omnify 0.2.3 → 0.3.0
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 +53 -20
- package/package.json +6 -6
- package/ts-dist/cli.js +108 -61
- package/ts-dist/php/base-model-generator.d.ts +7 -0
- package/ts-dist/php/base-model-generator.js +70 -0
- package/ts-dist/php/factory-generator.d.ts +7 -0
- package/ts-dist/php/factory-generator.js +95 -0
- package/ts-dist/php/faker-mapper.d.ts +12 -0
- package/ts-dist/php/faker-mapper.js +206 -0
- package/ts-dist/php/index.d.ts +18 -0
- package/ts-dist/php/index.js +40 -0
- package/ts-dist/php/locales-generator.d.ts +7 -0
- package/ts-dist/php/locales-generator.js +135 -0
- package/ts-dist/php/model-generator.d.ts +7 -0
- package/ts-dist/php/model-generator.js +396 -0
- package/ts-dist/php/naming-helper.d.ts +22 -0
- package/ts-dist/php/naming-helper.js +61 -0
- package/ts-dist/php/relation-builder.d.ts +12 -0
- package/ts-dist/php/relation-builder.js +147 -0
- package/ts-dist/php/request-generator.d.ts +7 -0
- package/ts-dist/php/request-generator.js +221 -0
- package/ts-dist/php/resource-generator.d.ts +7 -0
- package/ts-dist/php/resource-generator.js +178 -0
- package/ts-dist/php/schema-reader.d.ts +28 -0
- package/ts-dist/php/schema-reader.js +79 -0
- package/ts-dist/php/service-provider-generator.d.ts +7 -0
- package/ts-dist/php/service-provider-generator.js +64 -0
- package/ts-dist/php/trait-generator.d.ts +6 -0
- package/ts-dist/php/trait-generator.js +104 -0
- package/ts-dist/php/type-mapper.d.ts +25 -0
- package/ts-dist/php/type-mapper.js +217 -0
- package/ts-dist/php/types.d.ts +61 -0
- package/ts-dist/php/types.js +68 -0
- 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
|
|
@@ -91,9 +92,10 @@ npx omnify generate
|
|
|
91
92
|
This generates:
|
|
92
93
|
- **Laravel migrations** → `database/migrations/omnify/`
|
|
93
94
|
- **SQL files** → configured output path
|
|
94
|
-
- **TypeScript types** →
|
|
95
|
+
- **TypeScript types** → if `codegen.typescript` configured (interfaces, Zod schemas, i18n)
|
|
96
|
+
- **PHP codegen** → if `codegen.laravel` configured (Eloquent models, requests, resources, factories)
|
|
95
97
|
- **schemas.json** → serialized schema data
|
|
96
|
-
- **Lock file** → `.omnify.
|
|
98
|
+
- **Lock file** → `.omnify/lock.json` (tracks schema state)
|
|
97
99
|
|
|
98
100
|
---
|
|
99
101
|
|
|
@@ -103,20 +105,18 @@ Create `omnify.yaml` in your project root:
|
|
|
103
105
|
|
|
104
106
|
```yaml
|
|
105
107
|
schemasDir: ./schemas
|
|
106
|
-
lockFilePath: .omnify.
|
|
108
|
+
lockFilePath: .omnify/lock.json
|
|
107
109
|
|
|
108
110
|
connections:
|
|
109
111
|
default:
|
|
110
112
|
driver: mysql
|
|
111
|
-
|
|
112
|
-
laravel
|
|
113
|
-
|
|
113
|
+
migrations:
|
|
114
|
+
- type: laravel
|
|
115
|
+
path: database/migrations/omnify
|
|
114
116
|
schemasPath: database/omnify/schemas.json
|
|
115
|
-
sql
|
|
117
|
+
- type: sql
|
|
116
118
|
path: migrations/sql
|
|
117
119
|
dialect: mysql
|
|
118
|
-
typescript:
|
|
119
|
-
path: resources/js/types/models # TypeScript types output directory
|
|
120
120
|
|
|
121
121
|
default: default
|
|
122
122
|
|
|
@@ -127,35 +127,53 @@ locale:
|
|
|
127
127
|
|
|
128
128
|
compoundTypes:
|
|
129
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
|
|
130
140
|
```
|
|
131
141
|
|
|
132
|
-
###
|
|
142
|
+
### Codegen / コード自動生成
|
|
133
143
|
|
|
134
|
-
When `
|
|
144
|
+
When `codegen` is configured, `omnify generate` automatically generates code after writing `schemas.json`. No separate step needed.
|
|
135
145
|
|
|
136
|
-
`
|
|
146
|
+
`codegen`を設定すると、`omnify generate`実行時にコードが自動生成されます。
|
|
137
147
|
|
|
138
|
-
|
|
148
|
+
#### TypeScript (`codegen.typescript`)
|
|
139
149
|
- `base/` — Auto-generated interfaces and Zod schemas (overwritten on each generation)
|
|
140
150
|
- `enum/` — Enum type definitions
|
|
141
151
|
- Model files — User-editable model files (created once, never overwritten)
|
|
142
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
|
+
|
|
143
161
|
### Multiple Connections / 複数接続
|
|
144
162
|
|
|
145
163
|
```yaml
|
|
146
164
|
connections:
|
|
147
165
|
default:
|
|
148
166
|
driver: mysql
|
|
149
|
-
|
|
150
|
-
laravel
|
|
151
|
-
|
|
167
|
+
migrations:
|
|
168
|
+
- type: laravel
|
|
169
|
+
path: database/migrations/omnify
|
|
152
170
|
|
|
153
171
|
product-db:
|
|
154
172
|
driver: pgsql
|
|
155
|
-
|
|
156
|
-
laravel
|
|
157
|
-
|
|
158
|
-
sql
|
|
173
|
+
migrations:
|
|
174
|
+
- type: laravel
|
|
175
|
+
path: database/migrations/product
|
|
176
|
+
- type: sql
|
|
159
177
|
path: output/sql/product
|
|
160
178
|
dialect: postgresql
|
|
161
179
|
```
|
|
@@ -367,6 +385,21 @@ Add to `.mcp.json`:
|
|
|
367
385
|
| `omnify_list_types` | List all property types (built-in, compound, simple, enum) |
|
|
368
386
|
| `omnify_validate` | Validate YAML against the schema system with target verification |
|
|
369
387
|
|
|
388
|
+
### MCP Prompts (Slash Commands) / スラッシュコマンド
|
|
389
|
+
|
|
390
|
+
MCP prompts appear as autocomplete options when you type `/` in your editor.
|
|
391
|
+
|
|
392
|
+
MCPプロンプトはエディタで`/`を入力すると自動補完されます。
|
|
393
|
+
|
|
394
|
+
| Prompt | Usage | Description |
|
|
395
|
+
|--------|-------|-------------|
|
|
396
|
+
| `create_schema` | `/create_schema "Product with name, price, category"` | Create a new schema with full project context (existing schemas, types, locales) |
|
|
397
|
+
| `add_property` | `/add_property schema=Product "email field"` | Add a property to an existing schema with type guidance |
|
|
398
|
+
| `review_schema` | `/review_schema schema=User` | Review schema for best practices, missing fields, improvements |
|
|
399
|
+
|
|
400
|
+
In Claude Code, prompts are accessed via `/mcp__omnify__create_schema`.
|
|
401
|
+
In Cursor, prompts appear in the `/` autocomplete menu.
|
|
402
|
+
|
|
370
403
|
---
|
|
371
404
|
|
|
372
405
|
## TypeScript Editor DX / TypeScriptエディタ支援
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@omnifyjp/omnify",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
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.
|
|
40
|
-
"@omnifyjp/omnify-darwin-x64": "0.
|
|
41
|
-
"@omnifyjp/omnify-linux-x64": "0.
|
|
42
|
-
"@omnifyjp/omnify-linux-arm64": "0.
|
|
43
|
-
"@omnifyjp/omnify-win32-x64": "0.
|
|
39
|
+
"@omnifyjp/omnify-darwin-arm64": "0.3.0",
|
|
40
|
+
"@omnifyjp/omnify-darwin-x64": "0.3.0",
|
|
41
|
+
"@omnifyjp/omnify-linux-x64": "0.3.0",
|
|
42
|
+
"@omnifyjp/omnify-linux-arm64": "0.3.0",
|
|
43
|
+
"@omnifyjp/omnify-win32-x64": "0.3.0"
|
|
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
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
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
|
-
|
|
36
|
-
|
|
37
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
filePath
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
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
|
-
|
|
118
|
-
if (
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
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;
|