github.com/wangyougui/gf/v2@v2.6.5/database/gdb/gdb_model_join.go (about) 1 // Copyright GoFrame Author(https://goframe.org). All Rights Reserved. 2 // 3 // This Source Code Form is subject to the terms of the MIT License. 4 // If a copy of the MIT was not distributed with this file, 5 // You can obtain one at https://github.com/wangyougui/gf. 6 7 package gdb 8 9 import ( 10 "fmt" 11 12 "github.com/wangyougui/gf/v2/text/gstr" 13 ) 14 15 // LeftJoin does "LEFT JOIN ... ON ..." statement on the model. 16 // The parameter `table` can be joined table and its joined condition, 17 // and also with its alias name. 18 // 19 // Eg: 20 // Model("user").LeftJoin("user_detail", "user_detail.uid=user.uid") 21 // Model("user", "u").LeftJoin("user_detail", "ud", "ud.uid=u.uid") 22 // Model("user", "u").LeftJoin("SELECT xxx FROM xxx","a", "a.uid=u.uid"). 23 func (m *Model) LeftJoin(tableOrSubQueryAndJoinConditions ...string) *Model { 24 return m.doJoin(joinOperatorLeft, tableOrSubQueryAndJoinConditions...) 25 } 26 27 // RightJoin does "RIGHT JOIN ... ON ..." statement on the model. 28 // The parameter `table` can be joined table and its joined condition, 29 // and also with its alias name. 30 // 31 // Eg: 32 // Model("user").RightJoin("user_detail", "user_detail.uid=user.uid") 33 // Model("user", "u").RightJoin("user_detail", "ud", "ud.uid=u.uid") 34 // Model("user", "u").RightJoin("SELECT xxx FROM xxx","a", "a.uid=u.uid"). 35 func (m *Model) RightJoin(tableOrSubQueryAndJoinConditions ...string) *Model { 36 return m.doJoin(joinOperatorRight, tableOrSubQueryAndJoinConditions...) 37 } 38 39 // InnerJoin does "INNER JOIN ... ON ..." statement on the model. 40 // The parameter `table` can be joined table and its joined condition, 41 // and also with its alias name。 42 // 43 // Eg: 44 // Model("user").InnerJoin("user_detail", "user_detail.uid=user.uid") 45 // Model("user", "u").InnerJoin("user_detail", "ud", "ud.uid=u.uid") 46 // Model("user", "u").InnerJoin("SELECT xxx FROM xxx","a", "a.uid=u.uid"). 47 func (m *Model) InnerJoin(tableOrSubQueryAndJoinConditions ...string) *Model { 48 return m.doJoin(joinOperatorInner, tableOrSubQueryAndJoinConditions...) 49 } 50 51 // LeftJoinOnField performs as LeftJoin, but it joins both tables with the `same field name`. 52 // 53 // Eg: 54 // Model("order").LeftJoinOnField("user", "user_id") 55 // Model("order").LeftJoinOnField("product", "product_id"). 56 func (m *Model) LeftJoinOnField(table, field string) *Model { 57 return m.doJoin(joinOperatorLeft, table, fmt.Sprintf( 58 `%s.%s=%s.%s`, 59 m.tablesInit, 60 m.db.GetCore().QuoteWord(field), 61 m.db.GetCore().QuoteWord(table), 62 m.db.GetCore().QuoteWord(field), 63 )) 64 } 65 66 // RightJoinOnField performs as RightJoin, but it joins both tables with the `same field name`. 67 // 68 // Eg: 69 // Model("order").InnerJoinOnField("user", "user_id") 70 // Model("order").InnerJoinOnField("product", "product_id"). 71 func (m *Model) RightJoinOnField(table, field string) *Model { 72 return m.doJoin(joinOperatorRight, table, fmt.Sprintf( 73 `%s.%s=%s.%s`, 74 m.tablesInit, 75 m.db.GetCore().QuoteWord(field), 76 m.db.GetCore().QuoteWord(table), 77 m.db.GetCore().QuoteWord(field), 78 )) 79 } 80 81 // InnerJoinOnField performs as InnerJoin, but it joins both tables with the `same field name`. 82 // 83 // Eg: 84 // Model("order").InnerJoinOnField("user", "user_id") 85 // Model("order").InnerJoinOnField("product", "product_id"). 86 func (m *Model) InnerJoinOnField(table, field string) *Model { 87 return m.doJoin(joinOperatorInner, table, fmt.Sprintf( 88 `%s.%s=%s.%s`, 89 m.tablesInit, 90 m.db.GetCore().QuoteWord(field), 91 m.db.GetCore().QuoteWord(table), 92 m.db.GetCore().QuoteWord(field), 93 )) 94 } 95 96 // LeftJoinOnFields performs as LeftJoin. It specifies different fields and comparison operator. 97 // 98 // Eg: 99 // Model("user").LeftJoinOnFields("order", "id", "=", "user_id") 100 // Model("user").LeftJoinOnFields("order", "id", ">", "user_id") 101 // Model("user").LeftJoinOnFields("order", "id", "<", "user_id") 102 func (m *Model) LeftJoinOnFields(table, firstField, operator, secondField string) *Model { 103 return m.doJoin(joinOperatorLeft, table, fmt.Sprintf( 104 `%s.%s %s %s.%s`, 105 m.tablesInit, 106 m.db.GetCore().QuoteWord(firstField), 107 operator, 108 m.db.GetCore().QuoteWord(table), 109 m.db.GetCore().QuoteWord(secondField), 110 )) 111 } 112 113 // RightJoinOnFields performs as RightJoin. It specifies different fields and comparison operator. 114 // 115 // Eg: 116 // Model("user").RightJoinOnFields("order", "id", "=", "user_id") 117 // Model("user").RightJoinOnFields("order", "id", ">", "user_id") 118 // Model("user").RightJoinOnFields("order", "id", "<", "user_id") 119 func (m *Model) RightJoinOnFields(table, firstField, operator, secondField string) *Model { 120 return m.doJoin(joinOperatorRight, table, fmt.Sprintf( 121 `%s.%s %s %s.%s`, 122 m.tablesInit, 123 m.db.GetCore().QuoteWord(firstField), 124 operator, 125 m.db.GetCore().QuoteWord(table), 126 m.db.GetCore().QuoteWord(secondField), 127 )) 128 } 129 130 // InnerJoinOnFields performs as InnerJoin. It specifies different fields and comparison operator. 131 // 132 // Eg: 133 // Model("user").InnerJoinOnFields("order", "id", "=", "user_id") 134 // Model("user").InnerJoinOnFields("order", "id", ">", "user_id") 135 // Model("user").InnerJoinOnFields("order", "id", "<", "user_id") 136 func (m *Model) InnerJoinOnFields(table, firstField, operator, secondField string) *Model { 137 return m.doJoin(joinOperatorInner, table, fmt.Sprintf( 138 `%s.%s %s %s.%s`, 139 m.tablesInit, 140 m.db.GetCore().QuoteWord(firstField), 141 operator, 142 m.db.GetCore().QuoteWord(table), 143 m.db.GetCore().QuoteWord(secondField), 144 )) 145 } 146 147 // doJoin does "LEFT/RIGHT/INNER JOIN ... ON ..." statement on the model. 148 // The parameter `tableOrSubQueryAndJoinConditions` can be joined table and its joined condition, 149 // and also with its alias name. 150 // 151 // Eg: 152 // Model("user").InnerJoin("user_detail", "user_detail.uid=user.uid") 153 // Model("user", "u").InnerJoin("user_detail", "ud", "ud.uid=u.uid") 154 // Model("user", "u").InnerJoin("user_detail", "ud", "ud.uid>u.uid") 155 // Model("user", "u").InnerJoin("SELECT xxx FROM xxx","a", "a.uid=u.uid") 156 // Related issues: 157 // https://github.com/wangyougui/gf/issues/1024 158 func (m *Model) doJoin(operator joinOperator, tableOrSubQueryAndJoinConditions ...string) *Model { 159 var ( 160 model = m.getModel() 161 joinStr = "" 162 table string 163 alias string 164 ) 165 // Check the first parameter table or sub-query. 166 if len(tableOrSubQueryAndJoinConditions) > 0 { 167 if isSubQuery(tableOrSubQueryAndJoinConditions[0]) { 168 joinStr = gstr.Trim(tableOrSubQueryAndJoinConditions[0]) 169 if joinStr[0] != '(' { 170 joinStr = "(" + joinStr + ")" 171 } 172 } else { 173 table = tableOrSubQueryAndJoinConditions[0] 174 joinStr = m.db.GetCore().QuotePrefixTableName(table) 175 } 176 } 177 // Generate join condition statement string. 178 conditionLength := len(tableOrSubQueryAndJoinConditions) 179 switch { 180 case conditionLength > 2: 181 alias = tableOrSubQueryAndJoinConditions[1] 182 model.tables += fmt.Sprintf( 183 " %s JOIN %s AS %s ON (%s)", 184 operator, joinStr, 185 m.db.GetCore().QuoteWord(alias), 186 tableOrSubQueryAndJoinConditions[2], 187 ) 188 m.tableAliasMap[alias] = table 189 190 case conditionLength == 2: 191 model.tables += fmt.Sprintf( 192 " %s JOIN %s ON (%s)", 193 operator, joinStr, tableOrSubQueryAndJoinConditions[1], 194 ) 195 196 case conditionLength == 1: 197 model.tables += fmt.Sprintf( 198 " %s JOIN %s", operator, joinStr, 199 ) 200 } 201 return model 202 } 203 204 // getTableNameByPrefixOrAlias checks and returns the table name if `prefixOrAlias` is an alias of a table, 205 // it or else returns the `prefixOrAlias` directly. 206 func (m *Model) getTableNameByPrefixOrAlias(prefixOrAlias string) string { 207 value, ok := m.tableAliasMap[prefixOrAlias] 208 if ok { 209 return value 210 } 211 return prefixOrAlias 212 } 213 214 // isSubQuery checks and returns whether given string a sub-query sql string. 215 func isSubQuery(s string) bool { 216 s = gstr.TrimLeft(s, "()") 217 if p := gstr.Pos(s, " "); p != -1 { 218 if gstr.Equal(s[:p], "select") { 219 return true 220 } 221 } 222 return false 223 }