Skip to content

Commit 4360d93

Browse files
chore: wip
1 parent 1171920 commit 4360d93

File tree

2 files changed

+239
-16
lines changed

2 files changed

+239
-16
lines changed

storage/framework/core/db/src/orm/Models/User.ts

Lines changed: 95 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,16 @@
1-
import type { SelectQueryBuilder } from 'bun-query-builder'
2-
import { db2, schema } from '../../db'
1+
import { db2 } from '../../db'
32

43
class UserModel {
5-
private query: SelectQueryBuilder<typeof schema, 'users', Record<string, unknown>, 'users'>
4+
private readonly hidden: string[] = ['password']
5+
private readonly fillable: string[] = ['name', 'email', 'password', 'uuid', 'two_factor_secret', 'public_key']
6+
private readonly guarded: string[] = []
7+
protected attributes: Record<string, any> = {}
8+
private query: any
69

7-
constructor() {
10+
constructor(data?: Record<string, any>) {
11+
if (data) {
12+
this.attributes = { ...data }
13+
}
814
this.query = db2.selectFrom('users')
915
}
1016

@@ -15,22 +21,26 @@ class UserModel {
1521

1622
// Find by ID
1723
static async find(id: number) {
18-
return await db2.selectFrom('users').where('id', '=', id).executeTakeFirst()
24+
const result = await db2.selectFrom('users').where('id', '=', id).executeTakeFirst()
25+
return result ? new UserModel(result) : undefined
1926
}
2027

2128
// Get all records
2229
static async all() {
23-
return await db2.selectFrom('users').execute()
30+
const results = await db2.selectFrom('users').execute()
31+
return results.map((result: any) => new UserModel(result))
2432
}
2533

2634
// Get the first record
2735
async first() {
28-
return await this.query.executeTakeFirst()
36+
const result = await this.query.executeTakeFirst()
37+
return result ? new UserModel(result) : undefined
2938
}
3039

3140
// Get all records from the query
3241
async get() {
33-
return await this.query.execute()
42+
const results = await this.query.execute()
43+
return results.map((result: any) => new UserModel(result))
3444
}
3545

3646
// Chainable where clause
@@ -60,6 +70,83 @@ class UserModel {
6070
this.query = this.query.limit(count)
6171
return this
6272
}
73+
74+
// Create a new record
75+
static async create(data: Record<string, any>) {
76+
const instance = new UserModel()
77+
78+
// Filter based on fillable and guarded
79+
const filteredData = Object.fromEntries(
80+
Object.entries(data).filter(([key]) =>
81+
!instance.guarded.includes(key) && instance.fillable.includes(key)
82+
)
83+
)
84+
85+
const result = await db2.insertInto('users')
86+
.values(filteredData)
87+
.execute()
88+
89+
// Fetch the created record
90+
const created = await db2.selectFrom('users')
91+
.where('id', '=', Number((result as any).insertId))
92+
.executeTakeFirst()
93+
94+
return created ? new UserModel(created) : undefined
95+
}
96+
97+
// Update the current record
98+
async update(data: Record<string, any>) {
99+
if (!this.attributes.id) {
100+
throw new Error('Cannot update a model without an ID')
101+
}
102+
103+
// Filter based on fillable and guarded
104+
const filteredData = Object.fromEntries(
105+
Object.entries(data).filter(([key]) =>
106+
!this.guarded.includes(key) && this.fillable.includes(key)
107+
)
108+
)
109+
110+
await (db2 as any).updateTable('users')
111+
.set(filteredData)
112+
.where('id', '=', this.attributes.id)
113+
.execute()
114+
115+
// Fetch the updated record
116+
const updated = await db2.selectFrom('users')
117+
.where('id', '=', this.attributes.id)
118+
.executeTakeFirst()
119+
120+
if (updated) {
121+
this.attributes = { ...updated }
122+
}
123+
124+
return this
125+
}
126+
127+
// Delete the current record
128+
async delete() {
129+
if (!this.attributes.id) {
130+
throw new Error('Cannot delete a model without an ID')
131+
}
132+
133+
await (db2 as any).deleteFrom('users')
134+
.where('id', '=', this.attributes.id)
135+
.execute()
136+
137+
return true
138+
}
139+
140+
// Convert to JSON (excluding hidden fields)
141+
toJSON() {
142+
const json = { ...this.attributes }
143+
144+
for (const field of this.hidden) {
145+
delete json[field]
146+
}
147+
148+
return json
149+
}
63150
}
64151

65152
export default UserModel

storage/framework/core/db/src/orm/generate.ts

Lines changed: 144 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,68 @@ export function getTableName(model: Model, modelPath: string): string {
2020
return snakeCase(plural(getModelName(model, modelPath)))
2121
}
2222

23-
export function generateOrmModelString(modelName: string, tableName: string): string {
23+
function extractHiddenFields(model: Model): string[] {
24+
if (!model.attributes) return []
25+
26+
return Object.keys(model.attributes).filter(key =>
27+
model.attributes?.[key]?.hidden === true
28+
).map(field => snakeCase(field))
29+
}
30+
31+
function extractFillableFields(model: Model): string[] {
32+
if (!model.attributes) return []
33+
34+
const fillable = Object.keys(model.attributes).filter(key =>
35+
model.attributes?.[key]?.fillable === true
36+
).map(field => snakeCase(field))
37+
38+
// Add trait-specific fillable fields
39+
const additionalFields: string[] = []
40+
41+
if (model.traits?.useUuid) {
42+
additionalFields.push('uuid')
43+
}
44+
45+
if (model.traits?.useAuth) {
46+
const useAuth = model.traits.useAuth
47+
if (typeof useAuth === 'object' && useAuth.usePasskey) {
48+
additionalFields.push('two_factor_secret', 'public_key')
49+
}
50+
}
51+
52+
return [...fillable, ...additionalFields]
53+
}
54+
55+
function extractGuardedFields(model: Model): string[] {
56+
if (!model.attributes) return []
57+
58+
return Object.keys(model.attributes).filter(key =>
59+
model.attributes?.[key]?.guarded === true
60+
).map(field => snakeCase(field))
61+
}
62+
63+
export function generateOrmModelString(modelName: string, tableName: string, model: Model): string {
64+
const hidden = extractHiddenFields(model)
65+
const fillable = extractFillableFields(model)
66+
const guarded = extractGuardedFields(model)
67+
68+
const hiddenArray = hidden.length > 0 ? `['${hidden.join("', '")}']` : '[]'
69+
const fillableArray = fillable.length > 0 ? `['${fillable.join("', '")}']` : '[]'
70+
const guardedArray = guarded.length > 0 ? `['${guarded.join("', '")}']` : '[]'
71+
2472
return `import { db2 } from '../../db'
2573
2674
class ${modelName}Model {
75+
private readonly hidden: string[] = ${hiddenArray}
76+
private readonly fillable: string[] = ${fillableArray}
77+
private readonly guarded: string[] = ${guardedArray}
78+
protected attributes: Record<string, any> = {}
2779
private query: any
2880
29-
constructor() {
81+
constructor(data?: Record<string, any>) {
82+
if (data) {
83+
this.attributes = { ...data }
84+
}
3085
this.query = db2.selectFrom('${tableName}')
3186
}
3287
@@ -37,22 +92,26 @@ class ${modelName}Model {
3792
3893
// Find by ID
3994
static async find(id: number) {
40-
return await db2.selectFrom('${tableName}').where('id', '=', id).executeTakeFirst()
95+
const result = await db2.selectFrom('${tableName}').where('id', '=', id).executeTakeFirst()
96+
return result ? new ${modelName}Model(result) : undefined
4197
}
4298
4399
// Get all records
44100
static async all() {
45-
return await db2.selectFrom('${tableName}').execute()
101+
const results = await db2.selectFrom('${tableName}').execute()
102+
return results.map((result: any) => new ${modelName}Model(result))
46103
}
47104
48105
// Get the first record
49106
async first() {
50-
return await this.query.executeTakeFirst()
107+
const result = await this.query.executeTakeFirst()
108+
return result ? new ${modelName}Model(result) : undefined
51109
}
52110
53111
// Get all records from the query
54112
async get() {
55-
return await this.query.execute()
113+
const results = await this.query.execute()
114+
return results.map((result: any) => new ${modelName}Model(result))
56115
}
57116
58117
// Chainable where clause
@@ -82,6 +141,83 @@ class ${modelName}Model {
82141
this.query = this.query.limit(count)
83142
return this
84143
}
144+
145+
// Create a new record
146+
static async create(data: Record<string, any>) {
147+
const instance = new ${modelName}Model()
148+
149+
// Filter based on fillable and guarded
150+
const filteredData = Object.fromEntries(
151+
Object.entries(data).filter(([key]) =>
152+
!instance.guarded.includes(key) && instance.fillable.includes(key)
153+
)
154+
)
155+
156+
const result = await db2.insertInto('${tableName}')
157+
.values(filteredData)
158+
.execute()
159+
160+
// Fetch the created record
161+
const created = await db2.selectFrom('${tableName}')
162+
.where('id', '=', Number((result as any).insertId))
163+
.executeTakeFirst()
164+
165+
return created ? new ${modelName}Model(created) : undefined
166+
}
167+
168+
// Update the current record
169+
async update(data: Record<string, any>) {
170+
if (!this.attributes.id) {
171+
throw new Error('Cannot update a model without an ID')
172+
}
173+
174+
// Filter based on fillable and guarded
175+
const filteredData = Object.fromEntries(
176+
Object.entries(data).filter(([key]) =>
177+
!this.guarded.includes(key) && this.fillable.includes(key)
178+
)
179+
)
180+
181+
await (db2 as any).updateTable('${tableName}')
182+
.set(filteredData)
183+
.where('id', '=', this.attributes.id)
184+
.execute()
185+
186+
// Fetch the updated record
187+
const updated = await db2.selectFrom('${tableName}')
188+
.where('id', '=', this.attributes.id)
189+
.executeTakeFirst()
190+
191+
if (updated) {
192+
this.attributes = { ...updated }
193+
}
194+
195+
return this
196+
}
197+
198+
// Delete the current record
199+
async delete() {
200+
if (!this.attributes.id) {
201+
throw new Error('Cannot delete a model without an ID')
202+
}
203+
204+
await (db2 as any).deleteFrom('${tableName}')
205+
.where('id', '=', this.attributes.id)
206+
.execute()
207+
208+
return true
209+
}
210+
211+
// Convert to JSON (excluding hidden fields)
212+
toJSON() {
213+
const json = { ...this.attributes }
214+
215+
for (const field of this.hidden) {
216+
delete json[field]
217+
}
218+
219+
return json
220+
}
85221
}
86222
87223
export default ${modelName}Model
@@ -99,7 +235,7 @@ export async function generateOrmModels(): Promise<void> {
99235
const modelName = getModelName(model, userModelFile)
100236
const tableName = getTableName(model, userModelFile)
101237

102-
const modelString = generateOrmModelString(modelName, tableName)
238+
const modelString = generateOrmModelString(modelName, tableName, model)
103239

104240
// Ensure the directory exists
105241
const ormModelsDir = path.storagePath('framework/core/db/src/orm/Models')
@@ -118,7 +254,7 @@ export async function generateOrmModel(modelPath: string): Promise<void> {
118254
const modelName = getModelName(model, modelPath)
119255
const tableName = getTableName(model, modelPath)
120256

121-
const modelString = generateOrmModelString(modelName, tableName)
257+
const modelString = generateOrmModelString(modelName, tableName, model)
122258

123259
// Ensure the directory exists
124260
const ormModelsDir = path.storagePath('framework/core/db/src/orm/Models')

0 commit comments

Comments
 (0)