Skip to content

Commit c04a6c4

Browse files
chore: wip
1 parent 35a297b commit c04a6c4

File tree

2 files changed

+209
-67
lines changed

2 files changed

+209
-67
lines changed

storage/framework/core/orm/src/utils.ts

Lines changed: 196 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,18 @@
11
import type {
22
Attributes,
33
AttributesElements,
4+
BaseBelongsToMany,
5+
BaseHasOneThrough,
46
BelongsToMany,
57
FieldArrayElement,
8+
HasOne,
69
HasOneThrough,
710
Model,
811
ModelElement,
12+
ModelNames,
913
Relation,
1014
RelationConfig,
15+
Relations,
1116
TableNames,
1217
} from '@stacksjs/types'
1318
import { generator, parser, traverse } from '@stacksjs/build'
@@ -62,76 +67,209 @@ export function hasRelations(obj: any, key: string): boolean {
6267
}
6368

6469
export async function getRelations(model: Model, modelName: string): Promise<RelationConfig[]> {
65-
const relationsArray = ['hasOne', 'belongsTo', 'hasMany', 'belongsToMany', 'hasOneThrough']
6670
const relationships = []
6771

68-
for (const relation of relationsArray) {
69-
if (hasRelations(model, relation)) {
70-
for (const relationInstance of model[relation as keyof Model] as Relation[]) {
71-
let relationModel = relationInstance.model
72-
let modelRelation: Model
73-
let modelPath: string
74-
if (isString(relationInstance)) {
75-
relationModel = relationInstance
76-
}
72+
if (model.hasOne) {
73+
for (const relationInstance of model.hasOne) {
74+
relationships.push(await processHasOneAndMany(relationInstance, model, modelName, 'hasOne'))
75+
}
76+
}
7777

78-
const modelRelationPath = path.userModelsPath(`${relationModel}.ts`)
79-
const userModelPath = path.userModelsPath(`${modelName}.ts`)
80-
const coreModelPath = path.storagePath(`framework/defaults/models/${modelName}.ts`)
81-
const coreModelRelationPath = path.storagePath(`framework/defaults/models/${relationModel}.ts`)
82-
83-
if (fs.existsSync(modelRelationPath))
84-
modelRelation = (await import(modelRelationPath)).default as Model
85-
else
86-
modelRelation = (await import(coreModelRelationPath)).default as Model
87-
88-
if (fs.existsSync(userModelPath))
89-
modelPath = userModelPath
90-
else
91-
modelPath = coreModelPath
92-
93-
const modelRelationTable = getTableName(modelRelation, modelRelationPath)
94-
const table = getTableName(model, modelPath)
95-
const modelRelationName = snakeCase(getModelName(modelRelation, modelRelationPath))
96-
const formattedModelName = snakeCase(modelName)
97-
98-
const throughInstance = relationInstance as HasOneThrough
99-
const belongsToManyInstance = relationInstance as BelongsToMany
100-
101-
const relationshipData: RelationConfig = {
102-
relationship: relation,
103-
model: relationModel,
104-
table: modelRelationTable as TableNames,
105-
relationTable: table as TableNames,
106-
foreignKey: relationInstance.foreignKey || `${formattedModelName}_id`,
107-
modelKey: `${modelRelationName}_id`,
108-
relationName: relationInstance.relationName || '',
109-
relationModel: modelName,
110-
throughModel: throughInstance.through || '',
111-
throughForeignKey: throughInstance.throughForeignKey || '',
112-
pivotForeign: relationInstance.foreignKey || `${formattedModelName}_id`,
113-
pivotKey: `${modelRelationName}_id`,
114-
pivotTable:
115-
belongsToManyInstance?.pivotTable
116-
|| getPivotTableName(plural(formattedModelName), plural(modelRelation.table || '')),
117-
}
78+
if (model.hasMany) {
79+
for (const relationInstance of model.hasMany) {
80+
relationships.push(await processHasOneAndMany(relationInstance, model, modelName, 'hasMany'))
81+
}
82+
}
11883

119-
if (['belongsToMany', 'belongsTo'].includes(relation))
120-
relationshipData.foreignKey = ''
84+
if (model.belongsTo) {
85+
for (const relationInstance of model.belongsTo) {
86+
relationships.push(await processHasOneAndMany(relationInstance, model, modelName, 'belongsTo'))
87+
}
88+
}
12189

122-
if (['belongsToMany'].includes(relation)) {
123-
relationshipData.pivotForeign = relationInstance.foreignKey || `${formattedModelName}_id`
124-
relationshipData.pivotKey = `${modelRelationName}_id`
125-
}
90+
if (model.hasOneThrough) {
91+
for (const relationInstance of model.hasOneThrough) {
92+
relationships.push(await processHasThrough(relationInstance, model, modelName, 'hasOneThrough'))
93+
}
94+
}
12695

127-
relationships.push(relationshipData)
128-
}
96+
if (model.belongsToMany) {
97+
for (const relationInstance of model.belongsToMany) {
98+
relationships.push(await processBelongsToMany(relationInstance, model, modelName, 'belongsToMany'))
12999
}
130100
}
131101

132102
return relationships
133103
}
134104

105+
async function processHasThrough(relationInstance: ModelNames | BaseHasOneThrough<ModelNames>, model: Model, modelName: string, relation: string) {
106+
let relationModel = ''
107+
let modelRelation: Model
108+
let modelPath: string
109+
let relationName = ''
110+
let throughModel = ''
111+
let throughForeignKey = ''
112+
113+
if (isString(relationInstance)) {
114+
relationModel = relationInstance
115+
}
116+
else {
117+
relationModel = relationInstance.model
118+
relationName = relationInstance.relationName || ''
119+
throughModel = relationInstance.through
120+
throughForeignKey = relationInstance.throughForeignKey || ''
121+
}
122+
123+
const modelRelationPath = path.userModelsPath(`${relationModel}.ts`)
124+
const userModelPath = path.userModelsPath(`${modelName}.ts`)
125+
const coreModelPath = path.storagePath(`framework/defaults/models/${modelName}.ts`)
126+
const coreModelRelationPath = path.storagePath(`framework/defaults/models/${relationModel}.ts`)
127+
128+
if (fs.existsSync(modelRelationPath))
129+
modelRelation = (await import(modelRelationPath)).default as Model
130+
else
131+
modelRelation = (await import(coreModelRelationPath)).default as Model
132+
133+
if (fs.existsSync(userModelPath))
134+
modelPath = userModelPath
135+
else
136+
modelPath = coreModelPath
137+
138+
const modelRelationTable = getTableName(modelRelation, modelRelationPath)
139+
const table = getTableName(model, modelPath)
140+
const modelRelationName = snakeCase(getModelName(modelRelation, modelRelationPath))
141+
const formattedModelName = snakeCase(modelName)
142+
143+
const relationshipData: RelationConfig = {
144+
relationship: relation,
145+
model: relationModel,
146+
table: modelRelationTable as TableNames,
147+
relationTable: table as TableNames,
148+
foreignKey: `${formattedModelName}_id`,
149+
modelKey: `${modelRelationName}_id`,
150+
relationName,
151+
relationModel: modelName,
152+
throughModel,
153+
throughForeignKey,
154+
pivotForeign: `${formattedModelName}_id`,
155+
pivotKey: `${modelRelationName}_id`,
156+
pivotTable: table as TableNames,
157+
}
158+
159+
return relationshipData
160+
}
161+
162+
async function processBelongsToMany(relationInstance: ModelNames | BaseBelongsToMany<ModelNames>, model: Model, modelName: string, relation: string) {
163+
let relationModel = ''
164+
let modelRelation: Model
165+
let modelPath: string
166+
let pivotTable = ''
167+
let pivotForeign = ''
168+
169+
const modelRelationPath = path.userModelsPath(`${relationModel}.ts`)
170+
const userModelPath = path.userModelsPath(`${modelName}.ts`)
171+
const coreModelPath = path.storagePath(`framework/defaults/models/${modelName}.ts`)
172+
const coreModelRelationPath = path.storagePath(`framework/defaults/models/${relationModel}.ts`)
173+
174+
if (fs.existsSync(modelRelationPath))
175+
modelRelation = (await import(modelRelationPath)).default as Model
176+
else
177+
modelRelation = (await import(coreModelRelationPath)).default as Model
178+
179+
if (fs.existsSync(userModelPath))
180+
modelPath = userModelPath
181+
else
182+
modelPath = coreModelPath
183+
184+
const modelRelationTable = getTableName(modelRelation, modelRelationPath)
185+
const table = getTableName(model, modelPath)
186+
const modelRelationName = snakeCase(getModelName(modelRelation, modelRelationPath))
187+
const formattedModelName = snakeCase(modelName)
188+
189+
if (isString(relationInstance)) {
190+
relationModel = relationInstance
191+
}
192+
else {
193+
relationModel = relationInstance.model
194+
pivotTable = relationInstance.pivotTable || ''
195+
pivotForeign = relationInstance.firstForeignKey || `${formattedModelName}_id`
196+
}
197+
198+
const relationshipData: RelationConfig = {
199+
relationship: relation,
200+
model: relationModel,
201+
table: modelRelationTable as TableNames,
202+
relationTable: table as TableNames,
203+
foreignKey: '',
204+
modelKey: `${modelRelationName}_id`,
205+
relationName: '',
206+
relationModel: modelName,
207+
throughModel: '',
208+
throughForeignKey: '',
209+
pivotForeign,
210+
pivotKey: `${modelRelationName}_id`,
211+
pivotTable: pivotTable as TableNames,
212+
}
213+
214+
return relationshipData
215+
}
216+
217+
async function processHasOneAndMany(relationInstance: ModelNames | Relation<ModelNames>, model: Model, modelName: string, relation: string) {
218+
let relationModel = ''
219+
let modelRelation: Model
220+
let modelPath: string
221+
let relationName = ''
222+
223+
if (isString(relationInstance)) {
224+
relationModel = relationInstance
225+
}
226+
else {
227+
relationModel = relationInstance.model
228+
relationName = relationInstance.relationName || ''
229+
}
230+
231+
const modelRelationPath = path.userModelsPath(`${relationModel}.ts`)
232+
const userModelPath = path.userModelsPath(`${modelName}.ts`)
233+
const coreModelPath = path.storagePath(`framework/defaults/models/${modelName}.ts`)
234+
const coreModelRelationPath = path.storagePath(`framework/defaults/models/${relationModel}.ts`)
235+
236+
if (fs.existsSync(modelRelationPath))
237+
modelRelation = (await import(modelRelationPath)).default as Model
238+
else
239+
modelRelation = (await import(coreModelRelationPath)).default as Model
240+
241+
if (fs.existsSync(userModelPath))
242+
modelPath = userModelPath
243+
else
244+
modelPath = coreModelPath
245+
246+
const modelRelationTable = getTableName(modelRelation, modelRelationPath)
247+
const table = getTableName(model, modelPath)
248+
const modelRelationName = snakeCase(getModelName(modelRelation, modelRelationPath))
249+
const formattedModelName = snakeCase(modelName)
250+
251+
const relationshipData: RelationConfig = {
252+
relationship: relation,
253+
model: relationModel,
254+
table: modelRelationTable as TableNames,
255+
relationTable: table as TableNames,
256+
foreignKey: `${formattedModelName}_id`,
257+
modelKey: `${modelRelationName}_id`,
258+
relationName,
259+
relationModel: modelName,
260+
throughModel: '',
261+
throughForeignKey: '',
262+
pivotForeign: `${formattedModelName}_id`,
263+
pivotKey: `${modelRelationName}_id`,
264+
pivotTable: table as TableNames,
265+
}
266+
267+
if (relation === 'belongsTo')
268+
relationshipData.foreignKey = ''
269+
270+
return relationshipData
271+
}
272+
135273
export function getRelationType(relation: string): string {
136274
const belongToType = /belongs/
137275
const hasType = /has/

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

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -17,20 +17,24 @@ export interface Relation<T = string> extends BaseRelation {
1717
export interface HasOne<T = string> extends Array<Relation<T>> {}
1818
export interface HasMany<T = string> extends Array<Relation<T>> {}
1919
export interface BelongsTo<T = string> extends Array<Relation<T>> {}
20-
export interface BelongsToMany<T = string> extends Array<{
20+
21+
export interface BelongsToMany<T = string> extends Array<BaseBelongsToMany<T> | T> {}
22+
export interface HasOneThrough<T = string> extends Array<BaseHasOneThrough<T> | T> {}
23+
24+
export interface BaseBelongsToMany<T = string> {
2125
model: T
2226
firstForeignKey?: string
2327
secondForeignKey?: string
2428
pivotTable?: string
25-
} | T> {}
29+
}
2630

27-
export interface HasOneThrough<T = string> extends Array<{
31+
export interface BaseHasOneThrough<T = string> {
2832
model: T
2933
through: T
3034
foreignKey?: string
3135
throughForeignKey?: string
3236
relationName?: string
33-
}> {}
37+
}
3438

3539
export interface FieldArrayElement {
3640
entity: string
@@ -74,10 +78,10 @@ interface ActivityLogOption {
7478
}
7579

7680
export interface Relations {
77-
hasOne?: HasOne<ModelNames> | ModelNames[]
78-
hasMany?: HasMany<ModelNames> | ModelNames[]
79-
belongsTo?: BelongsTo<ModelNames> | ModelNames[]
80-
belongsToMany?: BelongsToMany<ModelNames> | ModelNames[]
81+
hasOne: HasOne<ModelNames> | ModelNames[]
82+
hasMany: HasMany<ModelNames> | ModelNames[]
83+
belongsTo: BelongsTo<ModelNames> | ModelNames[]
84+
belongsToMany: BelongsToMany<ModelNames> | ModelNames[]
8185
}
8286

8387
export interface ApiSettings {
@@ -158,7 +162,7 @@ export interface ModelOptions extends Base {
158162

159163
belongsToMany?: BelongsToMany<ModelNames> | ModelNames[]
160164

161-
hasOneThrough?: HasOneThrough<ModelNames>
165+
hasOneThrough?: HasOneThrough<ModelNames> | ModelNames[]
162166

163167
scopes?: {
164168
[key: string]: (value: any) => any

0 commit comments

Comments
 (0)