github.com/paweljw/pop/v5@v5.4.6/model.go (about)

     1  package pop
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"reflect"
     7  	"strings"
     8  	"time"
     9  
    10  	nflect "github.com/gobuffalo/flect/name"
    11  
    12  	"github.com/gobuffalo/pop/v5/columns"
    13  	"github.com/pkg/errors"
    14  
    15  	"github.com/gobuffalo/flect"
    16  	"github.com/gofrs/uuid"
    17  )
    18  
    19  var nowFunc = time.Now
    20  
    21  // Value is the contents of a `Model`.
    22  type Value interface{}
    23  
    24  type modelIterable func(*Model) error
    25  
    26  // Model is used throughout Pop to wrap the end user interface
    27  // that is passed in to many functions.
    28  type Model struct {
    29  	Value
    30  	ctx context.Context
    31  	As  string
    32  }
    33  
    34  // NewModel returns a new model with the specified value and context.
    35  func NewModel(v Value, ctx context.Context) *Model {
    36  	return &Model{Value: v, ctx: ctx}
    37  }
    38  
    39  // ID returns the ID of the Model. All models must have an `ID` field this is
    40  // of type `int`,`int64` or of type `uuid.UUID`.
    41  func (m *Model) ID() interface{} {
    42  	fbn, err := m.fieldByName("ID")
    43  	if err != nil {
    44  		return nil
    45  	}
    46  	if pkt, _ := m.PrimaryKeyType(); pkt == "UUID" {
    47  		return fbn.Interface().(uuid.UUID).String()
    48  	}
    49  	return fbn.Interface()
    50  }
    51  
    52  // IDField returns the name of the DB field used for the ID.
    53  // By default, it will return "id".
    54  func (m *Model) IDField() string {
    55  	modelType := reflect.TypeOf(m.Value)
    56  
    57  	// remove all indirections
    58  	for modelType.Kind() == reflect.Slice || modelType.Kind() == reflect.Ptr || modelType.Kind() == reflect.Array {
    59  		modelType = modelType.Elem()
    60  	}
    61  
    62  	if modelType.Kind() == reflect.String {
    63  		return "id"
    64  	}
    65  
    66  	field, ok := modelType.FieldByName("ID")
    67  	if !ok {
    68  		return "id"
    69  	}
    70  	dbField := field.Tag.Get("db")
    71  	if dbField == "" {
    72  		return "id"
    73  	}
    74  	return dbField
    75  }
    76  
    77  // PrimaryKeyType gives the primary key type of the `Model`.
    78  func (m *Model) PrimaryKeyType() (string, error) {
    79  	fbn, err := m.fieldByName("ID")
    80  	if err != nil {
    81  		return "", errors.Errorf("model %T is missing required field ID", m.Value)
    82  	}
    83  	return fbn.Type().Name(), nil
    84  }
    85  
    86  // TableNameAble interface allows for the customize table mapping
    87  // between a name and the database. For example the value
    88  // `User{}` will automatically map to "users". Implementing `TableNameAble`
    89  // would allow this to change to be changed to whatever you would like.
    90  type TableNameAble interface {
    91  	TableName() string
    92  }
    93  
    94  // TableNameAbleWithContext is equal to TableNameAble but will
    95  // be passed the queries' context. Useful in cases where the
    96  // table name depends on e.g.
    97  type TableNameAbleWithContext interface {
    98  	TableName(ctx context.Context) string
    99  }
   100  
   101  // TableName returns the corresponding name of the underlying database table
   102  // for a given `Model`. See also `TableNameAble` to change the default name of the table.
   103  func (m *Model) TableName() string {
   104  	if s, ok := m.Value.(string); ok {
   105  		return s
   106  	}
   107  
   108  	if n, ok := m.Value.(TableNameAble); ok {
   109  		return n.TableName()
   110  	}
   111  
   112  	if n, ok := m.Value.(TableNameAbleWithContext); ok {
   113  		if m.ctx == nil {
   114  			m.ctx = context.TODO()
   115  		}
   116  		return n.TableName(m.ctx)
   117  	}
   118  
   119  	return m.typeName(reflect.TypeOf(m.Value))
   120  }
   121  
   122  func (m *Model) Columns() columns.Columns {
   123  	return columns.ForStructWithAlias(m.Value, m.TableName(), m.As, m.IDField())
   124  }
   125  
   126  func (m *Model) cacheKey(t reflect.Type) string {
   127  	return t.PkgPath() + "." + t.Name()
   128  }
   129  
   130  func (m *Model) typeName(t reflect.Type) (name string) {
   131  	if t.Kind() == reflect.Ptr {
   132  		t = t.Elem()
   133  	}
   134  	switch t.Kind() {
   135  	case reflect.Slice, reflect.Array:
   136  		el := t.Elem()
   137  		if el.Kind() == reflect.Ptr {
   138  			el = el.Elem()
   139  		}
   140  
   141  		// validates if the elem of slice or array implements TableNameAble interface.
   142  		var tableNameAble *TableNameAble
   143  		if el.Implements(reflect.TypeOf(tableNameAble).Elem()) {
   144  			v := reflect.New(el)
   145  			out := v.MethodByName("TableName").Call([]reflect.Value{})
   146  			return out[0].String()
   147  		}
   148  
   149  		// validates if the elem of slice or array implements TableNameAbleWithContext interface.
   150  		var tableNameAbleWithContext *TableNameAbleWithContext
   151  		if el.Implements(reflect.TypeOf(tableNameAbleWithContext).Elem()) {
   152  			v := reflect.New(el)
   153  			out := v.MethodByName("TableName").Call([]reflect.Value{reflect.ValueOf(m.ctx)})
   154  			return out[0].String()
   155  
   156  			// We do not want to cache contextualized TableNames because that would break
   157  			// the contextualization.
   158  		}
   159  		return nflect.Tableize(el.Name())
   160  	default:
   161  		return nflect.Tableize(t.Name())
   162  	}
   163  }
   164  
   165  func (m *Model) fieldByName(s string) (reflect.Value, error) {
   166  	el := reflect.ValueOf(m.Value).Elem()
   167  	fbn := el.FieldByName(s)
   168  	if !fbn.IsValid() {
   169  		return fbn, fmt.Errorf("model does not have a field named %s", s)
   170  	}
   171  	return fbn, nil
   172  }
   173  
   174  func (m *Model) associationName() string {
   175  	tn := flect.Singularize(m.TableName())
   176  	return fmt.Sprintf("%s_id", tn)
   177  }
   178  
   179  func (m *Model) setID(i interface{}) {
   180  	fbn, err := m.fieldByName("ID")
   181  	if err == nil {
   182  		v := reflect.ValueOf(i)
   183  		switch fbn.Kind() {
   184  		case reflect.Int, reflect.Int64:
   185  			fbn.SetInt(v.Int())
   186  		default:
   187  			fbn.Set(reflect.ValueOf(i))
   188  		}
   189  	}
   190  }
   191  
   192  func (m *Model) setCreatedAt(now time.Time) {
   193  	fbn, err := m.fieldByName("CreatedAt")
   194  	if err == nil {
   195  		v := fbn.Interface()
   196  		if !IsZeroOfUnderlyingType(v) {
   197  			// Do not override already set CreatedAt
   198  			return
   199  		}
   200  		switch v.(type) {
   201  		case int, int64:
   202  			fbn.SetInt(now.Unix())
   203  		default:
   204  			fbn.Set(reflect.ValueOf(now))
   205  		}
   206  	}
   207  }
   208  
   209  func (m *Model) setUpdatedAt(now time.Time) {
   210  	fbn, err := m.fieldByName("UpdatedAt")
   211  	if err == nil {
   212  		v := fbn.Interface()
   213  		switch v.(type) {
   214  		case int, int64:
   215  			fbn.SetInt(now.Unix())
   216  		default:
   217  			fbn.Set(reflect.ValueOf(now))
   218  		}
   219  	}
   220  }
   221  
   222  func (m *Model) whereID() string {
   223  	return fmt.Sprintf("%s.%s = ?", m.alias(), m.IDField())
   224  }
   225  
   226  func (m *Model) alias() string {
   227  	as := m.As
   228  	if as == "" {
   229  		as = strings.ReplaceAll(m.TableName(), ".", "_")
   230  	}
   231  	return as
   232  }
   233  
   234  func (m *Model) whereNamedID() string {
   235  	return fmt.Sprintf("%s.%s = :%s", m.alias(), m.IDField(), m.IDField())
   236  }
   237  
   238  func (m *Model) isSlice() bool {
   239  	v := reflect.Indirect(reflect.ValueOf(m.Value))
   240  	return v.Kind() == reflect.Slice || v.Kind() == reflect.Array
   241  }
   242  
   243  func (m *Model) iterate(fn modelIterable) error {
   244  	if m.isSlice() {
   245  		v := reflect.Indirect(reflect.ValueOf(m.Value))
   246  		for i := 0; i < v.Len(); i++ {
   247  			val := v.Index(i)
   248  			newModel := &Model{
   249  				Value: val.Addr().Interface(),
   250  				ctx:   m.ctx,
   251  			}
   252  			err := fn(newModel)
   253  
   254  			if err != nil {
   255  				return err
   256  			}
   257  		}
   258  		return nil
   259  	}
   260  
   261  	return fn(m)
   262  }