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