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,246 @@
|
|
|
1
|
+
import { Relation } from './Relation';
|
|
2
|
+
import type { Model } from './Model';
|
|
3
|
+
import type { Binding } from '../query/types';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* BelongsTo Relationship - Represents the inverse of a one-to-one or one-to-many.
|
|
7
|
+
*
|
|
8
|
+
* The foreign key is on the CURRENT model's table, referencing the parent.
|
|
9
|
+
*
|
|
10
|
+
* Example Schema:
|
|
11
|
+
* - users table: id, name, email
|
|
12
|
+
* - profiles table: id, user_id, bio
|
|
13
|
+
*
|
|
14
|
+
* The `user_id` column on `profiles` references `users.id`.
|
|
15
|
+
* Profile "belongs to" User.
|
|
16
|
+
*
|
|
17
|
+
* Usage:
|
|
18
|
+
* ```typescript
|
|
19
|
+
* class Profile extends Model {
|
|
20
|
+
* user() {
|
|
21
|
+
* return this.belongsTo(User, 'user_id', 'id');
|
|
22
|
+
* }
|
|
23
|
+
* }
|
|
24
|
+
*
|
|
25
|
+
* // Get the profile's user
|
|
26
|
+
* const profile = await Profile.find(1);
|
|
27
|
+
* const user = await profile.user().get();
|
|
28
|
+
* ```
|
|
29
|
+
*
|
|
30
|
+
* Also used for the inverse of HasMany:
|
|
31
|
+
* ```typescript
|
|
32
|
+
* class Post extends Model {
|
|
33
|
+
* author() {
|
|
34
|
+
* return this.belongsTo(User, 'user_id', 'id');
|
|
35
|
+
* }
|
|
36
|
+
* }
|
|
37
|
+
* ```
|
|
38
|
+
*
|
|
39
|
+
* @template TRelated - The type of the related (parent) model
|
|
40
|
+
*/
|
|
41
|
+
export class BelongsTo<TRelated extends Model> extends Relation<TRelated> {
|
|
42
|
+
/**
|
|
43
|
+
* The foreign key on the child (current) model's table.
|
|
44
|
+
* Example: 'user_id' on profiles/posts table
|
|
45
|
+
*/
|
|
46
|
+
protected foreignKey: string;
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* The owner (primary) key on the parent model's table.
|
|
50
|
+
* Example: 'id' on users table
|
|
51
|
+
*/
|
|
52
|
+
protected ownerKey: string;
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Create a new BelongsTo relationship instance.
|
|
56
|
+
*
|
|
57
|
+
* @param parent - The child model instance (the one with the foreign key)
|
|
58
|
+
* @param related - An instance of the parent model class
|
|
59
|
+
* @param foreignKey - The foreign key column on the child model (e.g., 'user_id')
|
|
60
|
+
* @param ownerKey - The owner key column on the parent model (e.g., 'id')
|
|
61
|
+
*/
|
|
62
|
+
constructor(
|
|
63
|
+
parent: Model,
|
|
64
|
+
related: TRelated,
|
|
65
|
+
foreignKey: string,
|
|
66
|
+
ownerKey: string
|
|
67
|
+
) {
|
|
68
|
+
super(parent, related);
|
|
69
|
+
|
|
70
|
+
this.foreignKey = foreignKey;
|
|
71
|
+
this.ownerKey = ownerKey;
|
|
72
|
+
|
|
73
|
+
// Re-add constraints now that keys are set
|
|
74
|
+
this.reinitializeConstraints();
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Re-initialize constraints after keys are set.
|
|
79
|
+
*/
|
|
80
|
+
private reinitializeConstraints(): void {
|
|
81
|
+
this.query = this.newQuery();
|
|
82
|
+
const foreignKeyValue = this.parent.getAttribute(this.foreignKey);
|
|
83
|
+
|
|
84
|
+
if (foreignKeyValue !== undefined && foreignKeyValue !== null) {
|
|
85
|
+
this.query.where(this.ownerKey, '=', foreignKeyValue as Binding);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Add the constraints for the BelongsTo relationship.
|
|
91
|
+
* Called by parent constructor - keys may not be set yet.
|
|
92
|
+
*/
|
|
93
|
+
public addConstraints(): void {
|
|
94
|
+
// Constraints are added in reinitializeConstraints after keys are set
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Get the results of the BelongsTo relationship.
|
|
99
|
+
*
|
|
100
|
+
* Returns the parent model or null if none exists.
|
|
101
|
+
*
|
|
102
|
+
* @returns The parent model or null
|
|
103
|
+
*/
|
|
104
|
+
public async getResults(): Promise<TRelated | null> {
|
|
105
|
+
const foreignKeyValue = this.parent.getAttribute(this.foreignKey);
|
|
106
|
+
|
|
107
|
+
// If child doesn't have a foreign key value, return null
|
|
108
|
+
if (foreignKeyValue === undefined || foreignKeyValue === null) {
|
|
109
|
+
return null;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
return this.first();
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Associate a parent model with this child model.
|
|
117
|
+
*
|
|
118
|
+
* This sets the foreign key on the child model to the parent's primary key.
|
|
119
|
+
* Note: This does NOT save the child model - you must call save() yourself.
|
|
120
|
+
*
|
|
121
|
+
* Usage:
|
|
122
|
+
* ```typescript
|
|
123
|
+
* const profile = new Profile({ bio: 'Hello' });
|
|
124
|
+
* const user = await User.find(1);
|
|
125
|
+
* profile.user().associate(user);
|
|
126
|
+
* await profile.save();
|
|
127
|
+
* ```
|
|
128
|
+
*
|
|
129
|
+
* @param model - The parent model to associate
|
|
130
|
+
* @returns The child model for chaining
|
|
131
|
+
*/
|
|
132
|
+
public associate(model: TRelated): Model {
|
|
133
|
+
const ownerKeyValue = model.getAttribute(this.ownerKey);
|
|
134
|
+
this.parent.setAttribute(this.foreignKey, ownerKeyValue);
|
|
135
|
+
return this.parent;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Dissociate the parent model from this child model.
|
|
140
|
+
*
|
|
141
|
+
* This sets the foreign key on the child model to null.
|
|
142
|
+
* Note: This does NOT save the child model - you must call save() yourself.
|
|
143
|
+
*
|
|
144
|
+
* Usage:
|
|
145
|
+
* ```typescript
|
|
146
|
+
* const profile = await Profile.find(1);
|
|
147
|
+
* profile.user().dissociate();
|
|
148
|
+
* await profile.save();
|
|
149
|
+
* ```
|
|
150
|
+
*
|
|
151
|
+
* @returns The child model for chaining
|
|
152
|
+
*/
|
|
153
|
+
public dissociate(): Model {
|
|
154
|
+
this.parent.setAttribute(this.foreignKey, null);
|
|
155
|
+
return this.parent;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Get the foreign key column name.
|
|
160
|
+
*
|
|
161
|
+
* @returns The foreign key column name
|
|
162
|
+
*/
|
|
163
|
+
public getForeignKeyName(): string {
|
|
164
|
+
return this.foreignKey;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Get the fully qualified foreign key name (table.column).
|
|
169
|
+
*
|
|
170
|
+
* @returns The qualified foreign key name
|
|
171
|
+
*/
|
|
172
|
+
public getQualifiedForeignKeyName(): string {
|
|
173
|
+
return this.qualifyColumn(this.parent.getTable(), this.foreignKey);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Get the owner key column name.
|
|
178
|
+
*
|
|
179
|
+
* @returns The owner key column name
|
|
180
|
+
*/
|
|
181
|
+
public getOwnerKeyName(): string {
|
|
182
|
+
return this.ownerKey;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* Get the value of the child's foreign key.
|
|
187
|
+
*
|
|
188
|
+
* @returns The foreign key value
|
|
189
|
+
*/
|
|
190
|
+
public getForeignKeyValue(): unknown {
|
|
191
|
+
return this.parent.getAttribute(this.foreignKey);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// ==========================================
|
|
195
|
+
// Eager Loading Methods
|
|
196
|
+
// ==========================================
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Add constraints for eager loading.
|
|
200
|
+
* Uses WHERE IN to fetch all parent models for multiple children at once.
|
|
201
|
+
*
|
|
202
|
+
* @param models - Array of child models to load parents for
|
|
203
|
+
*/
|
|
204
|
+
public addEagerConstraints(models: Model[]): void {
|
|
205
|
+
// Collect all foreign key values from child models
|
|
206
|
+
const keys: unknown[] = [];
|
|
207
|
+
for (const model of models) {
|
|
208
|
+
const key = model.getAttribute(this.foreignKey);
|
|
209
|
+
if (key !== undefined && key !== null) {
|
|
210
|
+
keys.push(key);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// Remove duplicates
|
|
215
|
+
const uniqueKeys = [...new Set(keys)];
|
|
216
|
+
|
|
217
|
+
// Add WHERE IN constraint on the owner key
|
|
218
|
+
if (uniqueKeys.length > 0) {
|
|
219
|
+
this.query.whereIn(this.ownerKey, uniqueKeys as (string | number | boolean)[]);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* Match the eagerly loaded results to their child models.
|
|
225
|
+
* For BelongsTo, each child gets at most one parent model.
|
|
226
|
+
*
|
|
227
|
+
* @param models - Array of child models
|
|
228
|
+
* @param results - Array of parent models that were loaded
|
|
229
|
+
* @param relation - The name of the relationship being matched
|
|
230
|
+
*/
|
|
231
|
+
public match(models: Model[], results: TRelated[], relation: string): void {
|
|
232
|
+
// Build a dictionary for O(1) lookup: ownerKey value -> parent model
|
|
233
|
+
const dictionary = new Map<unknown, TRelated>();
|
|
234
|
+
for (const result of results) {
|
|
235
|
+
const ownerKeyValue = result.getAttribute(this.ownerKey);
|
|
236
|
+
dictionary.set(ownerKeyValue, result);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
// Match each child model with its parent model
|
|
240
|
+
for (const model of models) {
|
|
241
|
+
const foreignKeyValue = model.getAttribute(this.foreignKey);
|
|
242
|
+
const match = dictionary.get(foreignKeyValue) || null;
|
|
243
|
+
model.setRelation(relation, match);
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
}
|