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  }