github.com/solongordon/pop@v4.10.0+incompatible/model.go (about) 1 package pop 2 3 import ( 4 "fmt" 5 "reflect" 6 "sync" 7 "time" 8 9 "github.com/gobuffalo/flect" 10 nflect "github.com/gobuffalo/flect/name" 11 "github.com/gofrs/uuid" 12 "github.com/pkg/errors" 13 ) 14 15 var tableMap = map[string]string{} 16 var tableMapMu = sync.RWMutex{} 17 18 // Value is the contents of a `Model`. 19 type Value interface{} 20 21 type modelIterable func(*Model) error 22 23 // Model is used throughout Pop to wrap the end user interface 24 // that is passed in to many functions. 25 type Model struct { 26 Value 27 tableName string 28 As string 29 } 30 31 // ID returns the ID of the Model. All models must have an `ID` field this is 32 // of type `int`,`int64` or of type `uuid.UUID`. 33 func (m *Model) ID() interface{} { 34 fbn, err := m.fieldByName("ID") 35 if err != nil { 36 return 0 37 } 38 if m.PrimaryKeyType() == "UUID" { 39 return fbn.Interface().(uuid.UUID).String() 40 } 41 return fbn.Interface() 42 } 43 44 // PrimaryKeyType gives the primary key type of the `Model`. 45 func (m *Model) PrimaryKeyType() string { 46 fbn, err := m.fieldByName("ID") 47 if err != nil { 48 return "int" 49 } 50 return fbn.Type().Name() 51 } 52 53 // TableNameAble interface allows for the customize table mapping 54 // between a name and the database. For example the value 55 // `User{}` will automatically map to "users". Implementing `TableNameAble` 56 // would allow this to change to be changed to whatever you would like. 57 type TableNameAble interface { 58 TableName() string 59 } 60 61 // TableName returns the corresponding name of the underlying database table 62 // for a given `Model`. See also `TableNameAble` to change the default name of the table. 63 func (m *Model) TableName() string { 64 if s, ok := m.Value.(string); ok { 65 return s 66 } 67 if n, ok := m.Value.(TableNameAble); ok { 68 return n.TableName() 69 } 70 71 if m.tableName != "" { 72 return m.tableName 73 } 74 75 t := reflect.TypeOf(m.Value) 76 name := m.typeName(t) 77 78 defer tableMapMu.Unlock() 79 tableMapMu.Lock() 80 81 if tableMap[name] == "" { 82 m.tableName = nflect.Tableize(name) 83 tableMap[name] = m.tableName 84 } 85 return tableMap[name] 86 } 87 88 func (m *Model) typeName(t reflect.Type) string { 89 if t.Kind() == reflect.Ptr { 90 t = t.Elem() 91 } 92 switch t.Kind() { 93 case reflect.Slice, reflect.Array: 94 el := t.Elem() 95 if el.Kind() == reflect.Ptr { 96 el = el.Elem() 97 } 98 99 // validates if the elem of slice or array implements TableNameAble interface. 100 tableNameAble := (*TableNameAble)(nil) 101 if el.Implements(reflect.TypeOf(tableNameAble).Elem()) { 102 v := reflect.New(el) 103 out := v.MethodByName("TableName").Call([]reflect.Value{}) 104 name := out[0].String() 105 if tableMap[el.Name()] == "" { 106 tableMap[el.Name()] = name 107 } 108 } 109 110 return el.Name() 111 default: 112 return t.Name() 113 } 114 } 115 116 func (m *Model) fieldByName(s string) (reflect.Value, error) { 117 el := reflect.ValueOf(m.Value).Elem() 118 fbn := el.FieldByName(s) 119 if !fbn.IsValid() { 120 return fbn, errors.Errorf("Model does not have a field named %s", s) 121 } 122 return fbn, nil 123 } 124 125 func (m *Model) associationName() string { 126 tn := flect.Singularize(m.TableName()) 127 return fmt.Sprintf("%s_id", tn) 128 } 129 130 func (m *Model) setID(i interface{}) { 131 fbn, err := m.fieldByName("ID") 132 if err == nil { 133 v := reflect.ValueOf(i) 134 switch fbn.Kind() { 135 case reflect.Int, reflect.Int64: 136 fbn.SetInt(v.Int()) 137 default: 138 fbn.Set(reflect.ValueOf(i)) 139 } 140 } 141 } 142 143 func (m *Model) touchCreatedAt() { 144 fbn, err := m.fieldByName("CreatedAt") 145 if err == nil { 146 now := time.Now() 147 switch fbn.Kind() { 148 case reflect.Int, reflect.Int64: 149 fbn.SetInt(now.Unix()) 150 default: 151 fbn.Set(reflect.ValueOf(now)) 152 } 153 } 154 } 155 156 func (m *Model) touchUpdatedAt() { 157 fbn, err := m.fieldByName("UpdatedAt") 158 if err == nil { 159 now := time.Now() 160 switch fbn.Kind() { 161 case reflect.Int, reflect.Int64: 162 fbn.SetInt(now.Unix()) 163 default: 164 fbn.Set(reflect.ValueOf(now)) 165 } 166 } 167 } 168 169 func (m *Model) whereID() string { 170 return fmt.Sprintf("%s.id = ?", m.TableName()) 171 } 172 173 func (m *Model) whereNamedID() string { 174 return fmt.Sprintf("%s.id = :id", m.TableName()) 175 } 176 177 func (m *Model) isSlice() bool { 178 v := reflect.Indirect(reflect.ValueOf(m.Value)) 179 return v.Kind() == reflect.Slice || v.Kind() == reflect.Array 180 } 181 182 func (m *Model) iterate(fn modelIterable) error { 183 if m.isSlice() { 184 v := reflect.Indirect(reflect.ValueOf(m.Value)) 185 for i := 0; i < v.Len(); i++ { 186 val := v.Index(i) 187 newModel := &Model{Value: val.Addr().Interface()} 188 err := fn(newModel) 189 190 if err != nil { 191 return err 192 } 193 } 194 return nil 195 } 196 197 return fn(m) 198 }