Skip to content

Commit 88f97cf

Browse files
chore: wip
1 parent bedd768 commit 88f97cf

File tree

9 files changed

+115
-45
lines changed

9 files changed

+115
-45
lines changed

app/Models/Subscriber.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,10 @@ export default {
1919
belongsTo: {
2020
model: 'User',
2121
},
22+
belongsToMany: {
23+
model: 'User',
24+
pivotTable: 'user_subscribers',
25+
},
2226

2327
attributes: {
2428
subscribed: {

app/Models/User.ts

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -42,11 +42,9 @@ export default {
4242

4343
// useUuid: true, // defaults to false
4444
},
45-
hasOne: {
46-
model: 'Subscriber',
47-
},
48-
hasMany: {
45+
belongsToMany: {
4946
model: 'Subscriber',
47+
pivotTable: 'user_subscribers',
5048
},
5149

5250
attributes: {

database/migrations/1712833548699-create-subscribers-table.ts

Lines changed: 0 additions & 13 deletions
This file was deleted.

database/migrations/1712833548712-create-users-table.ts

Lines changed: 0 additions & 16 deletions
This file was deleted.

storage/framework/core/actions/src/database/mysql.ts

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,17 @@ export async function resetMysqlDatabase() {
1818
const files = await fs.readdir(path.userMigrationsPath())
1919
const modelFiles = await fs.readdir(path.frameworkPath('database/models'))
2020

21+
const userModelFiles = glob.sync(path.userModelsPath('*.ts'))
22+
23+
for (const userModel of userModelFiles) {
24+
const userModelPath = await import(userModel)
25+
26+
const pivotTable = await getPivotTable(userModelPath)
27+
28+
if (pivotTable)
29+
await db.schema.dropTable(pivotTable).ifExists().execute()
30+
}
31+
2132
if (modelFiles.length) {
2233
for (const modelFile of modelFiles) {
2334
if (modelFile.endsWith('.ts')) {
@@ -108,12 +119,30 @@ export async function generateMysqlMigration(modelPath: string) {
108119
await createTableMigration(modelPath)
109120
}
110121

122+
async function getPivotTable(model: any): Promise<string> {
123+
if ('belongsToMany' in model.default) {
124+
const modelRelationPath = path.userModelsPath(`${model.default.belongsToMany.model}.ts`)
125+
126+
const modelRelation = await import(modelRelationPath)
127+
128+
const formattedModelName = model.default.name.toLowerCase()
129+
130+
const pivotTable = model.default.belongsToMany?.pivotTable || `${formattedModelName}_${modelRelation.default.table}`
131+
132+
return pivotTable
133+
}
134+
135+
return ''
136+
}
137+
111138
async function createTableMigration(modelPath: string) {
112139
log.debug('createTableMigration modelPath:', modelPath)
113140

114141
const model = await import(modelPath)
115142
const tableName = model.default.table
116143

144+
await createPivotTableMigration(model)
145+
117146
const fields = model.default.attributes
118147
const useTimestamps = model.default?.traits?.useTimestamps ?? model.default?.traits?.timestampable
119148
const useSoftDeletes = model.default?.traits?.useSoftDeletes ?? model.default?.traits?.softDeletable
@@ -166,6 +195,33 @@ async function createTableMigration(modelPath: string) {
166195
log.success(`Created migration: ${migrationFileName}`)
167196
}
168197

198+
async function createPivotTableMigration(model: any) {
199+
const pivotTable = await getPivotTable(model)
200+
201+
if (!pivotTable)
202+
return
203+
204+
let migrationContent = `import type { Database } from '@stacksjs/database'\n`
205+
migrationContent += `import { sql } from '@stacksjs/database'\n\n`
206+
migrationContent += `export async function up(db: Database<any>) {\n`
207+
migrationContent += ` await db.schema\n`
208+
migrationContent += ` .createTable('${pivotTable}')\n`
209+
migrationContent += ` .addColumn('id', 'integer', col => col.primaryKey().autoIncrement())\n`
210+
migrationContent += ` .addColumn('user_id', 'integer')\n`
211+
migrationContent += ` .addColumn('subscriber_id', 'integer')\n`
212+
migrationContent += ` .execute()\n`
213+
migrationContent += ` }\n`
214+
215+
const timestamp = new Date().getTime().toString()
216+
const migrationFileName = `${timestamp}-create-${pivotTable}-table.ts`
217+
const migrationFilePath = path.userMigrationsPath(migrationFileName)
218+
219+
// Assuming fs.writeFileSync is available or use an equivalent method
220+
Bun.write(migrationFilePath, migrationContent)
221+
222+
log.success(`Created migration: ${migrationFileName}`)
223+
}
224+
169225
export async function createAlterTableMigration(modelPath: string) {
170226
// eslint-disable-next-line no-console
171227
console.log('createAlterTableMigration')

storage/framework/core/actions/src/orm/generate-model.ts

Lines changed: 35 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ async function initiateModelGeneration(): Promise<void> {
4343
}
4444

4545
async function getRelations(model: any) {
46-
const relationsArray = ['hasOne', 'belongsTo', 'hasMany']
46+
const relationsArray = ['hasOne', 'belongsTo', 'hasMany', 'belongsToMany']
4747

4848
const relationships = []
4949

@@ -53,8 +53,15 @@ async function getRelations(model: any) {
5353

5454
const modelRelation = await import(modelRelationPath)
5555

56-
// const formattedRelations = model.default[relation].toLowerCase()
57-
relationships.push({ relationship: relation, model: model.default[relation].model, table: modelRelation.default.table })
56+
const formattedModelName = model.default.name.toLowerCase()
57+
58+
relationships.push({
59+
relationship: relation,
60+
model: model.default[relation].model,
61+
table: modelRelation.default.table,
62+
foreignKey: model.default[relation].foreignKey || `${formattedModelName}_id`,
63+
pivotTable: model.default[relation]?.pivotTable || `${formattedModelName}_${modelRelation.default.table}`,
64+
})
5865
}
5966
}
6067

@@ -229,8 +236,11 @@ async function generateModelString(tableName: string, model: any, attributes: Mo
229236

230237
for (const relation of relations) {
231238
const modelRelation = relation.model
239+
const foreignKeyRelation = relation.foreignKey
232240
const tableRelation = relation.table
241+
const pivotTableRelation = relation.pivotTable
233242
const formattedModelRelation = modelRelation.toLowerCase()
243+
const capitalizeTableRelation = tableRelation.charAt(0).toUpperCase() + tableRelation.slice(1)
234244

235245
const relationType = getRelationType(relation.relationship)
236246
const relationCount = getRelationCount(relation.relationship)
@@ -242,7 +252,7 @@ async function generateModelString(tableName: string, model: any, attributes: Mo
242252
throw new Error('Relation Error!')
243253
244254
const results = await db.selectFrom('${tableRelation}')
245-
.where('${formattedModelName}_id', '=', this.${formattedModelName}.id)
255+
.where('${foreignKeyRelation}', '=', this.${formattedModelName}.id)
246256
.selectAll()
247257
.execute()
248258
@@ -257,7 +267,7 @@ async function generateModelString(tableName: string, model: any, attributes: Mo
257267
throw new Error('Relation Error!')
258268
259269
const model = await db.selectFrom('${tableRelation}')
260-
.where('${formattedModelName}_id', '=', this.${formattedModelName}.id)
270+
.where('${foreignKeyRelation}', '=', this.${formattedModelName}.id)
261271
.selectAll()
262272
.executeTakeFirst()
263273
@@ -268,14 +278,14 @@ async function generateModelString(tableName: string, model: any, attributes: Mo
268278
}\n\n`
269279
}
270280

271-
if (relationType === 'belongsType') {
281+
if (relationType === 'belongsType' && !relationCount) {
272282
relationMethods += `
273283
async ${formattedModelRelation}() {
274-
if (this.${formattedModelName}.${formattedModelRelation}_id === undefined)
284+
if (this.${foreignKeyRelation} === undefined)
275285
throw new Error('Relation Error!')
276286
277287
const model = await db.selectFrom('${tableRelation}')
278-
.where('id', '=', this.${formattedModelName}.${formattedModelRelation}_id)
288+
.where('id', '=', ${foreignKeyRelation})
279289
.selectAll()
280290
.executeTakeFirst()
281291
@@ -285,6 +295,23 @@ async function generateModelString(tableName: string, model: any, attributes: Mo
285295
return new ${modelRelation}.modelInstance(model)
286296
}\n\n`
287297
}
298+
299+
if (relationType === 'belongsType' && relationCount === 'many') {
300+
const pivotTable = pivotTableRelation || tableRelation
301+
302+
relationMethods += `
303+
async ${formattedModelName}${capitalizeTableRelation}() {
304+
if (this.${formattedModelName}.id === undefined)
305+
throw new Error('Relation Error!')
306+
307+
const results = await db.selectFrom('${pivotTable}')
308+
.where('${foreignKeyRelation}', '=', this.${formattedModelName}.id)
309+
.selectAll()
310+
.execute()
311+
312+
return results
313+
}\n\n`
314+
}
288315
}
289316

290317
for (const attribute of attributes)

storage/framework/core/types/src/model.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,12 @@ export interface ModelOptions extends Base {
7373
model: string // should be typed as ModelName
7474
foreignKey?: string
7575
} // belongsTo: 'User'
76-
belongsToMany: object
76+
belongsToMany: {
77+
model: string
78+
firstForeignKey?: string
79+
secondForeignKey?: string
80+
pivotTable?: string
81+
}
7782
hasThrough: {
7883
model: string // should be typed as ModelName
7984
through: string

storage/framework/database/models/Subscriber.ts

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,13 @@ export default {
1616
},
1717

1818
// relationships
19-
belongsTo: 'User',
19+
belongsTo: {
20+
model: 'User',
21+
},
22+
belongsToMany: {
23+
model: 'User',
24+
pivotTable: 'user_subscribers',
25+
},
2026

2127
attributes: {
2228
subscribed: {
@@ -27,13 +33,12 @@ export default {
2733

2834
factory: () => faker.datatype.boolean(),
2935
},
36+
3037
user_id: {
3138
validator: {
3239
rule: schema.number(),
3340
message: '',
3441
},
35-
36-
factory: () => faker.datatype.boolean(),
3742
},
3843
},
3944
} satisfies Model

storage/framework/database/models/User.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,10 @@ export default {
4242

4343
// useUuid: true, // defaults to false
4444
},
45+
belongsToMany: {
46+
model: 'Subscriber',
47+
pivotTable: 'user_subscribers',
48+
},
4549

4650
attributes: {
4751
name: {

0 commit comments

Comments
 (0)