create-phoenixjs 0.1.4 → 0.1.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/package.json +1 -1
- package/template/config/database.ts +13 -1
- package/template/database/migrations/2024_01_01_000000_create_test_users_cli_table.ts +16 -0
- package/template/database/migrations/20260108165704_TestCliMigration.ts +16 -0
- package/template/database/migrations/2026_01_08_16_57_04_CreateTestMigrationsTable.ts +21 -0
- package/template/framework/cli/artisan.ts +12 -0
- package/template/framework/cli/commands/MakeModelCommand.ts +33 -3
- package/template/framework/database/DatabaseManager.ts +133 -0
- package/template/framework/database/connection/Connection.ts +71 -0
- package/template/framework/database/connection/ConnectionFactory.ts +30 -0
- package/template/framework/database/connection/PostgresConnection.ts +159 -0
- package/template/framework/database/console/MakeMigrationCommand.ts +58 -0
- package/template/framework/database/console/MigrateCommand.ts +32 -0
- package/template/framework/database/console/MigrateResetCommand.ts +31 -0
- package/template/framework/database/console/MigrateRollbackCommand.ts +31 -0
- package/template/framework/database/console/MigrateStatusCommand.ts +38 -0
- package/template/framework/database/migrations/DatabaseMigrationRepository.ts +122 -0
- package/template/framework/database/migrations/Migration.ts +5 -0
- package/template/framework/database/migrations/MigrationRepository.ts +46 -0
- package/template/framework/database/migrations/Migrator.ts +249 -0
- package/template/framework/database/migrations/index.ts +4 -0
- package/template/framework/database/orm/BelongsTo.ts +246 -0
- package/template/framework/database/orm/BelongsToMany.ts +570 -0
- package/template/framework/database/orm/Builder.ts +160 -0
- package/template/framework/database/orm/EagerLoadingBuilder.ts +324 -0
- package/template/framework/database/orm/HasMany.ts +303 -0
- package/template/framework/database/orm/HasManyThrough.ts +282 -0
- package/template/framework/database/orm/HasOne.ts +201 -0
- package/template/framework/database/orm/HasOneThrough.ts +281 -0
- package/template/framework/database/orm/Model.ts +1802 -0
- package/template/framework/database/orm/Relation.ts +342 -0
- package/template/framework/database/orm/Scope.ts +14 -0
- package/template/framework/database/orm/SoftDeletes.ts +160 -0
- package/template/framework/database/orm/index.ts +54 -0
- package/template/framework/database/orm/scopes/SoftDeletingScope.ts +58 -0
- package/template/framework/database/pagination/LengthAwarePaginator.ts +55 -0
- package/template/framework/database/pagination/Paginator.ts +110 -0
- package/template/framework/database/pagination/index.ts +2 -0
- package/template/framework/database/query/Builder.ts +918 -0
- package/template/framework/database/query/DB.ts +139 -0
- package/template/framework/database/query/grammars/Grammar.ts +430 -0
- package/template/framework/database/query/grammars/PostgresGrammar.ts +224 -0
- package/template/framework/database/query/grammars/index.ts +6 -0
- package/template/framework/database/query/index.ts +8 -0
- package/template/framework/database/query/types.ts +196 -0
- package/template/framework/database/schema/Blueprint.ts +478 -0
- package/template/framework/database/schema/Schema.ts +149 -0
- package/template/framework/database/schema/SchemaBuilder.ts +152 -0
- package/template/framework/database/schema/grammars/PostgresSchemaGrammar.ts +293 -0
- package/template/framework/database/schema/grammars/index.ts +5 -0
- package/template/framework/database/schema/index.ts +9 -0
- package/template/package.json +4 -1
|
@@ -0,0 +1,478 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PhoenixJS ORM - Blueprint
|
|
3
|
+
*
|
|
4
|
+
* Defines the structure of a database table. Used to create or modify
|
|
5
|
+
* table schemas via the Schema Builder.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Column definition representing a single column in the blueprint
|
|
10
|
+
*/
|
|
11
|
+
export interface ColumnDefinition {
|
|
12
|
+
name: string;
|
|
13
|
+
type: string;
|
|
14
|
+
length?: number;
|
|
15
|
+
precision?: number;
|
|
16
|
+
scale?: number;
|
|
17
|
+
nullable: boolean;
|
|
18
|
+
primary: boolean;
|
|
19
|
+
unique: boolean;
|
|
20
|
+
autoIncrement: boolean;
|
|
21
|
+
defaultValue?: string | number | boolean | null;
|
|
22
|
+
references?: {
|
|
23
|
+
column: string;
|
|
24
|
+
table: string;
|
|
25
|
+
onDelete?: string;
|
|
26
|
+
onUpdate?: string;
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Index definition
|
|
32
|
+
*/
|
|
33
|
+
export interface IndexDefinition {
|
|
34
|
+
name: string;
|
|
35
|
+
columns: string[];
|
|
36
|
+
unique: boolean;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Foreign key definition
|
|
41
|
+
*/
|
|
42
|
+
export interface ForeignKeyDefinition {
|
|
43
|
+
columns: string[];
|
|
44
|
+
references: string[];
|
|
45
|
+
on: string;
|
|
46
|
+
onDelete?: string;
|
|
47
|
+
onUpdate?: string;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Column builder for fluent column configuration
|
|
52
|
+
*/
|
|
53
|
+
export class ColumnBuilder {
|
|
54
|
+
constructor(private column: ColumnDefinition) { }
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Allow NULL values for this column
|
|
58
|
+
*/
|
|
59
|
+
nullable(): this {
|
|
60
|
+
this.column.nullable = true;
|
|
61
|
+
return this;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Add UNIQUE constraint to this column
|
|
66
|
+
*/
|
|
67
|
+
unique(): this {
|
|
68
|
+
this.column.unique = true;
|
|
69
|
+
return this;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Set this column as PRIMARY KEY
|
|
74
|
+
*/
|
|
75
|
+
primary(): this {
|
|
76
|
+
this.column.primary = true;
|
|
77
|
+
return this;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Set a default value for this column
|
|
82
|
+
*/
|
|
83
|
+
default(value: string | number | boolean | null): this {
|
|
84
|
+
this.column.defaultValue = value;
|
|
85
|
+
return this;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Set foreign key reference
|
|
90
|
+
*/
|
|
91
|
+
references(column: string): this & { on(table: string): this } {
|
|
92
|
+
this.column.references = { column, table: '' };
|
|
93
|
+
const self = this;
|
|
94
|
+
return Object.assign(this, {
|
|
95
|
+
on(table: string): ColumnBuilder {
|
|
96
|
+
self.column.references!.table = table;
|
|
97
|
+
return self;
|
|
98
|
+
},
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Set ON DELETE action for foreign key
|
|
104
|
+
*/
|
|
105
|
+
onDelete(action: 'CASCADE' | 'SET NULL' | 'RESTRICT' | 'NO ACTION'): this {
|
|
106
|
+
if (this.column.references) {
|
|
107
|
+
this.column.references.onDelete = action;
|
|
108
|
+
}
|
|
109
|
+
return this;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Set ON UPDATE action for foreign key
|
|
114
|
+
*/
|
|
115
|
+
onUpdate(action: 'CASCADE' | 'SET NULL' | 'RESTRICT' | 'NO ACTION'): this {
|
|
116
|
+
if (this.column.references) {
|
|
117
|
+
this.column.references.onUpdate = action;
|
|
118
|
+
}
|
|
119
|
+
return this;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Get the column definition
|
|
124
|
+
*/
|
|
125
|
+
getColumn(): ColumnDefinition {
|
|
126
|
+
return this.column;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Blueprint for defining table structure
|
|
132
|
+
*/
|
|
133
|
+
export class Blueprint {
|
|
134
|
+
private columns: ColumnDefinition[] = [];
|
|
135
|
+
private indexes: IndexDefinition[] = [];
|
|
136
|
+
private foreignKeys: ForeignKeyDefinition[] = [];
|
|
137
|
+
private droppedColumns: string[] = [];
|
|
138
|
+
private droppedIndexes: string[] = [];
|
|
139
|
+
|
|
140
|
+
constructor(private tableName: string) { }
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Create a new column with base definition
|
|
144
|
+
*/
|
|
145
|
+
private addColumn(name: string, type: string, options: Partial<ColumnDefinition> = {}): ColumnBuilder {
|
|
146
|
+
const column: ColumnDefinition = {
|
|
147
|
+
name,
|
|
148
|
+
type,
|
|
149
|
+
nullable: false,
|
|
150
|
+
primary: false,
|
|
151
|
+
unique: false,
|
|
152
|
+
autoIncrement: false,
|
|
153
|
+
...options,
|
|
154
|
+
};
|
|
155
|
+
this.columns.push(column);
|
|
156
|
+
return new ColumnBuilder(column);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// ============================================
|
|
160
|
+
// Primary Key / ID Columns
|
|
161
|
+
// ============================================
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Auto-incrementing SERIAL primary key
|
|
165
|
+
*/
|
|
166
|
+
id(column: string = 'id'): ColumnBuilder {
|
|
167
|
+
return this.addColumn(column, 'SERIAL', { primary: true, autoIncrement: true });
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Auto-incrementing BIGSERIAL primary key
|
|
172
|
+
*/
|
|
173
|
+
bigId(column: string = 'id'): ColumnBuilder {
|
|
174
|
+
return this.addColumn(column, 'BIGSERIAL', { primary: true, autoIncrement: true });
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* UUID primary key
|
|
179
|
+
*/
|
|
180
|
+
uuid(column: string = 'id'): ColumnBuilder {
|
|
181
|
+
return this.addColumn(column, 'UUID', { primary: true });
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// ============================================
|
|
185
|
+
// String Columns
|
|
186
|
+
// ============================================
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* VARCHAR column with optional length
|
|
190
|
+
*/
|
|
191
|
+
string(column: string, length: number = 255): ColumnBuilder {
|
|
192
|
+
return this.addColumn(column, 'VARCHAR', { length });
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* CHAR column with fixed length
|
|
197
|
+
*/
|
|
198
|
+
char(column: string, length: number = 255): ColumnBuilder {
|
|
199
|
+
return this.addColumn(column, 'CHAR', { length });
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* TEXT column for long strings
|
|
204
|
+
*/
|
|
205
|
+
text(column: string): ColumnBuilder {
|
|
206
|
+
return this.addColumn(column, 'TEXT');
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// ============================================
|
|
210
|
+
// Numeric Columns
|
|
211
|
+
// ============================================
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* INTEGER column
|
|
215
|
+
*/
|
|
216
|
+
integer(column: string): ColumnBuilder {
|
|
217
|
+
return this.addColumn(column, 'INTEGER');
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* SMALLINT column
|
|
222
|
+
*/
|
|
223
|
+
smallInteger(column: string): ColumnBuilder {
|
|
224
|
+
return this.addColumn(column, 'SMALLINT');
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* BIGINT column
|
|
229
|
+
*/
|
|
230
|
+
bigInteger(column: string): ColumnBuilder {
|
|
231
|
+
return this.addColumn(column, 'BIGINT');
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* REAL/FLOAT column
|
|
236
|
+
*/
|
|
237
|
+
float(column: string, precision?: number): ColumnBuilder {
|
|
238
|
+
return this.addColumn(column, 'REAL', { precision });
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* DOUBLE PRECISION column
|
|
243
|
+
*/
|
|
244
|
+
double(column: string): ColumnBuilder {
|
|
245
|
+
return this.addColumn(column, 'DOUBLE PRECISION');
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* NUMERIC/DECIMAL column
|
|
250
|
+
*/
|
|
251
|
+
decimal(column: string, precision: number = 8, scale: number = 2): ColumnBuilder {
|
|
252
|
+
return this.addColumn(column, 'NUMERIC', { precision, scale });
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
// ============================================
|
|
256
|
+
// Boolean Column
|
|
257
|
+
// ============================================
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
* BOOLEAN column
|
|
261
|
+
*/
|
|
262
|
+
boolean(column: string): ColumnBuilder {
|
|
263
|
+
return this.addColumn(column, 'BOOLEAN');
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
// ============================================
|
|
267
|
+
// Date/Time Columns
|
|
268
|
+
// ============================================
|
|
269
|
+
|
|
270
|
+
/**
|
|
271
|
+
* DATE column
|
|
272
|
+
*/
|
|
273
|
+
date(column: string): ColumnBuilder {
|
|
274
|
+
return this.addColumn(column, 'DATE');
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
/**
|
|
278
|
+
* TIME column
|
|
279
|
+
*/
|
|
280
|
+
time(column: string): ColumnBuilder {
|
|
281
|
+
return this.addColumn(column, 'TIME');
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
/**
|
|
285
|
+
* TIMESTAMP column
|
|
286
|
+
*/
|
|
287
|
+
timestamp(column: string): ColumnBuilder {
|
|
288
|
+
return this.addColumn(column, 'TIMESTAMP');
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
/**
|
|
292
|
+
* TIMESTAMP WITH TIME ZONE column
|
|
293
|
+
*/
|
|
294
|
+
timestampTz(column: string): ColumnBuilder {
|
|
295
|
+
return this.addColumn(column, 'TIMESTAMPTZ');
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
/**
|
|
299
|
+
* Add created_at and updated_at TIMESTAMP columns
|
|
300
|
+
*/
|
|
301
|
+
timestamps(): void {
|
|
302
|
+
this.timestamp('created_at').nullable().default('CURRENT_TIMESTAMP');
|
|
303
|
+
this.timestamp('updated_at').nullable().default('CURRENT_TIMESTAMP');
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
/**
|
|
307
|
+
* Add soft delete deleted_at column
|
|
308
|
+
*/
|
|
309
|
+
softDeletes(column: string = 'deleted_at'): ColumnBuilder {
|
|
310
|
+
return this.timestamp(column).nullable();
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
// ============================================
|
|
314
|
+
// JSON Columns
|
|
315
|
+
// ============================================
|
|
316
|
+
|
|
317
|
+
/**
|
|
318
|
+
* JSONB column (PostgreSQL optimized JSON)
|
|
319
|
+
*/
|
|
320
|
+
json(column: string): ColumnBuilder {
|
|
321
|
+
return this.addColumn(column, 'JSONB');
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
/**
|
|
325
|
+
* JSON column (plain JSON storage)
|
|
326
|
+
*/
|
|
327
|
+
jsonPlain(column: string): ColumnBuilder {
|
|
328
|
+
return this.addColumn(column, 'JSON');
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
// ============================================
|
|
332
|
+
// Binary Columns
|
|
333
|
+
// ============================================
|
|
334
|
+
|
|
335
|
+
/**
|
|
336
|
+
* BYTEA column for binary data
|
|
337
|
+
*/
|
|
338
|
+
binary(column: string): ColumnBuilder {
|
|
339
|
+
return this.addColumn(column, 'BYTEA');
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
// ============================================
|
|
343
|
+
// Special Columns
|
|
344
|
+
// ============================================
|
|
345
|
+
|
|
346
|
+
/**
|
|
347
|
+
* INET column for IP addresses
|
|
348
|
+
*/
|
|
349
|
+
ipAddress(column: string = 'ip_address'): ColumnBuilder {
|
|
350
|
+
return this.addColumn(column, 'INET');
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
/**
|
|
354
|
+
* MACADDR column for MAC addresses
|
|
355
|
+
*/
|
|
356
|
+
macAddress(column: string = 'mac_address'): ColumnBuilder {
|
|
357
|
+
return this.addColumn(column, 'MACADDR');
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
// ============================================
|
|
361
|
+
// Indexes
|
|
362
|
+
// ============================================
|
|
363
|
+
|
|
364
|
+
/**
|
|
365
|
+
* Add an index on columns
|
|
366
|
+
*/
|
|
367
|
+
index(columns: string | string[], name?: string): void {
|
|
368
|
+
const cols = Array.isArray(columns) ? columns : [columns];
|
|
369
|
+
const indexName = name || `${this.tableName}_${cols.join('_')}_index`;
|
|
370
|
+
this.indexes.push({ name: indexName, columns: cols, unique: false });
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
/**
|
|
374
|
+
* Add a unique index
|
|
375
|
+
*/
|
|
376
|
+
uniqueIndex(columns: string | string[], name?: string): void {
|
|
377
|
+
const cols = Array.isArray(columns) ? columns : [columns];
|
|
378
|
+
const indexName = name || `${this.tableName}_${cols.join('_')}_unique`;
|
|
379
|
+
this.indexes.push({ name: indexName, columns: cols, unique: true });
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
// ============================================
|
|
383
|
+
// Foreign Keys
|
|
384
|
+
// ============================================
|
|
385
|
+
|
|
386
|
+
/**
|
|
387
|
+
* Add a foreign key constraint
|
|
388
|
+
*/
|
|
389
|
+
foreign(columns: string | string[]): {
|
|
390
|
+
references: (refs: string | string[]) => {
|
|
391
|
+
on: (table: string) => {
|
|
392
|
+
onDelete: (action: string) => void;
|
|
393
|
+
onUpdate: (action: string) => void;
|
|
394
|
+
};
|
|
395
|
+
};
|
|
396
|
+
} {
|
|
397
|
+
const cols = Array.isArray(columns) ? columns : [columns];
|
|
398
|
+
const fk: ForeignKeyDefinition = {
|
|
399
|
+
columns: cols,
|
|
400
|
+
references: [],
|
|
401
|
+
on: '',
|
|
402
|
+
};
|
|
403
|
+
|
|
404
|
+
return {
|
|
405
|
+
references: (refs: string | string[]) => {
|
|
406
|
+
fk.references = Array.isArray(refs) ? refs : [refs];
|
|
407
|
+
return {
|
|
408
|
+
on: (table: string) => {
|
|
409
|
+
fk.on = table;
|
|
410
|
+
this.foreignKeys.push(fk);
|
|
411
|
+
return {
|
|
412
|
+
onDelete: (action: string) => {
|
|
413
|
+
fk.onDelete = action;
|
|
414
|
+
},
|
|
415
|
+
onUpdate: (action: string) => {
|
|
416
|
+
fk.onUpdate = action;
|
|
417
|
+
},
|
|
418
|
+
};
|
|
419
|
+
},
|
|
420
|
+
};
|
|
421
|
+
},
|
|
422
|
+
};
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
// ============================================
|
|
426
|
+
// Drop Operations (for alter table)
|
|
427
|
+
// ============================================
|
|
428
|
+
|
|
429
|
+
/**
|
|
430
|
+
* Drop a column
|
|
431
|
+
*/
|
|
432
|
+
dropColumn(column: string | string[]): void {
|
|
433
|
+
const cols = Array.isArray(column) ? column : [column];
|
|
434
|
+
this.droppedColumns.push(...cols);
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
/**
|
|
438
|
+
* Drop an index
|
|
439
|
+
*/
|
|
440
|
+
dropIndex(name: string): void {
|
|
441
|
+
this.droppedIndexes.push(name);
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
// ============================================
|
|
445
|
+
// Getters
|
|
446
|
+
// ============================================
|
|
447
|
+
|
|
448
|
+
getTableName(): string {
|
|
449
|
+
return this.tableName;
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
getColumns(): ColumnDefinition[] {
|
|
453
|
+
return this.columns;
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
getIndexes(): IndexDefinition[] {
|
|
457
|
+
return this.indexes;
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
getForeignKeys(): ForeignKeyDefinition[] {
|
|
461
|
+
return this.foreignKeys;
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
getDroppedColumns(): string[] {
|
|
465
|
+
return this.droppedColumns;
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
getDroppedIndexes(): string[] {
|
|
469
|
+
return this.droppedIndexes;
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
/**
|
|
473
|
+
* Check if this is a create table operation
|
|
474
|
+
*/
|
|
475
|
+
isCreating(): boolean {
|
|
476
|
+
return this.columns.length > 0 && this.droppedColumns.length === 0;
|
|
477
|
+
}
|
|
478
|
+
}
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PhoenixJS ORM - Schema Facade
|
|
3
|
+
*
|
|
4
|
+
* Static facade for schema operations, providing a simple API for
|
|
5
|
+
* creating and modifying database tables.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type { Connection } from '../connection/Connection';
|
|
9
|
+
import { Blueprint } from './Blueprint';
|
|
10
|
+
import { SchemaBuilder } from './SchemaBuilder';
|
|
11
|
+
import { PostgresSchemaGrammar } from './grammars/PostgresSchemaGrammar';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Schema - Static facade for database schema operations
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```typescript
|
|
18
|
+
* Schema.init(connection);
|
|
19
|
+
*
|
|
20
|
+
* await Schema.create('users', (table) => {
|
|
21
|
+
* table.id();
|
|
22
|
+
* table.string('name');
|
|
23
|
+
* table.string('email').unique();
|
|
24
|
+
* table.timestamps();
|
|
25
|
+
* });
|
|
26
|
+
*
|
|
27
|
+
* await Schema.dropIfExists('users');
|
|
28
|
+
* ```
|
|
29
|
+
*/
|
|
30
|
+
export class Schema {
|
|
31
|
+
private static builder: SchemaBuilder | null = null;
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Initialize the Schema facade with a connection
|
|
35
|
+
*/
|
|
36
|
+
static init(connection: Connection, grammar?: PostgresSchemaGrammar): void {
|
|
37
|
+
Schema.builder = new SchemaBuilder(connection, grammar);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Get the schema builder instance
|
|
42
|
+
*/
|
|
43
|
+
static getBuilder(): SchemaBuilder {
|
|
44
|
+
if (!Schema.builder) {
|
|
45
|
+
throw new Error('Schema not initialized. Call Schema.init(connection) first.');
|
|
46
|
+
}
|
|
47
|
+
return Schema.builder;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Create a new table
|
|
52
|
+
*/
|
|
53
|
+
static async create(table: string, callback: (blueprint: Blueprint) => void): Promise<void> {
|
|
54
|
+
return Schema.getBuilder().create(table, callback);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Modify an existing table
|
|
59
|
+
*/
|
|
60
|
+
static async table(table: string, callback: (blueprint: Blueprint) => void): Promise<void> {
|
|
61
|
+
return Schema.getBuilder().table(table, callback);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Drop a table
|
|
66
|
+
*/
|
|
67
|
+
static async drop(table: string): Promise<void> {
|
|
68
|
+
return Schema.getBuilder().drop(table);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Drop a table if it exists
|
|
73
|
+
*/
|
|
74
|
+
static async dropIfExists(table: string): Promise<void> {
|
|
75
|
+
return Schema.getBuilder().dropIfExists(table);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Rename a table
|
|
80
|
+
*/
|
|
81
|
+
static async rename(from: string, to: string): Promise<void> {
|
|
82
|
+
return Schema.getBuilder().rename(from, to);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Check if a table exists
|
|
87
|
+
*/
|
|
88
|
+
static async hasTable(table: string): Promise<boolean> {
|
|
89
|
+
return Schema.getBuilder().hasTable(table);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Check if a column exists in a table
|
|
94
|
+
*/
|
|
95
|
+
static async hasColumn(table: string, column: string): Promise<boolean> {
|
|
96
|
+
return Schema.getBuilder().hasColumn(table, column);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Get all tables in the database
|
|
101
|
+
*/
|
|
102
|
+
static async getTables(): Promise<string[]> {
|
|
103
|
+
return Schema.getBuilder().getTables();
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Get all columns for a table
|
|
108
|
+
*/
|
|
109
|
+
static async getColumns(
|
|
110
|
+
table: string
|
|
111
|
+
): Promise<
|
|
112
|
+
{
|
|
113
|
+
column_name: string;
|
|
114
|
+
data_type: string;
|
|
115
|
+
is_nullable: string;
|
|
116
|
+
column_default: string | null;
|
|
117
|
+
}[]
|
|
118
|
+
> {
|
|
119
|
+
return Schema.getBuilder().getColumns(table);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Truncate a table
|
|
124
|
+
*/
|
|
125
|
+
static async truncate(table: string): Promise<void> {
|
|
126
|
+
return Schema.getBuilder().truncate(table);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Drop all tables (use with caution!)
|
|
131
|
+
*/
|
|
132
|
+
static async dropAllTables(): Promise<void> {
|
|
133
|
+
return Schema.getBuilder().dropAllTables();
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Create a new SchemaBuilder instance without using the facade
|
|
138
|
+
*/
|
|
139
|
+
static connection(connection: Connection, grammar?: PostgresSchemaGrammar): SchemaBuilder {
|
|
140
|
+
return new SchemaBuilder(connection, grammar);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Reset the facade (useful for testing)
|
|
145
|
+
*/
|
|
146
|
+
static reset(): void {
|
|
147
|
+
Schema.builder = null;
|
|
148
|
+
}
|
|
149
|
+
}
|