prisma-laravel-migrate 3.0.5 → 3.0.6
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 +496 -309
- package/dist/cli/cli.js +167 -109
- package/dist/cli/generate.js +20 -24
- package/dist/cli/ts.index.js +12 -0
- package/dist/diff-writer/writer.js +27 -8
- package/dist/generator/modeler/generator.js +2 -1
- package/dist/generator/ts/directives.js +76 -0
- package/dist/generator/ts/generator.js +243 -0
- package/dist/generator/ts/index.js +181 -0
- package/dist/generator/ts/printer.js +362 -0
- package/dist/generator/ts/types.js +2 -0
- package/dist/index.js +1 -0
- package/dist/utils/clean.js +2 -2
- package/dist/utils/pretty.js +2 -2
- package/dist/utils/stubResolver.js +35 -14
- package/package.json +1 -1
- package/small.prisma +1 -0
- package/stubs/ts.stub +3 -0
package/README.md
CHANGED
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
# Prisma Laravel Migrate
|
|
2
2
|
|
|
3
|
-
A generator plugin that translates your **Prisma schema** into Laravel‑ready
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
A generator plugin that translates your **Prisma schema** into Laravel‑ready
|
|
4
|
+
|
|
5
|
+
* **Database Migrations**
|
|
6
|
+
* **Eloquent Models & PHP Enums**
|
|
7
|
+
* **TypeScript model & enum types**
|
|
8
|
+
|
|
9
|
+
Built in strict TypeScript with fully‑customisable stubs, grouping, smart merge updates, and an optional TS types generator.
|
|
6
10
|
|
|
7
11
|
---
|
|
8
12
|
|
|
@@ -14,7 +18,8 @@ npm install prisma-laravel-migrate --save-dev
|
|
|
14
18
|
```
|
|
15
19
|
|
|
16
20
|
---
|
|
17
|
-
|
|
21
|
+
|
|
22
|
+
## 🛠️ Configuration layers
|
|
18
23
|
|
|
19
24
|
The generators read options in **three tiers (highest → lowest)**:
|
|
20
25
|
|
|
@@ -43,54 +48,97 @@ module.exports = {
|
|
|
43
48
|
output: {
|
|
44
49
|
migrations: "database/migrations",
|
|
45
50
|
models: "app/Models",
|
|
46
|
-
enums: "app/Enums"
|
|
51
|
+
enums: "app/Enums",
|
|
47
52
|
},
|
|
48
53
|
|
|
49
54
|
/* -- per‑generator overrides ------------------------ */
|
|
50
55
|
migrate: {
|
|
51
56
|
groups: "./prisma/migrate-groups.js",
|
|
52
|
-
rules : "./prisma/custom-rules.js"
|
|
57
|
+
rules : "./prisma/custom-rules.js",
|
|
53
58
|
},
|
|
54
59
|
|
|
55
60
|
modeler: {
|
|
56
61
|
groups: [
|
|
57
|
-
{ stubFile: "audit.stub", tables: ["logs","audit_trails"] }
|
|
62
|
+
{ stubFile: "audit.stub", tables: ["logs","audit_trails"] },
|
|
58
63
|
],
|
|
59
64
|
outputEnumDir: "app/Enums",
|
|
60
65
|
overwriteExisting: true,
|
|
61
|
-
allowedPivotExtraFields: ["scope"]
|
|
62
|
-
|
|
66
|
+
allowedPivotExtraFields: ["scope"],
|
|
67
|
+
// use awobaz/compoships for multi-column relations
|
|
68
|
+
awobaz: true,
|
|
69
|
+
},
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* NEW: TypeScript generator (models + enums → TS types)
|
|
73
|
+
*/
|
|
74
|
+
types: {
|
|
75
|
+
// Where generated TS goes (default: "resources/ts/prisma")
|
|
76
|
+
outputDir: "resources/ts/prisma",
|
|
77
|
+
|
|
78
|
+
// Emit .d.ts instead of .ts when true
|
|
79
|
+
declaration: false,
|
|
80
|
+
|
|
81
|
+
// "interface" | "type" for model declarations
|
|
82
|
+
shape: "interface",
|
|
83
|
+
|
|
84
|
+
// Optional module wrapper for the main types file
|
|
85
|
+
// declare module "database/prisma" { ... }
|
|
86
|
+
moduleName: "database/prisma",
|
|
87
|
+
|
|
88
|
+
// Example scalar mapping overrides
|
|
89
|
+
scalarMap: {
|
|
90
|
+
// BigInt: "bigint",
|
|
91
|
+
// Decimal: "string",
|
|
92
|
+
// Json: "unknown",
|
|
93
|
+
},
|
|
94
|
+
|
|
95
|
+
// If true: nullable fields become optional properties
|
|
96
|
+
nullableAsOptional: false,
|
|
97
|
+
|
|
98
|
+
// If true: lists become ReadonlyArray<T>
|
|
99
|
+
readonlyArrays: false,
|
|
100
|
+
|
|
101
|
+
// Name decoration for interfaces/types
|
|
102
|
+
namePrefix: "",
|
|
103
|
+
nameSuffix: "",
|
|
104
|
+
},
|
|
63
105
|
};
|
|
64
106
|
```
|
|
65
107
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
108
|
+
> There is also a small section in the codebase labelled
|
|
109
|
+
> **“Related Generator Options (Formatting & Compoships)”** covering:
|
|
110
|
+
>
|
|
111
|
+
> * `prettier` (per‑generator): enables formatting via Prettier for
|
|
112
|
+
> PHP/TS outputs during merge.
|
|
113
|
+
> * `modeler.awobaz`: whether to emit Compoships‑aware relationships
|
|
114
|
+
> (requires `awobaz/compoships` in your Laravel app).
|
|
115
|
+
|
|
116
|
+
---
|
|
69
117
|
|
|
70
118
|
<details>
|
|
71
119
|
<summary>Type reference</summary>
|
|
72
120
|
|
|
73
121
|
```ts
|
|
74
122
|
export interface Rule {
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
123
|
+
test(
|
|
124
|
+
def: ColumnDefinition,
|
|
125
|
+
allDefs: ColumnDefinition[],
|
|
126
|
+
dmmf: DMMF.Document,
|
|
127
|
+
): boolean;
|
|
128
|
+
render(
|
|
129
|
+
def: ColumnDefinition,
|
|
130
|
+
allDefs: ColumnDefinition[],
|
|
131
|
+
dmmf: DMMF.Document,
|
|
132
|
+
): Render;
|
|
85
133
|
}
|
|
86
134
|
|
|
87
135
|
/* ------------------------------------------------------------
|
|
88
136
|
* Re-usable stub-group description
|
|
89
137
|
* ---------------------------------------------------------- */
|
|
90
138
|
export interface StubGroupConfig extends FlexibleStubGroup {
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
139
|
+
/** Path relative to stubDir/<type>/ (e.g. "auth.stub") */
|
|
140
|
+
stubFile: string;
|
|
141
|
+
tables: string[]; // ["users","accounts",…] or enum names
|
|
94
142
|
}
|
|
95
143
|
|
|
96
144
|
/**
|
|
@@ -98,20 +146,20 @@ export interface StubGroupConfig extends FlexibleStubGroup {
|
|
|
98
146
|
* Supply EITHER `tables` *or* (`include` / `exclude` / `pattern`).
|
|
99
147
|
*/
|
|
100
148
|
interface FlexibleStubGroup {
|
|
101
|
-
|
|
102
|
-
|
|
149
|
+
/** Path relative to stubDir/<type>/, e.g. "auth.stub" */
|
|
150
|
+
stubFile: string;
|
|
103
151
|
|
|
104
|
-
|
|
105
|
-
|
|
152
|
+
/** Old style - explicit white-list */
|
|
153
|
+
tables?: string[];
|
|
106
154
|
|
|
107
|
-
|
|
108
|
-
|
|
155
|
+
/** New style – include list ( '*' means “all tables” ) */
|
|
156
|
+
include?: string[] | '*';
|
|
109
157
|
|
|
110
|
-
|
|
111
|
-
|
|
158
|
+
/** New style – blacklist applied after include / pattern */
|
|
159
|
+
exclude?: string[];
|
|
112
160
|
|
|
113
|
-
|
|
114
|
-
|
|
161
|
+
/** New style – RegExp OR minimatch glob(s) */
|
|
162
|
+
pattern?: RegExp | string | Array<RegExp | string>;
|
|
115
163
|
}
|
|
116
164
|
|
|
117
165
|
|
|
@@ -119,81 +167,124 @@ interface FlexibleStubGroup {
|
|
|
119
167
|
* Per-generator overrides (migration / modeler)
|
|
120
168
|
* ---------------------------------------------------------- */
|
|
121
169
|
export interface LaravelGeneratorConfig {
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
170
|
+
tablePrefix?: string;
|
|
171
|
+
tableSuffix?: string;
|
|
172
|
+
|
|
173
|
+
/** Override stubDir only for this generator */
|
|
174
|
+
stubDir?: string;
|
|
175
|
+
|
|
176
|
+
/** Where the generated PHP goes (overrides block) */
|
|
177
|
+
outputDir?: string;
|
|
178
|
+
|
|
179
|
+
overwriteExisting?: boolean;
|
|
180
|
+
/** Allow formatting with prettier */
|
|
181
|
+
prettier?: boolean;
|
|
182
|
+
/**
|
|
183
|
+
* Stub grouping:
|
|
184
|
+
* • string – path to a JS module exporting StubGroupConfig[]
|
|
185
|
+
* • array – the group definitions themselves
|
|
186
|
+
*/
|
|
187
|
+
groups?: string | StubGroupConfig[];
|
|
188
|
+
|
|
189
|
+
/** Skip file emission for *this* generator only */
|
|
190
|
+
noEmit?: boolean;
|
|
191
|
+
|
|
192
|
+
/** Default namespace for local imports */
|
|
193
|
+
namespace?: "App\\";
|
|
146
194
|
}
|
|
147
195
|
|
|
148
196
|
/* ------------------------------------------------------------
|
|
149
197
|
* Top-level shared config (visible to all generators)
|
|
150
198
|
* ---------------------------------------------------------- */
|
|
151
199
|
export interface LaravelSharedConfig {
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
200
|
+
/** Table name decoration */
|
|
201
|
+
tablePrefix?: string;
|
|
202
|
+
tableSuffix?: string;
|
|
203
|
+
|
|
204
|
+
/** Default stub root (migration/, model/, enum/, ts/) */
|
|
205
|
+
stubDir?: string;
|
|
206
|
+
|
|
207
|
+
/** Global “don’t write files” switch */
|
|
208
|
+
noEmit?: boolean;
|
|
209
|
+
|
|
210
|
+
/** Override default output folders (PHP side) */
|
|
211
|
+
output?: {
|
|
212
|
+
migrations?: string;
|
|
213
|
+
models?: string;
|
|
214
|
+
enums?: string;
|
|
215
|
+
};
|
|
216
|
+
|
|
217
|
+
/** Per-generator fine-tuning */
|
|
218
|
+
migrate?: Partial<MigratorConfigOverride>;
|
|
219
|
+
modeler?: Partial<ModelConfigOverride>;
|
|
220
|
+
|
|
221
|
+
/** NEW: TypeScript generator overrides */
|
|
222
|
+
types?: Partial<TypesConfigOverride>;
|
|
172
223
|
}
|
|
173
224
|
|
|
174
225
|
|
|
175
226
|
/* --- Migrator-specific extra keys ---------------------------------------- */
|
|
176
227
|
export interface MigratorConfigOverride extends LaravelGeneratorConfig {
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
228
|
+
/**
|
|
229
|
+
* Custom migration rules:
|
|
230
|
+
* • string – path to JS module exporting Rule[]
|
|
231
|
+
* • Rule[] – rules array inline
|
|
232
|
+
*/
|
|
233
|
+
rules?: string | Rule[];
|
|
234
|
+
stubPath?: string;
|
|
235
|
+
defaultMaps?: DefaultMaps;
|
|
185
236
|
}
|
|
186
237
|
|
|
187
238
|
|
|
188
239
|
export interface ModelConfigOverride extends LaravelGeneratorConfig {
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
240
|
+
modelStubPath?: string;
|
|
241
|
+
enumStubPath?: string;
|
|
242
|
+
/** Extra folder for enums (modeler only) */
|
|
243
|
+
outputEnumDir?: string;
|
|
244
|
+
/** use awobaz/compoships */
|
|
245
|
+
awobaz?: boolean;
|
|
246
|
+
/** Extra fields allowed on pivot models */
|
|
247
|
+
allowedPivotExtraFields?: string[];
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
/* ------------------------------------------------------------
|
|
251
|
+
* TypeScript generator overrides
|
|
252
|
+
* ---------------------------------------------------------- */
|
|
253
|
+
export interface TypesConfigOverride extends LaravelGeneratorConfig {
|
|
254
|
+
/** Where generated TS types should be written, e.g. "resources/ts/prisma" */
|
|
255
|
+
outputDir?: string;
|
|
256
|
+
|
|
257
|
+
/** Emit `.d.ts` declaration files instead of `.ts` source files. */
|
|
258
|
+
declaration?: boolean; // default: false → .ts
|
|
259
|
+
|
|
260
|
+
/** Use `interface` or `type` for model declarations. */
|
|
261
|
+
shape?: "interface" | "type"; // default: "interface"
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
* Map Prisma scalar types → TypeScript types.
|
|
265
|
+
* Keys are Prisma scalar names ("Int", "BigInt", "Decimal", "Json", "DateTime", etc.).
|
|
266
|
+
*/
|
|
267
|
+
scalarMap?: Record<string, string>;
|
|
268
|
+
|
|
269
|
+
/** If true, nullable fields become optional: `foo?: string` instead of `foo: string | null`. */
|
|
270
|
+
nullableAsOptional?: boolean;
|
|
271
|
+
|
|
272
|
+
/** If true, lists are emitted as `ReadonlyArray<T>` instead of `T[]`. */
|
|
273
|
+
readonlyArrays?: boolean;
|
|
274
|
+
|
|
275
|
+
/** Optional name decoration for generated types/interfaces. */
|
|
276
|
+
namePrefix?: string;
|
|
277
|
+
nameSuffix?: string;
|
|
278
|
+
|
|
279
|
+
/**
|
|
280
|
+
* Optional root module/namespace hint.
|
|
281
|
+
* When set, the main types file is wrapped in:
|
|
282
|
+
* declare module "…" { ... }
|
|
283
|
+
*/
|
|
284
|
+
moduleName?: string;
|
|
285
|
+
|
|
286
|
+
/** Where models should import enums from (default: "./enums"). */
|
|
287
|
+
enumImportFrom?: string;
|
|
197
288
|
}
|
|
198
289
|
```
|
|
199
290
|
|
|
@@ -214,35 +305,47 @@ generator migrate {
|
|
|
214
305
|
generator modeler {
|
|
215
306
|
provider = "prisma-laravel-models"
|
|
216
307
|
stubDir = "./prisma/stubs"
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
// NEW: TypeScript types generator
|
|
311
|
+
generator types {
|
|
312
|
+
provider = "prisma-laravel-types"
|
|
313
|
+
stubDir = "./prisma/stubs"
|
|
314
|
+
}
|
|
217
315
|
```
|
|
218
316
|
|
|
219
317
|
### Field Reference
|
|
220
318
|
|
|
221
|
-
| Key
|
|
222
|
-
|
|
|
223
|
-
| `outputDir / output`
|
|
224
|
-
| `outputEnumDir`
|
|
225
|
-
| `stubDir`
|
|
226
|
-
| `tablePrefix`
|
|
227
|
-
| `tableSuffix`
|
|
228
|
-
| `groups`
|
|
229
|
-
| `noEmit`
|
|
319
|
+
| Key | Notes | |
|
|
320
|
+
| -------------------- | ----------------------------------------------------------------------------------------------- | ------ |
|
|
321
|
+
| `outputDir / output` | Destination folder (`outputDir` overrides `output`) — PHP and TS. | |
|
|
322
|
+
| `outputEnumDir` | (modeler) directory for generated PHP enum classes. | |
|
|
323
|
+
| `stubDir` | Root stub folder (`migration/`, `model/`, `enum/`, `ts/`). | |
|
|
324
|
+
| `tablePrefix` | String prepended to every generated **physical** table name. | |
|
|
325
|
+
| `tableSuffix` | String appended to every generated **physical** table name. | |
|
|
326
|
+
| `groups` | JS module *or* inline array that maps stub files to table/model/enum/TS groups. | |
|
|
327
|
+
| `noEmit` | If `true`, generator parses and validates but **does not write** any files (dry-run / CI mode). | |
|
|
328
|
+
| `prettier` | Per-generator toggle for formatting (PHP or TS) before 3‑way merge. | |
|
|
329
|
+
| `awobaz` | (modeler) Emit Compoships‑aware relationships; requires `awobaz/compoships`. | |
|
|
330
|
+
| `scalarMap` | (types) Map Prisma scalars → TS type names. | |
|
|
331
|
+
| `nullableAsOptional` | (types) Emit `foo?: T` for nullable fields instead of `foo: T | null`. |
|
|
332
|
+
| `readonlyArrays` | (types) Emit `ReadonlyArray<T>` instead of `T[]`. | |
|
|
333
|
+
| `moduleName` | (types) Wrap main types output in `declare module "…" {}`. | |
|
|
230
334
|
|
|
231
335
|
---
|
|
232
336
|
|
|
233
|
-
|
|
234
337
|
## 🔀 `groups` – Stub Grouping (v2)
|
|
235
338
|
|
|
236
|
-
A **group** links one stub file to
|
|
339
|
+
A **group** links one stub file to *many* tables (or enums / TS variants).
|
|
237
340
|
From v2 you can keep the old **`tables: [...]`** list **or** use the new, more
|
|
238
341
|
expressive selectors:
|
|
239
342
|
|
|
240
|
-
| Key
|
|
241
|
-
|
|
242
|
-
| `tables`
|
|
243
|
-
| `include`
|
|
244
|
-
| `exclude`
|
|
245
|
-
| `pattern`
|
|
343
|
+
| Key | Type / Example | Meaning |
|
|
344
|
+
| --------- | --------------------------------- | ----------------------------------------------- |
|
|
345
|
+
| `tables` | `["users","accounts"]` | Classic explicit list – **exact names** only |
|
|
346
|
+
| `include` | `["audit_*","logs"]` **or** `"*"` | White‑list using globs or `'*'` (all) |
|
|
347
|
+
| `exclude` | `["*_archive","failed_jobs"]` | Black‑list applied *after* include / pattern |
|
|
348
|
+
| `pattern` | `/^temp_/` or `"report_*"` | `RegExp` **or** glob(s) – match if **any** hits |
|
|
246
349
|
|
|
247
350
|
> Only one of **`tables`** **or** the new selector trio is required.
|
|
248
351
|
|
|
@@ -264,34 +367,36 @@ module.exports = [
|
|
|
264
367
|
// 1. legacy explicit list
|
|
265
368
|
{
|
|
266
369
|
stubFile: "auth.stub", // stubs/migration/auth.stub
|
|
267
|
-
tables: ["users","accounts","password_resets"]
|
|
370
|
+
tables: ["users","accounts","password_resets"],
|
|
268
371
|
},
|
|
269
372
|
|
|
270
373
|
// 2. regex + blacklist
|
|
271
374
|
{
|
|
272
375
|
stubFile: "audit.stub",
|
|
273
376
|
pattern : /^audit_/,
|
|
274
|
-
exclude : ["audit_archive"]
|
|
377
|
+
exclude : ["audit_archive"],
|
|
275
378
|
},
|
|
276
379
|
|
|
277
380
|
// 3. catch‑all
|
|
278
381
|
{
|
|
279
382
|
stubFile: "catch-all.stub",
|
|
280
383
|
include : "*",
|
|
281
|
-
exclude : ["failed_jobs","migrations"]
|
|
282
|
-
}
|
|
384
|
+
exclude : ["failed_jobs","migrations"],
|
|
385
|
+
},
|
|
283
386
|
];
|
|
284
387
|
```
|
|
285
388
|
|
|
286
389
|
### Resolution order
|
|
287
390
|
|
|
288
|
-
1. **Table‑specific** — `stubs/<type>/<table>.stub`
|
|
289
|
-
2. **First matching group** (objects are checked top‑to‑bottom)
|
|
391
|
+
1. **Table‑specific** — `stubs/<type>/<table>.stub`
|
|
392
|
+
2. **First matching group** (objects are checked top‑to‑bottom)
|
|
290
393
|
3. **Default** — `stubs/<type>/index.stub`
|
|
291
394
|
|
|
292
395
|
> If a group’s `stubFile` does not exist it is skipped, so you may leave unused
|
|
293
396
|
> groups in place without breaking the build.
|
|
294
397
|
|
|
398
|
+
---
|
|
399
|
+
|
|
295
400
|
## 📁 Stub Folder Layout
|
|
296
401
|
|
|
297
402
|
```text
|
|
@@ -299,21 +404,26 @@ prisma/stubs/
|
|
|
299
404
|
├── migration/index.stub
|
|
300
405
|
├── model/index.stub
|
|
301
406
|
├── model/simple-model.stub
|
|
302
|
-
|
|
407
|
+
├── enum/index.stub
|
|
408
|
+
└── ts/index.stub
|
|
303
409
|
```
|
|
304
410
|
|
|
305
|
-
Add table‑specific overrides at
|
|
306
|
-
`stubs/<type>/<table>.stub` (e.g. `stubs/model/users.stub`).
|
|
411
|
+
Add table‑specific overrides at
|
|
412
|
+
`stubs/<type>/<table>.stub` (e.g. `stubs/model/users.stub`, `stubs/ts/Account.stub`).
|
|
413
|
+
|
|
414
|
+
* `migration/` and `model/` stubs are **PHP** template literals.
|
|
415
|
+
* `enum/` stubs are **PHP** enum templates.
|
|
416
|
+
* `ts/` stubs are **JavaScript template literals** evaluated by the TS printer.
|
|
307
417
|
|
|
308
418
|
---
|
|
309
419
|
|
|
310
420
|
## 🔧 CLI Commands
|
|
311
421
|
|
|
312
|
-
| Command
|
|
313
|
-
|
|
|
314
|
-
| `init`
|
|
315
|
-
| `customize` | Create per-table stub overrides
|
|
316
|
-
| `gen`
|
|
422
|
+
| Command | Purpose |
|
|
423
|
+
| ----------- | -------------------------------------------------- |
|
|
424
|
+
| `init` | Inject generator blocks & scaffold stubs |
|
|
425
|
+
| `customize` | Create per-table stub overrides |
|
|
426
|
+
| `gen` | Run `prisma generate` then Laravel + TS generators |
|
|
317
427
|
|
|
318
428
|
### init
|
|
319
429
|
|
|
@@ -324,15 +434,18 @@ npx prisma-laravel-cli init --schema=prisma/schema.prisma
|
|
|
324
434
|
### customize
|
|
325
435
|
|
|
326
436
|
```bash
|
|
327
|
-
npx prisma-laravel-cli customize
|
|
437
|
+
npx prisma-laravel-cli customize \
|
|
438
|
+
-t migration,model,ts \
|
|
439
|
+
-n users,accounts \
|
|
440
|
+
--force
|
|
328
441
|
```
|
|
329
442
|
|
|
330
|
-
| Flag
|
|
331
|
-
|
|
|
332
|
-
| `-t, --
|
|
333
|
-
| `-n, --names` | **Required.** Table or enum names (`users,accounts`).
|
|
334
|
-
| `--force`
|
|
335
|
-
| `--config`
|
|
443
|
+
| Flag | Description |
|
|
444
|
+
| ------------- | ---------------------------------------------------------------------------------------------- |
|
|
445
|
+
| `-t, --types` | **Required.** Stub types (`migration`, `model`, `enum`, `ts`). `enum` may not mix with others. |
|
|
446
|
+
| `-n, --names` | **Required.** Table or enum names (`users,accounts`). |
|
|
447
|
+
| `--force` | Overwrite existing stub files. |
|
|
448
|
+
| `--config` | Alternate CLI config path. |
|
|
336
449
|
|
|
337
450
|
### gen
|
|
338
451
|
|
|
@@ -346,17 +459,22 @@ npx prisma-laravel-cli gen --config=prisma/laravel.config.js --skipGenerate
|
|
|
346
459
|
|
|
347
460
|
```js
|
|
348
461
|
module.exports = {
|
|
349
|
-
|
|
462
|
+
migrate: {
|
|
350
463
|
outputDir: "database/migrations",
|
|
351
464
|
stubDir: "prisma/stubs",
|
|
352
|
-
groups: "./prisma/group-stubs.js"
|
|
465
|
+
groups: "./prisma/group-stubs.js",
|
|
353
466
|
},
|
|
354
467
|
modeler: {
|
|
355
468
|
outputDir: "app/Models",
|
|
356
469
|
outputEnumDir: "app/Enums",
|
|
357
470
|
stubDir: "prisma/stubs",
|
|
358
|
-
groups: "./prisma/group-stubs.js"
|
|
359
|
-
}
|
|
471
|
+
groups: "./prisma/group-stubs.js",
|
|
472
|
+
},
|
|
473
|
+
types: {
|
|
474
|
+
outputDir: "resources/ts/prisma",
|
|
475
|
+
stubDir: "prisma/stubs",
|
|
476
|
+
moduleName: "database/prisma",
|
|
477
|
+
},
|
|
360
478
|
};
|
|
361
479
|
```
|
|
362
480
|
|
|
@@ -366,56 +484,67 @@ module.exports = {
|
|
|
366
484
|
|
|
367
485
|
1. Generator builds a **full new file** from your schema & stubs.
|
|
368
486
|
2. Performs a **git‑style 3‑way merge** (using `node-diff3`):
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
487
|
+
|
|
488
|
+
* **base** = last generator output (`.prisma-laravel/backups/...`)
|
|
489
|
+
* **ours** = file on disk (user edits)
|
|
490
|
+
* **theirs** = freshly generated file
|
|
491
|
+
3. Non‑conflicting changes merge automatically; conflicts are wrapped with
|
|
492
|
+
`<<<<<<<`, `=======`, `>>>>>>>`.
|
|
493
|
+
4. New imports are merged, duplicates skipped.
|
|
375
494
|
5. Baseline copy is updated in the backups folder.
|
|
376
495
|
|
|
496
|
+
The same merge pipeline is used for:
|
|
497
|
+
|
|
498
|
+
* PHP migrations (`type: "migrator"` in the diff writer),
|
|
499
|
+
* PHP models/enums (`type: "model"`),
|
|
500
|
+
* TypeScript outputs (`type: "ts"`, using the `typescript` Prettier parser).
|
|
501
|
+
|
|
377
502
|
Delete the marker block **and** set `noEmit = true` to stop updates for a file.
|
|
378
503
|
|
|
379
504
|
---
|
|
380
505
|
|
|
381
506
|
## ✨ Stub Customisation Notes
|
|
382
507
|
|
|
383
|
-
Stubs are **JavaScript template literals**. Escape
|
|
508
|
+
Stubs are **JavaScript template literals**. Escape ` and ${ } if you want them literally.
|
|
384
509
|
|
|
385
|
-
> **Fully custom model stubs**
|
|
510
|
+
> **Fully custom model stubs**
|
|
386
511
|
> If you remove the `${content}` placeholder **and** the marker block, the
|
|
387
|
-
> generator leaves the file untouched.
|
|
512
|
+
> generator leaves the file untouched.
|
|
388
513
|
> Keep the markers if you want automated updates but customised surroundings.
|
|
389
514
|
|
|
390
515
|
---
|
|
391
516
|
|
|
392
517
|
## 📑 Default Stub Templates
|
|
393
518
|
|
|
519
|
+
### PHP Enum `index.stub`
|
|
520
|
+
|
|
394
521
|
<details>
|
|
395
|
-
<summary>Enum <code>index.stub</code
|
|
522
|
+
<summary>Enum <code>index.stub</code> (PHP)</summary>
|
|
396
523
|
|
|
397
524
|
```php
|
|
398
525
|
<?php
|
|
399
526
|
|
|
400
|
-
namespace App
|
|
527
|
+
namespace App\Enums;
|
|
401
528
|
|
|
402
529
|
enum ${enumDef.name}: string
|
|
403
530
|
{
|
|
404
|
-
${enumDef.values.map(v => ` case ${v} = '${v}';`).join('
|
|
531
|
+
${enumDef.values.map(v => ` case ${v} = '${v}';`).join('\n')}
|
|
405
532
|
}
|
|
406
533
|
```
|
|
407
534
|
|
|
408
535
|
</details>
|
|
409
536
|
|
|
537
|
+
### PHP Migration `index.stub`
|
|
538
|
+
|
|
410
539
|
<details>
|
|
411
540
|
<summary>Migration <code>index.stub</code></summary>
|
|
412
541
|
|
|
413
542
|
```php
|
|
414
543
|
<?php
|
|
415
544
|
|
|
416
|
-
use Illuminate
|
|
417
|
-
use Illuminate
|
|
418
|
-
use Illuminate
|
|
545
|
+
use Illuminate\Database\Migrations\Migration;
|
|
546
|
+
use Illuminate\Database\Schema\Blueprint;
|
|
547
|
+
use Illuminate\Support\Facades\Schema;
|
|
419
548
|
|
|
420
549
|
return new class extends Migration
|
|
421
550
|
{
|
|
@@ -435,9 +564,35 @@ return new class extends Migration
|
|
|
435
564
|
|
|
436
565
|
</details>
|
|
437
566
|
|
|
567
|
+
### TypeScript module `index.stub`
|
|
568
|
+
|
|
569
|
+
<details>
|
|
570
|
+
<summary>TS <code>ts/index.stub</code> (module-level)</summary>
|
|
571
|
+
|
|
572
|
+
```ts
|
|
573
|
+
${imports}
|
|
574
|
+
|
|
575
|
+
${body}
|
|
576
|
+
```
|
|
577
|
+
|
|
578
|
+
* `imports` → merged import block for all non-stubbed models (including enums).
|
|
579
|
+
* `body` → full `declare module "…" { … }` wrapper generated by the TS printer.
|
|
580
|
+
* `content` (exposed to the stub function) is just the inner interfaces/types
|
|
581
|
+
without the `declare module` wrapper, if you need it for advanced patterns.
|
|
582
|
+
|
|
583
|
+
Per-model TS stubs under `stubs/ts/<Model>.stub` receive:
|
|
584
|
+
|
|
585
|
+
```ts
|
|
586
|
+
(model, imports, content, body, moduleName) => string
|
|
587
|
+
```
|
|
588
|
+
|
|
589
|
+
and are free to ignore or wrap the default `body` however they like.
|
|
590
|
+
|
|
591
|
+
</details>
|
|
592
|
+
|
|
438
593
|
---
|
|
439
594
|
|
|
440
|
-
## 🏗️ Complex Model Stub Example
|
|
595
|
+
## 🏗️ Complex Model Stub Example (PHP)
|
|
441
596
|
|
|
442
597
|
<details>
|
|
443
598
|
<summary>Expand stub</summary>
|
|
@@ -445,11 +600,11 @@ return new class extends Migration
|
|
|
445
600
|
```php
|
|
446
601
|
<?php
|
|
447
602
|
|
|
448
|
-
namespace App
|
|
603
|
+
namespace App\Models;
|
|
449
604
|
|
|
450
605
|
${model.imports}
|
|
451
|
-
use Illuminate
|
|
452
|
-
use Illuminate
|
|
606
|
+
use Illuminate\Database\Eloquent\Model;
|
|
607
|
+
use Illuminate\Database\Eloquent\Relations\{ BelongsTo, HasMany, BelongsToMany };
|
|
453
608
|
|
|
454
609
|
class ${model.className} extends Model
|
|
455
610
|
{
|
|
@@ -457,25 +612,25 @@ class ${model.className} extends Model
|
|
|
457
612
|
|
|
458
613
|
/* Mass Assignment */
|
|
459
614
|
protected $fillable = [
|
|
460
|
-
${model.properties.filter(p => p.fillable).map(p => ` '${p.name}',`).join('
|
|
615
|
+
${model.properties.filter(p => p.fillable).map(p => ` '${p.name}',`).join('\n')}
|
|
461
616
|
];
|
|
462
617
|
protected $guarded = [
|
|
463
|
-
${(model.guarded ?? []).map(n => ` '${n}',`).join('
|
|
618
|
+
${(model.guarded ?? []).map(n => ` '${n}',`).join('\n')}
|
|
464
619
|
];
|
|
465
620
|
|
|
466
621
|
/* Hidden & Casts */
|
|
467
622
|
protected $hidden = [
|
|
468
|
-
${model.properties.filter(p => p.hidden).map(p => ` '${p.name}',`).join('
|
|
623
|
+
${model.properties.filter(p => p.hidden).map(p => ` '${p.name}',`).join('\n')}
|
|
469
624
|
];
|
|
470
625
|
protected $casts = [
|
|
471
|
-
${model.properties.filter(p => p.cast).map(p => ` '${p.name}' => '${p.cast}',`).join('
|
|
472
|
-
${model.properties.filter(p => p.enumRef).map(p => ` '${p.name}' => ${p.enumRef}::class,`).join('
|
|
626
|
+
${model.properties.filter(p => p.cast).map(p => ` '${p.name}' => '${p.cast}',`).join('\n')}
|
|
627
|
+
${model.properties.filter(p => p.enumRef).map(p => ` '${p.name}' => ${p.enumRef}::class,`).join('\n')}
|
|
473
628
|
];
|
|
474
629
|
|
|
475
630
|
/* Interfaces metadata */
|
|
476
631
|
public array $interfaces = [
|
|
477
632
|
${Object.entries(model.interfaces).map(([k,i]) =>
|
|
478
|
-
` '${k}' => {${i.import ? ` import: '${i.import}',` : ''} type: '${i.type}' },`).join('
|
|
633
|
+
` '${k}' => {${i.import ? ` import: '${i.import}',` : ''} type: '${i.type}' },`).join('\n')}
|
|
479
634
|
];
|
|
480
635
|
// This structure is useful for packages like fumeapp/modeltyper which
|
|
481
636
|
// read interface metadata to build TypeScript helpers.
|
|
@@ -483,9 +638,9 @@ ${Object.entries(model.interfaces).map(([k,i]) =>
|
|
|
483
638
|
/* Relationships */
|
|
484
639
|
${model.relations.map(r => {
|
|
485
640
|
const args = [r.modelClass, r.foreignKey ? `'${r.foreignKey}'` : '', r.localKey ? `'${r.localKey}'` : ''].filter(Boolean).join(', ');
|
|
486
|
-
return ` public function ${r.name}(): ${r.type.charAt(0).toUpperCase()+r.type.slice(1)}
|
|
487
|
-
}).join('
|
|
488
|
-
// Or
|
|
641
|
+
return ` public function ${r.name}(): ${r.type.charAt(0).toUpperCase()+r.type.slice(1)}\n {\n return $this->${r.type}(${args});\n }`;
|
|
642
|
+
}).join('\n\n')}
|
|
643
|
+
// Or
|
|
489
644
|
${relationships}
|
|
490
645
|
|
|
491
646
|
${content}
|
|
@@ -506,7 +661,6 @@ protected $casts = [
|
|
|
506
661
|
|
|
507
662
|
---
|
|
508
663
|
|
|
509
|
-
|
|
510
664
|
## 🧩 Custom Migration Rules
|
|
511
665
|
|
|
512
666
|
Point the generator’s `rules` field to a JS file exporting an **array** of
|
|
@@ -543,7 +697,7 @@ module.exports = [
|
|
|
543
697
|
|
|
544
698
|
**Rule execution order**
|
|
545
699
|
|
|
546
|
-
1. Built‑in rules
|
|
700
|
+
1. Built‑in rules
|
|
547
701
|
2. Custom rules (executed in array order)
|
|
548
702
|
|
|
549
703
|
---
|
|
@@ -568,7 +722,7 @@ export interface ColumnDefinition extends DMMF.Field {
|
|
|
568
722
|
relationship?: {
|
|
569
723
|
on: string;
|
|
570
724
|
references?: string[];
|
|
571
|
-
fields: string[]
|
|
725
|
+
fields: string[];
|
|
572
726
|
onDelete?: string;
|
|
573
727
|
onUpdate?: string;
|
|
574
728
|
};
|
|
@@ -586,9 +740,8 @@ def.migrationType // mapped Laravel builder name
|
|
|
586
740
|
def.isId
|
|
587
741
|
```
|
|
588
742
|
|
|
589
|
-
📝 **Prisma DMMF docs:**
|
|
590
|
-
https://github.com/prisma/prisma/blob/main/packages/prisma-schema-wasm/src/__tests__/snapshot/dmmf.md
|
|
591
|
-
|
|
743
|
+
📝 **Prisma DMMF docs:**
|
|
744
|
+
[https://github.com/prisma/prisma/blob/main/packages/prisma-schema-wasm/src/__tests__/snapshot/dmmf.md](https://github.com/prisma/prisma/blob/main/packages/prisma-schema-wasm/src/__tests__/snapshot/dmmf.md)
|
|
592
745
|
|
|
593
746
|
---
|
|
594
747
|
|
|
@@ -603,6 +756,13 @@ You can attach them either:
|
|
|
603
756
|
|
|
604
757
|
> This document focuses on the **new & updated directives** and how they interact with existing ones like `@fillable`, `@hidden`, `@guarded`, `@cast{…}`, `@type{…}`, `@with`, `@trait:…`, `@extend:…`, `@implements:…`, `@observer:…`, `@factory:…`, `@touch{…}`, `@appends{…}`.
|
|
605
758
|
|
|
759
|
+
The same directives are visible to the **PHP generators** (migrator + modeler)
|
|
760
|
+
**and** to the **TS types generator**, which uses them to:
|
|
761
|
+
|
|
762
|
+
* apply `@type{…}` overrides for TS field types/imports,
|
|
763
|
+
* honour `@with` when deciding whether relation properties are optional, and
|
|
764
|
+
* strip directive noise from generated JSDoc/docblocks.
|
|
765
|
+
|
|
606
766
|
---
|
|
607
767
|
|
|
608
768
|
## What’s New
|
|
@@ -615,21 +775,21 @@ You can attach them either:
|
|
|
615
775
|
|
|
616
776
|
## Summary of Directives
|
|
617
777
|
|
|
618
|
-
| Directive | Scope
|
|
619
|
-
| ------------------------------------------------------------------------------------------- |
|
|
620
|
-
| `@fillable` | Field **or** `@fillable{…}` on model
|
|
621
|
-
| `@hidden` | Field **or** `@hidden{…}` on model
|
|
622
|
-
| `@guarded` | Field **or** `@guarded{…}` on model
|
|
623
|
-
| `@cast{…}` | Field
|
|
624
|
-
| `@type{ import:'…', type:'…' }` | Field
|
|
625
|
-
| `@with` / `@with(a,b,…)` | Field / Model
|
|
626
|
-
| `@trait:…` `@extend:…` `@implements:…` `@observer:…` `@factory:…` `@touch{…}` `@appends{…}` | Model
|
|
627
|
-
| `@local` | Relation Field
|
|
628
|
-
| `@silent` | Model / Enum
|
|
629
|
-
| `@morph(…)` | Model
|
|
630
|
-
| `@pivot` / `@pivot(a,b,…)` | Pivot **model** and/or scalar **fields
|
|
631
|
-
| `@withTimestamps` | Pivot **model** / relation **field**
|
|
632
|
-
| `@pivotAlias(name)` | Pivot **model**
|
|
778
|
+
| Directive | Scope | Purpose |
|
|
779
|
+
| ------------------------------------------------------------------------------------------- | ---------------------------------------- | --------------------------------------------------------------------------------------- |
|
|
780
|
+
| `@fillable` | Field **or** `@fillable{…}` on model | Adds field(s) to `$fillable`. |
|
|
781
|
+
| `@hidden` | Field **or** `@hidden{…}` on model | Adds field(s) to `$hidden`. |
|
|
782
|
+
| `@guarded` | Field **or** `@guarded{…}` on model | Adds field(s) to `$guarded`. |
|
|
783
|
+
| `@cast{…}` | Field | Adds a cast entry to `$casts`. |
|
|
784
|
+
| `@type{ import:'…', type:'…' }` | Field | Exposes a PHP/interface/TS type hint for downstream tooling. |
|
|
785
|
+
| `@with` / `@with(a,b,…)` | Field / Model | Marks relations to eager-load via `$with` and as non‑optional in TS types. |
|
|
786
|
+
| `@trait:…` `@extend:…` `@implements:…` `@observer:…` `@factory:…` `@touch{…}` `@appends{…}` | Model | Class customization & extras (traits, parents, observers, factories, touches, appends). |
|
|
787
|
+
| `@local` | Relation Field | Skip generating that **specific relation method** on the model. Replaces `@ignore`. |
|
|
788
|
+
| `@silent` | Model / Enum | Do **not** emit files for this entity (model + migration / enum). |
|
|
789
|
+
| `@morph(…)` | Model | Declare owner-side polymorphic relations; child-side `morphTo` is auto-detected. |
|
|
790
|
+
| `@pivot` / `@pivot(a,b,…)` | Pivot **model** and/or scalar **fields** | Explicitly mark extra pivot columns to include in generated `withPivot(…)` chains. |
|
|
791
|
+
| `@withTimestamps` | Pivot **model** / relation **field** | Instructs the generator to append `->withTimestamps()` on the relation definition. |
|
|
792
|
+
| `@pivotAlias(name)` | Pivot **model** | Sets the pivot attribute alias; generator should add `->as('name')` on the relation. |
|
|
633
793
|
|
|
634
794
|
> **Syntax options**
|
|
635
795
|
> • Inline: `balance Decimal /// @fillable @cast{decimal:2}`
|
|
@@ -646,10 +806,12 @@ You can attach them either:
|
|
|
646
806
|
> `/// @touch{company,profile}`
|
|
647
807
|
> `/// @appends{full_name,age}`
|
|
648
808
|
|
|
649
|
-
> **Note:** Directives like `@fillable`, `@hidden`, `@guarded`, `@with`, `@touch`, and `@appends`
|
|
650
|
-
>
|
|
651
|
-
>
|
|
652
|
-
>
|
|
809
|
+
> **Note:** Directives like `@fillable`, `@hidden`, `@guarded`, `@with`, `@touch`, and `@appends` support **all** of the following syntaxes:
|
|
810
|
+
>
|
|
811
|
+
> * `/// @fillable{name,balance}`
|
|
812
|
+
> * `/// @fillable(name,balance)`
|
|
813
|
+
> * `/// @fillable: name,balance`
|
|
814
|
+
|
|
653
815
|
---
|
|
654
816
|
|
|
655
817
|
## `@local` — Skip a Single Relation Method (replaces `@ignore`)
|
|
@@ -672,7 +834,7 @@ model Account {
|
|
|
672
834
|
}
|
|
673
835
|
```
|
|
674
836
|
|
|
675
|
-
**Effect:** the `user()` method is *not* written to `Account.php`. If scope includes Migrator, the migration also skips generating the FK/constraint. Other methods remain unaffected.
|
|
837
|
+
**Effect:** the `user()` method is *not* written to `Account.php`. If scope includes Migrator, the migration also skips generating the FK/constraint. Other methods remain unaffected. TS types still include the `user?: User | null` navigation property unless you override it with `@type`.
|
|
676
838
|
|
|
677
839
|
---
|
|
678
840
|
|
|
@@ -696,11 +858,10 @@ model AuditTrail {
|
|
|
696
858
|
}
|
|
697
859
|
```
|
|
698
860
|
|
|
699
|
-
**Effect:** no Eloquent model and/or no migration file are emitted for `AuditTrail` depending on the scope. For enums, no PHP enum is emitted.
|
|
861
|
+
**Effect:** no Eloquent model and/or no migration file are emitted for `AuditTrail` depending on the scope. For enums, no PHP enum is emitted. The TS generator can still expose types for `AuditTrail` if you wish (or you can add an additional TS-side filter later).
|
|
700
862
|
|
|
701
863
|
---
|
|
702
864
|
|
|
703
|
-
|
|
704
865
|
## Polymorphic Relations
|
|
705
866
|
|
|
706
867
|
### Auto-Detected Child Side: `morphTo`
|
|
@@ -716,6 +877,16 @@ public function commentable()
|
|
|
716
877
|
|
|
717
878
|
No directive is required for `morphTo`.
|
|
718
879
|
|
|
880
|
+
On the TS side, the generator also detects these pairs and adds a
|
|
881
|
+
corresponding **appended property** like:
|
|
882
|
+
|
|
883
|
+
```ts
|
|
884
|
+
commentable?: any;
|
|
885
|
+
```
|
|
886
|
+
|
|
887
|
+
which you can refine via `@appends{commentable: Comment}` or project‑specific
|
|
888
|
+
TS overrides.
|
|
889
|
+
|
|
719
890
|
### Owner Side via `@morph(…)`
|
|
720
891
|
|
|
721
892
|
Add one or more `@morph(...)` directives to the **owner model’s** docblock to generate Laravel morph methods. Parentheses and quotes inside `raw:"…"` are supported.
|
|
@@ -758,8 +929,8 @@ model User {
|
|
|
758
929
|
/// Owner: Video → morphToMany(Tag::class, 'taggable', 'taggables')
|
|
759
930
|
/// @morph(name: taggable, type: to many, model: Tag, table:"taggables")
|
|
760
931
|
model Video {
|
|
761
|
-
id
|
|
762
|
-
url
|
|
932
|
+
id Int @id @default(autoincrement())
|
|
933
|
+
url String
|
|
763
934
|
}
|
|
764
935
|
```
|
|
765
936
|
|
|
@@ -809,7 +980,7 @@ model Image {
|
|
|
809
980
|
}
|
|
810
981
|
```
|
|
811
982
|
|
|
812
|
-
**C) Polymorphic M
|
|
983
|
+
**C) Polymorphic M:N (`morphToMany` / `morphedByMany`)**
|
|
813
984
|
|
|
814
985
|
```prisma
|
|
815
986
|
/// @morph(name: taggable, type: to many, model: Tag, table: "taggables")
|
|
@@ -863,64 +1034,53 @@ model Activity {
|
|
|
863
1034
|
}
|
|
864
1035
|
```
|
|
865
1036
|
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
#### Other Examples
|
|
1037
|
+
---
|
|
869
1038
|
|
|
870
|
-
|
|
1039
|
+
## Other Examples
|
|
871
1040
|
|
|
872
1041
|
```prisma
|
|
873
|
-
|
|
874
1042
|
/// @fillable{name,balance}
|
|
875
1043
|
/// @hidden{secretToken}
|
|
876
1044
|
model Account {
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
1045
|
+
id Int @id @default(autoincrement())
|
|
1046
|
+
balance Decimal @default(0.0) /// @cast{decimal:2}
|
|
1047
|
+
nickname String /// @fillable @hidden
|
|
1048
|
+
profile Json? /// @type{ import:'@types/forms', type:'ProfileDTO' }
|
|
1049
|
+
company Company? @relation(fields:[companyId], references:[id]) /// @local
|
|
1050
|
+
|
|
1051
|
+
companyId Int?
|
|
1052
|
+
posts Post[] /// @with
|
|
885
1053
|
}
|
|
886
1054
|
|
|
887
|
-
|
|
888
|
-
|
|
889
1055
|
/// @with(posts,comments)
|
|
890
|
-
|
|
891
1056
|
model User {
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
1057
|
+
id Int @id @default(autoincrement())
|
|
1058
|
+
email String
|
|
1059
|
+
posts Post[]
|
|
1060
|
+
comments Comment[]
|
|
896
1061
|
}
|
|
897
|
-
|
|
898
1062
|
```
|
|
899
1063
|
|
|
900
|
-
**Generated
|
|
1064
|
+
**Generated (PHP, simplified)**
|
|
901
1065
|
|
|
902
1066
|
```php
|
|
903
|
-
|
|
904
1067
|
protected $fillable = ['name','balance','nickname'];
|
|
905
|
-
protected $hidden
|
|
906
|
-
protected $casts
|
|
1068
|
+
protected $hidden = ['secretToken','nickname'];
|
|
1069
|
+
protected $casts = ['balance' => 'decimal:2'];
|
|
907
1070
|
|
|
908
1071
|
public array $interfaces = [
|
|
909
|
-
|
|
1072
|
+
'profile' => { import: '@types/forms', type: 'ProfileDTO' },
|
|
910
1073
|
];
|
|
911
1074
|
|
|
912
1075
|
protected $with = ['posts'];
|
|
913
|
-
|
|
914
1076
|
```
|
|
915
1077
|
|
|
916
|
-
`@local` prevents the `company()` relation method.
|
|
917
|
-
|
|
1078
|
+
`@local` prevents the `company()` relation method.
|
|
918
1079
|
Combine multiple inline directives; they’re processed left‑to‑right.
|
|
919
1080
|
|
|
920
|
-
|
|
1081
|
+
### Example: Combined Directives
|
|
921
1082
|
|
|
922
1083
|
```prisma
|
|
923
|
-
|
|
924
1084
|
/// @fillable{name,balance}
|
|
925
1085
|
/// @hidden{secretToken}
|
|
926
1086
|
/// @guarded{password,apiToken}
|
|
@@ -932,22 +1092,20 @@ Combine multiple inline directives; they’re processed left‑to‑right.
|
|
|
932
1092
|
/// @touch{company}
|
|
933
1093
|
/// @appends{full_name}
|
|
934
1094
|
model User {
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
1095
|
+
id Int @id @default(autoincrement())
|
|
1096
|
+
email String @unique /// @hidden @fillable
|
|
1097
|
+
password String /// @hidden @guarded
|
|
1098
|
+
balance Decimal @default(0.0) /// @cast{decimal:2}
|
|
1099
|
+
profile Json? /// @type{ import:'@types/forms', type:'ProfileDTO' }
|
|
1100
|
+
company Company? @relation(fields:[companyId], references:[id]) /// @local
|
|
1101
|
+
companyId Int?
|
|
1102
|
+
posts Post[] /// @with
|
|
943
1103
|
}
|
|
944
|
-
|
|
945
1104
|
```
|
|
946
1105
|
|
|
947
1106
|
**Generated output (simplified)**
|
|
948
1107
|
|
|
949
1108
|
```php
|
|
950
|
-
|
|
951
1109
|
use Illuminate\Auth\Authenticatable;
|
|
952
1110
|
use Illuminate\Auth\User;
|
|
953
1111
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
|
@@ -956,47 +1114,51 @@ use App\Observers\UserObserver;
|
|
|
956
1114
|
|
|
957
1115
|
class User extends User implements AuthenticatableContract
|
|
958
1116
|
{
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
1117
|
+
use HasFactory, Authenticatable;
|
|
1118
|
+
|
|
1119
|
+
protected $fillable = ['name','balance','email'];
|
|
1120
|
+
protected $hidden = ['secretToken','password'];
|
|
1121
|
+
protected $guarded = ['password','apiToken'];
|
|
1122
|
+
protected $casts = ['balance' => 'decimal:2'];
|
|
1123
|
+
protected $touches = ['company'];
|
|
1124
|
+
protected $appends = ['full_name'];
|
|
1125
|
+
protected static string $factory = UserFactory::class;
|
|
1126
|
+
|
|
1127
|
+
protected static function boot()
|
|
1128
|
+
{
|
|
1129
|
+
parent::boot();
|
|
1130
|
+
static::observe(UserObserver::class);
|
|
1131
|
+
}
|
|
1132
|
+
|
|
1133
|
+
public function getFullNameAttribute()
|
|
1134
|
+
{
|
|
1135
|
+
return $this->attributes['full_name'] ?? null;
|
|
1136
|
+
}
|
|
1137
|
+
|
|
1138
|
+
public function posts()
|
|
1139
|
+
{
|
|
1140
|
+
return $this->hasMany(Post::class);
|
|
1141
|
+
}
|
|
984
1142
|
}
|
|
985
1143
|
```
|
|
986
1144
|
|
|
987
1145
|
---
|
|
1146
|
+
|
|
988
1147
|
## Notes & Limitations
|
|
989
1148
|
|
|
990
1149
|
* `@local` is **model-method only**; it does not change the migration. Use `@silent` to suppress a whole model/enum from emission.
|
|
991
1150
|
* Laravel cannot express **composite pivot keys** directly inside `belongsToMany(...)`. If your schema uses composite pivot keys, consider generating a Pivot model or using query/Compoships patterns on the one-to-many sides.
|
|
992
1151
|
* The generator keeps your edits safe via merge markers; conflicts will be surfaced with `<<<<<<<` sections for manual resolution.
|
|
1152
|
+
* TS types are generated from the **same DMMF + directives** as PHP. You can always override a single field type with `@type{…}`.
|
|
1153
|
+
|
|
993
1154
|
---
|
|
994
1155
|
|
|
995
1156
|
## 💡 Tips
|
|
996
1157
|
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1158
|
+
* Combine `migration` & `model` & `ts` in one `customize` command when table names align.
|
|
1159
|
+
* Use `noEmit: true` for dry‑runs or CI validation.
|
|
1160
|
+
* Escape template chars in stub files.
|
|
1161
|
+
* Keep your Prisma schema as the single source of truth; everything else is generated.
|
|
1000
1162
|
|
|
1001
1163
|
---
|
|
1002
1164
|
|
|
@@ -1008,6 +1170,7 @@ Use the library directly in a script or build tool instead of the CLI.
|
|
|
1008
1170
|
import {
|
|
1009
1171
|
generateLaravelSchema,
|
|
1010
1172
|
generateLaravelModels,
|
|
1173
|
+
generateTypesFromPrisma,
|
|
1011
1174
|
sortMigrations,
|
|
1012
1175
|
} from 'prisma-laravel-migrate';
|
|
1013
1176
|
import { readFileSync, writeFileSync } from 'fs';
|
|
@@ -1020,22 +1183,37 @@ import { getDMMF } from '@prisma/sdk';
|
|
|
1020
1183
|
const dmmf = await getDMMF({ datamodel });
|
|
1021
1184
|
|
|
1022
1185
|
/* 2. Run generators entirely in-memory */
|
|
1023
|
-
const migrations = generateLaravelSchema({
|
|
1186
|
+
const migrations = await generateLaravelSchema({
|
|
1024
1187
|
dmmf,
|
|
1025
1188
|
schemaPath, // ← always pass this
|
|
1026
1189
|
generator : { config: {} } as any,
|
|
1027
1190
|
});
|
|
1028
1191
|
|
|
1029
|
-
const { models, enums } = generateLaravelModels({
|
|
1192
|
+
const { models, enums } = await generateLaravelModels({
|
|
1030
1193
|
dmmf,
|
|
1031
1194
|
schemaPath,
|
|
1032
1195
|
generator : { config: {} } as any,
|
|
1033
1196
|
});
|
|
1034
1197
|
|
|
1198
|
+
const tsResult = await generateTypesFromPrisma({
|
|
1199
|
+
dmmf: dmmf as any,
|
|
1200
|
+
schemaPath,
|
|
1201
|
+
generator: {
|
|
1202
|
+
config: {
|
|
1203
|
+
// types-specific overrides
|
|
1204
|
+
moduleName: 'database/prisma',
|
|
1205
|
+
noEmit: true,
|
|
1206
|
+
},
|
|
1207
|
+
output: { value: 'resources/ts/prisma' },
|
|
1208
|
+
} as any,
|
|
1209
|
+
});
|
|
1210
|
+
|
|
1035
1211
|
/* 3. Inspect or write output */
|
|
1036
1212
|
sortMigrations(migrations).forEach(m => {
|
|
1037
1213
|
writeFileSync(`./out/${m.tableName}.php`, m.statements.join('\n'), 'utf8');
|
|
1038
1214
|
});
|
|
1215
|
+
|
|
1216
|
+
// tsResult.models / tsResult.enums contain TS model/enum definitions
|
|
1039
1217
|
})();
|
|
1040
1218
|
```
|
|
1041
1219
|
|
|
@@ -1061,30 +1239,34 @@ generateLaravelSchema({
|
|
|
1061
1239
|
|
|
1062
1240
|
### Public exports
|
|
1063
1241
|
|
|
1064
|
-
| Export
|
|
1065
|
-
|
|
|
1066
|
-
| `generateLaravelSchema`
|
|
1067
|
-
| `generateLaravelModels`
|
|
1068
|
-
| `
|
|
1069
|
-
| `
|
|
1070
|
-
|
|
|
1242
|
+
| Export | Purpose |
|
|
1243
|
+
| ----------------------------------------------------- | ------------------------------------------------------------- |
|
|
1244
|
+
| `generateLaravelSchema` | Build migration objects (and optionally write files) |
|
|
1245
|
+
| `generateLaravelModels` | Build PHP model + enum definitions |
|
|
1246
|
+
| `generateTypesFromPrisma` | Build TS model + enum definitions, with stub + module support |
|
|
1247
|
+
| `sortMigrations` | Topologically sort migrations by FK dependencies |
|
|
1248
|
+
| `Rule` | Type helper for custom migration shortcuts |
|
|
1249
|
+
| *types* (`column-definition-types`, `laravel-config`) | Full TypeScript typings |
|
|
1071
1250
|
|
|
1072
1251
|
```ts
|
|
1073
1252
|
import {
|
|
1074
1253
|
ColumnDefinition,
|
|
1075
1254
|
LaravelSharedConfig,
|
|
1076
1255
|
MigratorConfigOverride,
|
|
1256
|
+
ModelConfigOverride,
|
|
1257
|
+
TypesConfigOverride,
|
|
1077
1258
|
} from 'prisma-laravel-migrate';
|
|
1078
1259
|
```
|
|
1079
1260
|
|
|
1080
|
-
> **Heads-up:**
|
|
1081
|
-
> `generateLaravelSchema` and `
|
|
1082
|
-
> (honouring
|
|
1261
|
+
> **Heads-up:**
|
|
1262
|
+
> `generateLaravelSchema`, `generateLaravelModels`, and `generateTypesFromPrisma`
|
|
1263
|
+
> **write files by default** (honouring their respective `outputDir` settings).
|
|
1083
1264
|
> If you only want the in-memory objects—e.g. to capture the returned
|
|
1084
|
-
> `migrations`, `models`, or `enums` arrays—set
|
|
1265
|
+
> `migrations`, `models`, `enums`, or TS `models`/`enums` arrays—set
|
|
1085
1266
|
> `noEmit: true` in either
|
|
1086
1267
|
>
|
|
1087
1268
|
> * the per-call `generator.config` object:
|
|
1269
|
+
>
|
|
1088
1270
|
> ```ts
|
|
1089
1271
|
> generateLaravelSchema({
|
|
1090
1272
|
> dmmf,
|
|
@@ -1093,18 +1275,20 @@ import {
|
|
|
1093
1275
|
> });
|
|
1094
1276
|
> ```
|
|
1095
1277
|
> * **or** in `prisma/laravel.config.js`:
|
|
1278
|
+
>
|
|
1096
1279
|
> ```js
|
|
1097
1280
|
> module.exports = {
|
|
1098
1281
|
> migrate: { noEmit: true },
|
|
1099
1282
|
> modeler: { noEmit: true },
|
|
1283
|
+
> types: { noEmit: true },
|
|
1100
1284
|
> };
|
|
1101
1285
|
> ```
|
|
1286
|
+
>
|
|
1102
1287
|
> This prevents any files from being created or overwritten while still
|
|
1103
1288
|
> returning the fully-populated data structures for custom processing.
|
|
1104
1289
|
|
|
1105
1290
|
---
|
|
1106
1291
|
|
|
1107
|
-
|
|
1108
1292
|
# Prisma → Laravel Migration Guide
|
|
1109
1293
|
|
|
1110
1294
|
This document explains **how `prisma‑laravel‑migrate` converts your Prisma schema into
|
|
@@ -1118,21 +1302,21 @@ clean, idiomatic Laravel migrations.
|
|
|
1118
1302
|
The generator first turns every Prisma (or native) scalar into the
|
|
1119
1303
|
corresponding **Laravel schema builder method**.
|
|
1120
1304
|
|
|
1121
|
-
| Native type
|
|
1122
|
-
|
|
1123
|
-
| `Text`
|
|
1124
|
-
| `VarChar`
|
|
1125
|
-
| `Boolean`
|
|
1126
|
-
| `TinyInt`
|
|
1305
|
+
| Native type | Laravel method |
|
|
1306
|
+
| ---------------- | ---------------------- |
|
|
1307
|
+
| `Text` | `text()` |
|
|
1308
|
+
| `VarChar` | `string()` |
|
|
1309
|
+
| `Boolean` | `boolean()` |
|
|
1310
|
+
| `TinyInt` | `tinyInteger()` |
|
|
1127
1311
|
| `UnsignedBigInt` | `unsignedBigInteger()` |
|
|
1128
|
-
| `BigInt`
|
|
1129
|
-
| `Decimal`
|
|
1130
|
-
| `Double`
|
|
1131
|
-
| `DateTime`
|
|
1132
|
-
| `Timestamptz`
|
|
1133
|
-
| `Json`
|
|
1134
|
-
| `Uuid`
|
|
1135
|
-
| `Inet`
|
|
1312
|
+
| `BigInt` | `bigInteger()` |
|
|
1313
|
+
| `Decimal` | `decimal()` |
|
|
1314
|
+
| `Double` | `double()` |
|
|
1315
|
+
| `DateTime` | `timestamp()` |
|
|
1316
|
+
| `Timestamptz` | `timestampsTz()` |
|
|
1317
|
+
| `Json` | `json()` |
|
|
1318
|
+
| `Uuid` | `uuid()` |
|
|
1319
|
+
| `Inet` | `ipAddress()` |
|
|
1136
1320
|
|
|
1137
1321
|
*(See source `migrationTypes.ts` for the full mapping.)*
|
|
1138
1322
|
|
|
@@ -1143,17 +1327,17 @@ corresponding **Laravel schema builder method**.
|
|
|
1143
1327
|
After basic mapping, each column is checked against a **rule set**
|
|
1144
1328
|
so that common Laravel helper methods are used instead of verbose definitions.
|
|
1145
1329
|
|
|
1146
|
-
| Rule
|
|
1147
|
-
|
|
1148
|
-
| **Primary ID**
|
|
1149
|
-
| **Timestamps**
|
|
1150
|
-
| **TimestampsTz**
|
|
1151
|
-
| **Soft‑deletes**
|
|
1152
|
-
| **Soft‑deletesTz**
|
|
1153
|
-
| **Remember token**
|
|
1154
|
-
| **`foreignId`**
|
|
1155
|
-
| **Morphs / NullableMorphs** | `<base>_id` & `<base>_type` combo
|
|
1156
|
-
| **UUID / ULID Morphs**
|
|
1330
|
+
| Rule | Condition in Prisma | Generated code |
|
|
1331
|
+
| --------------------------- | ---------------------------------------------------------- | ------------------------------------------------------------- |
|
|
1332
|
+
| **Primary ID** | `id BigInt @id @default(autoincrement())` | `$table->id();` |
|
|
1333
|
+
| **Timestamps** | `created_at / updated_at` pair (`DateTime` or `Timestamp`) | `$table->timestamps();` |
|
|
1334
|
+
| **TimestampsTz** | Same pair but `DateTimeTz` / tz‑aware types | `$table->timestampsTz();` |
|
|
1335
|
+
| **Soft‑deletes** | `deleted_at DateTime` | `$table->softDeletes();` |
|
|
1336
|
+
| **Soft‑deletesTz** | `deleted_at DateTimeTz` | `$table->softDeletesTz();` |
|
|
1337
|
+
| **Remember token** | `remember_token String?` | `$table->rememberToken();` |
|
|
1338
|
+
| **`foreignId`** | `<col>_id` + `@relation(...)` | `$table->foreignId('…')->constrained();` |
|
|
1339
|
+
| **Morphs / NullableMorphs** | `<base>_id` & `<base>_type` combo | `$table->morphs('base');` / `$table->nullableMorphs('base');` |
|
|
1340
|
+
| **UUID / ULID Morphs** | Same but with `Uuid` / `Ulid` id column | `$table->uuidMorphs()` & friends |
|
|
1157
1341
|
|
|
1158
1342
|
If a rule fires, both columns involved are marked *ignored* so the fallback
|
|
1159
1343
|
builder doesn't emit them twice.
|
|
@@ -1183,7 +1367,7 @@ Foreign‑key references (`@relation`) then add a separate `$table->foreign()` c
|
|
|
1183
1367
|
```prisma
|
|
1184
1368
|
model Post {
|
|
1185
1369
|
id Int @id @default(autoincrement()) // ➜ $table->id()
|
|
1186
|
-
title String
|
|
1370
|
+
title String
|
|
1187
1371
|
body Text
|
|
1188
1372
|
author_id Int
|
|
1189
1373
|
author User @relation(fields:[author_id], references:[id])
|
|
@@ -1226,7 +1410,9 @@ $table->softDeletes();
|
|
|
1226
1410
|
for shortcut rules; non‑standard names fall back to the generic builder.
|
|
1227
1411
|
* Remember to run migrations **after** generating to ensure FK order is correct
|
|
1228
1412
|
(the tool topologically sorts tables to avoid dependency loops).
|
|
1413
|
+
|
|
1229
1414
|
---
|
|
1415
|
+
|
|
1230
1416
|
### Custom `defaultMaps` for `formatDefault`
|
|
1231
1417
|
|
|
1232
1418
|
You can override the built‑in `formatDefault()` logic without forking the package by
|
|
@@ -1267,14 +1453,15 @@ module.exports = {
|
|
|
1267
1453
|
4. Keys are matched *case‑sensitive* to the Prisma default function
|
|
1268
1454
|
(`uuid`, `cuid`, `sequence`, etc.).
|
|
1269
1455
|
|
|
1270
|
-
> **Reference** – full list of Prisma `@@default()` helpers
|
|
1271
|
-
>
|
|
1456
|
+
> **Reference** – full list of Prisma `@@default()` helpers
|
|
1457
|
+
> [https://www.prisma.io/docs/orm/reference/prisma-schema-reference#default](https://www.prisma.io/docs/orm/reference/prisma-schema-reference#default)
|
|
1458
|
+
|
|
1272
1459
|
---
|
|
1273
|
-
Happy scaffolding! 🎉
|
|
1274
1460
|
|
|
1461
|
+
Happy scaffolding! 🎉
|
|
1275
1462
|
|
|
1276
1463
|
---
|
|
1277
1464
|
|
|
1278
1465
|
## 📜 License
|
|
1279
1466
|
|
|
1280
|
-
MIT — Happy scaffolding! 🎉
|
|
1467
|
+
MIT — Happy scaffolding! 🎉
|