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  }