github.com/Ali-iotechsys/sqlboiler/v4@v4.0.0-20221208124957-6aec9a5f1f71/boilingcore/aliases.go (about) 1 package boilingcore 2 3 import ( 4 "fmt" 5 "unicode" 6 "unicode/utf8" 7 8 "github.com/volatiletech/sqlboiler/v4/drivers" 9 "github.com/volatiletech/strmangle" 10 ) 11 12 // Aliases defines aliases for the generation run 13 type Aliases struct { 14 Tables map[string]TableAlias `toml:"tables,omitempty" json:"tables,omitempty"` 15 } 16 17 // TableAlias defines the spellings for a table name in Go 18 type TableAlias struct { 19 UpPlural string `toml:"up_plural,omitempty" json:"up_plural,omitempty"` 20 UpSingular string `toml:"up_singular,omitempty" json:"up_singular,omitempty"` 21 DownPlural string `toml:"down_plural,omitempty" json:"down_plural,omitempty"` 22 DownSingular string `toml:"down_singular,omitempty" json:"down_singular,omitempty"` 23 24 Columns map[string]string `toml:"columns,omitempty" json:"columns,omitempty"` 25 Relationships map[string]RelationshipAlias `toml:"relationships,omitempty" json:"relationships,omitempty"` 26 } 27 28 // RelationshipAlias defines the naming for both sides of 29 // a foreign key. 30 type RelationshipAlias struct { 31 Local string `toml:"local,omitempty" json:"local,omitempty"` 32 Foreign string `toml:"foreign,omitempty" json:"foreign,omitempty"` 33 } 34 35 // FillAliases takes the table information from the driver 36 // and fills in aliases where the user has provided none. 37 // 38 // This leaves us with a complete list of Go names for all tables, 39 // columns, and relationships. 40 func FillAliases(a *Aliases, tables []drivers.Table) { 41 if a.Tables == nil { 42 a.Tables = make(map[string]TableAlias) 43 } 44 45 for _, t := range tables { 46 if t.IsJoinTable { 47 jt, ok := a.Tables[t.Name] 48 if !ok { 49 a.Tables[t.Name] = TableAlias{Relationships: make(map[string]RelationshipAlias)} 50 } else if jt.Relationships == nil { 51 jt.Relationships = make(map[string]RelationshipAlias) 52 } 53 continue 54 } 55 56 table := a.Tables[t.Name] 57 58 if len(table.UpPlural) == 0 { 59 table.UpPlural = strmangle.TitleCase(strmangle.Plural(t.Name)) 60 } 61 if len(table.UpSingular) == 0 { 62 table.UpSingular = strmangle.TitleCase(strmangle.Singular(t.Name)) 63 } 64 if len(table.DownPlural) == 0 { 65 table.DownPlural = strmangle.CamelCase(strmangle.Plural(t.Name)) 66 } 67 if len(table.DownSingular) == 0 { 68 table.DownSingular = strmangle.CamelCase(strmangle.Singular(t.Name)) 69 } 70 71 if table.Columns == nil { 72 table.Columns = make(map[string]string) 73 } 74 if table.Relationships == nil { 75 table.Relationships = make(map[string]RelationshipAlias) 76 } 77 78 for _, c := range t.Columns { 79 if _, ok := table.Columns[c.Name]; !ok { 80 table.Columns[c.Name] = strmangle.TitleCase(c.Name) 81 } 82 83 r, _ := utf8.DecodeRuneInString(table.Columns[c.Name]) 84 if unicode.IsNumber(r) { 85 table.Columns[c.Name] = "C" + table.Columns[c.Name] 86 } 87 } 88 89 a.Tables[t.Name] = table 90 91 for _, k := range t.FKeys { 92 r := table.Relationships[k.Name] 93 if len(r.Local) != 0 && len(r.Foreign) != 0 { 94 continue 95 } 96 97 local, foreign := txtNameToOne(k) 98 if len(r.Local) == 0 { 99 r.Local = local 100 } 101 if len(r.Foreign) == 0 { 102 r.Foreign = foreign 103 } 104 105 table.Relationships[k.Name] = r 106 } 107 108 } 109 110 for _, t := range tables { 111 if !t.IsJoinTable { 112 continue 113 } 114 115 table := a.Tables[t.Name] 116 117 lhs := t.FKeys[0] 118 rhs := t.FKeys[1] 119 120 lhsAlias, lhsOK := table.Relationships[lhs.Name] 121 rhsAlias, rhsOK := table.Relationships[rhs.Name] 122 123 if lhsOK && len(lhsAlias.Local) != 0 && len(lhsAlias.Foreign) != 0 && 124 rhsOK && len(rhsAlias.Local) != 0 && len(rhsAlias.Foreign) != 0 { 125 continue 126 } 127 128 // Here we actually reverse the meaning of local/foreign to be 129 // consistent with the way normal one-to-many relationships are done. 130 // That's to say local = the side with the foreign key. Now in a many-to-many 131 // if we were able to not have a join table our foreign key say "videos_id" 132 // would be on the tags table. Hence the relationships should look like: 133 // videos_tags.relationships.fk_video_id.local = "Tags" 134 // videos_tags.relationships.fk_video_id.foreign = "Videos" 135 // Consistent, yes. Confusing? Also yes. 136 137 lhsName, rhsName := txtNameToMany(lhs, rhs) 138 139 if len(lhsAlias.Local) != 0 { 140 rhsName = lhsAlias.Local 141 } else if len(rhsAlias.Local) != 0 { 142 lhsName = rhsAlias.Local 143 } 144 145 if len(lhsAlias.Foreign) != 0 { 146 lhsName = lhsAlias.Foreign 147 } else if len(rhsAlias.Foreign) != 0 { 148 rhsName = rhsAlias.Foreign 149 } 150 151 if len(lhsAlias.Local) == 0 { 152 lhsAlias.Local = rhsName 153 } 154 if len(lhsAlias.Foreign) == 0 { 155 lhsAlias.Foreign = lhsName 156 } 157 if len(rhsAlias.Local) == 0 { 158 rhsAlias.Local = lhsName 159 } 160 if len(rhsAlias.Foreign) == 0 { 161 rhsAlias.Foreign = rhsName 162 } 163 164 table.Relationships[lhs.Name] = lhsAlias 165 table.Relationships[rhs.Name] = rhsAlias 166 } 167 } 168 169 // Table gets a table alias, panics if not found. 170 func (a Aliases) Table(table string) TableAlias { 171 t, ok := a.Tables[table] 172 if !ok { 173 panic("could not find table aliases for: " + table) 174 } 175 176 return t 177 } 178 179 // Column get's a column's aliased name, panics if not found. 180 func (t TableAlias) Column(column string) string { 181 c, ok := t.Columns[column] 182 if !ok { 183 panic(fmt.Sprintf("could not find column alias for: %s.%s", t.UpSingular, column)) 184 } 185 186 return c 187 } 188 189 // Relationship looks up a relationship, panics if not found. 190 func (t TableAlias) Relationship(fkey string) RelationshipAlias { 191 r, ok := t.Relationships[fkey] 192 if !ok { 193 panic(fmt.Sprintf("could not find relationship alias for: %s.%s", t.UpSingular, fkey)) 194 } 195 196 return r 197 } 198 199 // ManyRelationship looks up a relationship alias, panics if not found. 200 // It will first try to look up a join table relationship, then it will 201 // try a normal one-to-many relationship. That's to say joinTable/joinTableFKey 202 // are used if they're not empty. 203 // 204 // This allows us to skip additional conditionals in the templates. 205 func (a Aliases) ManyRelationship(table, fkey, joinTable, joinTableFKey string) RelationshipAlias { 206 var lookupTable, lookupFKey string 207 if len(joinTable) != 0 { 208 lookupTable, lookupFKey = joinTable, joinTableFKey 209 } else { 210 lookupTable, lookupFKey = table, fkey 211 } 212 213 t := a.Table(lookupTable) 214 return t.Relationship(lookupFKey) 215 }