gorm/join_table_handler.go

156 lines
5.1 KiB
Go
Raw Normal View History

2015-02-28 06:48:18 +03:00
package gorm
import (
2015-03-20 06:11:30 +03:00
"errors"
2015-02-28 06:48:18 +03:00
"fmt"
2015-03-18 13:14:28 +03:00
"reflect"
2015-02-28 06:48:18 +03:00
"strings"
)
2015-03-18 06:47:11 +03:00
type JoinTableHandlerInterface interface {
2015-03-20 06:11:30 +03:00
Setup(relationship *Relationship, tableName string, source reflect.Type, destination reflect.Type)
2015-03-18 06:47:11 +03:00
Table(db *DB) string
2015-03-20 06:11:30 +03:00
Add(db *DB, source interface{}, destination interface{}) error
2015-03-18 06:47:11 +03:00
Delete(db *DB, sources ...interface{}) error
JoinWith(db *DB, source interface{}) *DB
2015-02-28 06:48:18 +03:00
}
2015-03-19 10:02:15 +03:00
type JoinTableForeignKey struct {
DBName string
AssociationDBName string
}
2015-03-19 13:23:54 +03:00
type JoinTableSource struct {
ModelType reflect.Type
ForeignKeys []JoinTableForeignKey
}
type JoinTableHandler struct {
TableName string `sql:"-"`
Source JoinTableSource `sql:"-"`
Destination JoinTableSource `sql:"-"`
}
2015-03-20 06:11:30 +03:00
func (s *JoinTableHandler) Setup(relationship *Relationship, tableName string, source reflect.Type, destination reflect.Type) {
s.TableName = tableName
2015-03-19 10:02:15 +03:00
2015-03-20 06:11:30 +03:00
s.Source = JoinTableSource{ModelType: source}
sourceScope := &Scope{Value: reflect.New(source).Interface()}
for _, primaryField := range sourceScope.GetModelStruct().PrimaryFields {
if relationship.ForeignDBName == "" {
relationship.ForeignFieldName = source.Name() + primaryField.Name
relationship.ForeignDBName = ToDBName(relationship.ForeignFieldName)
}
2015-03-20 06:11:30 +03:00
s.Source.ForeignKeys = append(s.Source.ForeignKeys, JoinTableForeignKey{
DBName: relationship.ForeignDBName,
2015-03-19 10:02:15 +03:00
AssociationDBName: primaryField.DBName,
})
}
2015-03-20 06:11:30 +03:00
s.Destination = JoinTableSource{ModelType: destination}
destinationScope := &Scope{Value: reflect.New(destination).Interface()}
for _, primaryField := range destinationScope.GetModelStruct().PrimaryFields {
if relationship.AssociationForeignDBName == "" {
relationship.AssociationForeignFieldName = destination.Name() + primaryField.Name
relationship.AssociationForeignDBName = ToDBName(relationship.AssociationForeignFieldName)
}
2015-03-20 06:11:30 +03:00
s.Destination.ForeignKeys = append(s.Destination.ForeignKeys, JoinTableForeignKey{
DBName: relationship.AssociationForeignDBName,
2015-03-19 10:02:15 +03:00
AssociationDBName: primaryField.DBName,
})
}
}
2015-03-18 13:14:28 +03:00
func (s JoinTableHandler) Table(*DB) string {
return s.TableName
2015-03-18 06:47:11 +03:00
}
2015-02-28 06:48:18 +03:00
2015-03-18 13:14:28 +03:00
func (s JoinTableHandler) GetSearchMap(db *DB, sources ...interface{}) map[string]interface{} {
2015-03-18 06:47:11 +03:00
values := map[string]interface{}{}
2015-03-18 13:14:28 +03:00
2015-03-18 06:47:11 +03:00
for _, source := range sources {
scope := db.NewScope(source)
2015-03-18 13:14:28 +03:00
modelType := scope.GetModelStruct().ModelType
if s.Source.ModelType == modelType {
for _, foreignKey := range s.Source.ForeignKeys {
values[foreignKey.DBName] = scope.Fields()[foreignKey.AssociationDBName].Field.Interface()
}
} else if s.Destination.ModelType == modelType {
for _, foreignKey := range s.Destination.ForeignKeys {
values[foreignKey.DBName] = scope.Fields()[foreignKey.AssociationDBName].Field.Interface()
2015-03-18 06:47:11 +03:00
}
}
}
return values
2015-03-04 07:16:16 +03:00
}
2015-03-18 13:14:28 +03:00
func (s JoinTableHandler) Add(db *DB, source1 interface{}, source2 interface{}) error {
2015-02-28 06:48:18 +03:00
scope := db.NewScope("")
2015-03-18 13:14:28 +03:00
searchMap := s.GetSearchMap(db, source1, source2)
2015-03-18 06:47:11 +03:00
2015-03-18 13:14:28 +03:00
var assignColumns, binVars, conditions []string
2015-03-18 06:47:11 +03:00
var values []interface{}
2015-03-18 13:14:28 +03:00
for key, value := range searchMap {
assignColumns = append(assignColumns, key)
binVars = append(binVars, `?`)
conditions = append(conditions, fmt.Sprintf("%v = ?", scope.Quote(key)))
2015-03-18 06:47:11 +03:00
values = append(values, value)
}
2015-02-28 06:48:18 +03:00
2015-03-19 13:23:54 +03:00
for _, value := range values {
2015-03-18 06:47:11 +03:00
values = append(values, value)
}
2015-03-18 13:14:28 +03:00
quotedTable := s.Table(db)
2015-02-28 06:48:18 +03:00
sql := fmt.Sprintf(
2015-03-18 06:47:11 +03:00
"INSERT INTO %v (%v) SELECT %v %v WHERE NOT EXISTS (SELECT * FROM %v WHERE %v);",
quotedTable,
2015-03-18 13:14:28 +03:00
strings.Join(assignColumns, ","),
strings.Join(binVars, ","),
2015-02-28 06:48:18 +03:00
scope.Dialect().SelectFromDummyTable(),
2015-03-18 06:47:11 +03:00
quotedTable,
2015-03-18 13:14:28 +03:00
strings.Join(conditions, " AND "),
2015-02-28 06:48:18 +03:00
)
2015-03-18 06:47:11 +03:00
return db.Exec(sql, values...).Error
2015-02-28 06:48:18 +03:00
}
2015-03-18 13:14:28 +03:00
func (s JoinTableHandler) Delete(db *DB, sources ...interface{}) error {
var conditions []string
var values []interface{}
for key, value := range s.GetSearchMap(db, sources...) {
conditions = append(conditions, fmt.Sprintf("%v = ?", key))
values = append(values, value)
}
return db.Table(s.Table(db)).Where(strings.Join(conditions, " AND "), values...).Delete("").Error
2015-02-28 06:48:18 +03:00
}
2015-03-18 13:14:28 +03:00
func (s JoinTableHandler) JoinWith(db *DB, source interface{}) *DB {
quotedTable := s.Table(db)
scope := db.NewScope(source)
modelType := scope.GetModelStruct().ModelType
var joinConditions []string
var queryConditions []string
var values []interface{}
if s.Source.ModelType == modelType {
for _, foreignKey := range s.Destination.ForeignKeys {
2015-03-19 11:42:13 +03:00
destinationTableName := scope.New(reflect.New(s.Destination.ModelType).Interface()).QuotedTableName()
joinConditions = append(joinConditions, fmt.Sprintf("%v.%v = %v.%v", quotedTable, scope.Quote(foreignKey.DBName), destinationTableName, scope.Quote(foreignKey.AssociationDBName)))
2015-03-18 13:14:28 +03:00
}
for _, foreignKey := range s.Source.ForeignKeys {
queryConditions = append(queryConditions, fmt.Sprintf("%v.%v = ?", quotedTable, scope.Quote(foreignKey.DBName)))
values = append(values, scope.Fields()[foreignKey.AssociationDBName].Field.Interface())
}
2015-03-20 06:11:30 +03:00
return db.Joins(fmt.Sprintf("INNER JOIN %v ON %v", quotedTable, strings.Join(joinConditions, " AND "))).
Where(strings.Join(queryConditions, " AND "), values...)
} else {
db.Error = errors.New("wrong source type for join table handler")
return db
2015-03-18 13:14:28 +03:00
}
2015-02-28 06:48:18 +03:00
}