|
1 | 1 | import type {
|
2 | 2 | Attributes,
|
3 | 3 | AttributesElements,
|
| 4 | + BaseBelongsToMany, |
| 5 | + BaseHasOneThrough, |
4 | 6 | BelongsToMany,
|
5 | 7 | FieldArrayElement,
|
| 8 | + HasOne, |
6 | 9 | HasOneThrough,
|
7 | 10 | Model,
|
8 | 11 | ModelElement,
|
| 12 | + ModelNames, |
9 | 13 | Relation,
|
10 | 14 | RelationConfig,
|
| 15 | + Relations, |
11 | 16 | TableNames,
|
12 | 17 | } from '@stacksjs/types'
|
13 | 18 | import { generator, parser, traverse } from '@stacksjs/build'
|
@@ -62,76 +67,209 @@ export function hasRelations(obj: any, key: string): boolean {
|
62 | 67 | }
|
63 | 68 |
|
64 | 69 | export async function getRelations(model: Model, modelName: string): Promise<RelationConfig[]> {
|
65 |
| - const relationsArray = ['hasOne', 'belongsTo', 'hasMany', 'belongsToMany', 'hasOneThrough'] |
66 | 70 | const relationships = []
|
67 | 71 |
|
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 | + } |
77 | 77 |
|
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 | + } |
118 | 83 |
|
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 | + } |
121 | 89 |
|
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 | + } |
126 | 95 |
|
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')) |
129 | 99 | }
|
130 | 100 | }
|
131 | 101 |
|
132 | 102 | return relationships
|
133 | 103 | }
|
134 | 104 |
|
| 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 | + |
135 | 273 | export function getRelationType(relation: string): string {
|
136 | 274 | const belongToType = /belongs/
|
137 | 275 | const hasType = /has/
|
|
0 commit comments