github.com/duskeagle/pop@v4.10.1-0.20190417200916-92f2b794aab5+incompatible/executors.go (about)

     1  package pop
     2  
     3  import (
     4  	"context"
     5  	"reflect"
     6  
     7  	"github.com/gobuffalo/pop/associations"
     8  	"github.com/gobuffalo/pop/columns"
     9  	"github.com/gobuffalo/pop/logging"
    10  	"github.com/gobuffalo/validate"
    11  	"github.com/gofrs/uuid"
    12  )
    13  
    14  // Reload fetch fresh data for a given model, using its ID.
    15  func (c *Connection) Reload(model interface{}) error {
    16  	sm := Model{Value: model}
    17  	return sm.iterate(func(m *Model) error {
    18  		return c.Find(context.TODO(), m.Value, m.ID())
    19  	})
    20  }
    21  
    22  // Exec runs the given query.
    23  func (q *Query) Exec() error {
    24  	return q.Connection.timeFunc("Exec", func() error {
    25  		sql, args := q.ToSQL(nil)
    26  		log(logging.SQL, sql, args...)
    27  		_, err := q.Connection.Store.Exec(sql, args...)
    28  		return err
    29  	})
    30  }
    31  
    32  // ExecWithCount runs the given query, and returns the amount of
    33  // affected rows.
    34  func (q *Query) ExecWithCount() (int, error) {
    35  	count := int64(0)
    36  	return int(count), q.Connection.timeFunc("Exec", func() error {
    37  		sql, args := q.ToSQL(nil)
    38  		log(logging.SQL, sql, args...)
    39  		result, err := q.Connection.Store.Exec(sql, args...)
    40  		if err != nil {
    41  			return err
    42  		}
    43  
    44  		count, err = result.RowsAffected()
    45  		return err
    46  	})
    47  }
    48  
    49  // ValidateAndSave applies validation rules on the given entry, then save it
    50  // if the validation succeed, excluding the given columns.
    51  func (c *Connection) ValidateAndSave(model interface{}, excludeColumns ...string) (*validate.Errors, error) {
    52  	sm := &Model{Value: model}
    53  	verrs, err := sm.validateSave(c)
    54  	if err != nil {
    55  		return verrs, err
    56  	}
    57  	if verrs.HasAny() {
    58  		return verrs, nil
    59  	}
    60  	return verrs, c.Save(model, excludeColumns...)
    61  }
    62  
    63  var emptyUUID = uuid.Nil.String()
    64  
    65  // IsZeroOfUnderlyingType will check if the value of anything is the equal to the Zero value of that type.
    66  func IsZeroOfUnderlyingType(x interface{}) bool {
    67  	return reflect.DeepEqual(x, reflect.Zero(reflect.TypeOf(x)).Interface())
    68  }
    69  
    70  // Save wraps the Create and Update methods. It executes a Create if no ID is provided with the entry;
    71  // or issues an Update otherwise.
    72  func (c *Connection) Save(model interface{}, excludeColumns ...string) error {
    73  	sm := &Model{Value: model}
    74  	return sm.iterate(func(m *Model) error {
    75  		id, err := m.fieldByName("ID")
    76  		if err != nil {
    77  			return err
    78  		}
    79  		if IsZeroOfUnderlyingType(id.Interface()) {
    80  			return c.Create(m.Value, excludeColumns...)
    81  		}
    82  		return c.Update(m.Value, excludeColumns...)
    83  	})
    84  }
    85  
    86  // ValidateAndCreate applies validation rules on the given entry, then creates it
    87  // if the validation succeed, excluding the given columns.
    88  func (c *Connection) ValidateAndCreate(model interface{}, excludeColumns ...string) (*validate.Errors, error) {
    89  	sm := &Model{Value: model}
    90  	verrs, err := sm.validateCreate(c)
    91  	if err != nil {
    92  		return verrs, err
    93  	}
    94  	if verrs.HasAny() {
    95  		return verrs, nil
    96  	}
    97  
    98  	if c.eager {
    99  		asos, err2 := associations.ForStruct(model, c.eagerFields...)
   100  
   101  		if err2 != nil {
   102  			return verrs, err2
   103  		}
   104  
   105  		if len(asos) == 0 {
   106  			c.disableEager()
   107  			return verrs, c.Create(model, excludeColumns...)
   108  		}
   109  
   110  		before := asos.AssociationsBeforeCreatable()
   111  		for index := range before {
   112  			i := before[index].BeforeInterface()
   113  			if i == nil {
   114  				continue
   115  			}
   116  
   117  			sm := &Model{Value: i}
   118  			verrs, err := sm.validateAndOnlyCreate(c)
   119  			if err != nil || verrs.HasAny() {
   120  				return verrs, err
   121  			}
   122  		}
   123  
   124  		after := asos.AssociationsAfterCreatable()
   125  		for index := range after {
   126  			i := after[index].AfterInterface()
   127  			if i == nil {
   128  				continue
   129  			}
   130  
   131  			sm := &Model{Value: i}
   132  			verrs, err := sm.validateAndOnlyCreate(c)
   133  			if err != nil || verrs.HasAny() {
   134  				return verrs, err
   135  			}
   136  		}
   137  
   138  		sm := &Model{Value: model}
   139  		verrs, err = sm.validateCreate(c)
   140  		if err != nil || verrs.HasAny() {
   141  			return verrs, err
   142  		}
   143  	}
   144  
   145  	return verrs, c.Create(model, excludeColumns...)
   146  }
   147  
   148  // Create add a new given entry to the database, excluding the given columns.
   149  // It updates `created_at` and `updated_at` columns automatically.
   150  //
   151  // Create support two modes:
   152  // * Flat (default): Associate existing nested objects only. NO creation or update of nested objects.
   153  // * Eager: Associate existing nested objects and create non-existent objects. NO change to existing objects.
   154  func (c *Connection) Create(model interface{}, excludeColumns ...string) error {
   155  	var isEager = c.eager
   156  
   157  	c.disableEager()
   158  
   159  	sm := &Model{Value: model}
   160  	return sm.iterate(func(m *Model) error {
   161  		return c.timeFunc("Create", func() error {
   162  			var localIsEager = isEager
   163  			asos, err := associations.ForStruct(m.Value, c.eagerFields...)
   164  			if err != nil {
   165  				return err
   166  			}
   167  
   168  			if localIsEager && len(asos) == 0 {
   169  				// No association, fallback to non-eager mode.
   170  				localIsEager = false
   171  			}
   172  
   173  			if err = m.beforeSave(c); err != nil {
   174  				return err
   175  			}
   176  
   177  			if err = m.beforeCreate(c); err != nil {
   178  				return err
   179  			}
   180  
   181  			processAssoc := len(asos) > 0
   182  
   183  			if processAssoc {
   184  				before := asos.AssociationsBeforeCreatable()
   185  				for index := range before {
   186  					i := before[index].BeforeInterface()
   187  					if i == nil {
   188  						continue
   189  					}
   190  
   191  					if localIsEager {
   192  						sm := &Model{Value: i}
   193  						err = sm.iterate(func(m *Model) error {
   194  							id, err := m.fieldByName("ID")
   195  							if err != nil {
   196  								return err
   197  							}
   198  							if IsZeroOfUnderlyingType(id.Interface()) {
   199  								return c.Create(m.Value)
   200  							}
   201  							return nil
   202  						})
   203  
   204  						if err != nil {
   205  							return err
   206  						}
   207  					}
   208  
   209  					err = before[index].BeforeSetup()
   210  					if err != nil {
   211  						return err
   212  					}
   213  				}
   214  			}
   215  
   216  			tn := m.TableName()
   217  			cols := columns.ForStructWithAlias(m.Value, tn, m.As)
   218  
   219  			if tn == sm.TableName() {
   220  				cols.Remove(excludeColumns...)
   221  			}
   222  
   223  			m.touchCreatedAt()
   224  			m.touchUpdatedAt()
   225  
   226  			if err = c.Dialect.Create(c.Store, m, cols); err != nil {
   227  				return err
   228  			}
   229  
   230  			if processAssoc {
   231  				after := asos.AssociationsAfterCreatable()
   232  				for index := range after {
   233  					if localIsEager {
   234  						err = after[index].AfterSetup()
   235  						if err != nil {
   236  							return err
   237  						}
   238  
   239  						i := after[index].AfterInterface()
   240  						if i == nil {
   241  							continue
   242  						}
   243  
   244  						sm := &Model{Value: i}
   245  						err = sm.iterate(func(m *Model) error {
   246  							fbn, err := m.fieldByName("ID")
   247  							if err != nil {
   248  								return err
   249  							}
   250  							id := fbn.Interface()
   251  							if IsZeroOfUnderlyingType(id) {
   252  								return c.Create(m.Value)
   253  							}
   254  							exists, errE := Q(c).Exists(i)
   255  							if errE != nil || !exists {
   256  								return c.Create(m.Value)
   257  							}
   258  							return nil
   259  						})
   260  
   261  						if err != nil {
   262  							return err
   263  						}
   264  					}
   265  					stm := after[index].AfterProcess()
   266  					if c.TX != nil && !stm.Empty() {
   267  						_, err := c.TX.Exec(c.Dialect.TranslateSQL(stm.Statement), stm.Args...)
   268  						if err != nil {
   269  							return err
   270  						}
   271  					}
   272  				}
   273  
   274  				stms := asos.AssociationsCreatableStatement()
   275  				for index := range stms {
   276  					statements := stms[index].Statements()
   277  					for _, stm := range statements {
   278  						if c.TX != nil {
   279  							_, err := c.TX.Exec(c.Dialect.TranslateSQL(stm.Statement), stm.Args...)
   280  							if err != nil {
   281  								return err
   282  							}
   283  							continue
   284  						}
   285  						_, err = c.Store.Exec(c.Dialect.TranslateSQL(stm.Statement), stm.Args...)
   286  						if err != nil {
   287  							return err
   288  						}
   289  					}
   290  				}
   291  			}
   292  
   293  			if err = m.afterCreate(c); err != nil {
   294  				return err
   295  			}
   296  
   297  			return m.afterSave(c)
   298  		})
   299  	})
   300  }
   301  
   302  // ValidateAndUpdate applies validation rules on the given entry, then update it
   303  // if the validation succeed, excluding the given columns.
   304  func (c *Connection) ValidateAndUpdate(model interface{}, excludeColumns ...string) (*validate.Errors, error) {
   305  	sm := &Model{Value: model}
   306  	verrs, err := sm.validateUpdate(c)
   307  	if err != nil {
   308  		return verrs, err
   309  	}
   310  	if verrs.HasAny() {
   311  		return verrs, nil
   312  	}
   313  	return verrs, c.Update(model, excludeColumns...)
   314  }
   315  
   316  // Update writes changes from an entry to the database, excluding the given columns.
   317  // It updates the `updated_at` column automatically.
   318  func (c *Connection) Update(model interface{}, excludeColumns ...string) error {
   319  	sm := &Model{Value: model}
   320  	return sm.iterate(func(m *Model) error {
   321  		return c.timeFunc("Update", func() error {
   322  			var err error
   323  
   324  			if err = m.beforeSave(c); err != nil {
   325  				return err
   326  			}
   327  			if err = m.beforeUpdate(c); err != nil {
   328  				return err
   329  			}
   330  
   331  			tn := m.TableName()
   332  			cols := columns.ForStructWithAlias(model, tn, m.As)
   333  			cols.Remove("id", "created_at")
   334  
   335  			if tn == sm.TableName() {
   336  				cols.Remove(excludeColumns...)
   337  			}
   338  
   339  			m.touchUpdatedAt()
   340  
   341  			if err = c.Dialect.Update(c.Store, m, cols); err != nil {
   342  				return err
   343  			}
   344  			if err = m.afterUpdate(c); err != nil {
   345  				return err
   346  			}
   347  
   348  			return m.afterSave(c)
   349  		})
   350  	})
   351  }
   352  
   353  // Destroy deletes a given entry from the database
   354  func (c *Connection) Destroy(model interface{}) error {
   355  	sm := &Model{Value: model}
   356  	return sm.iterate(func(m *Model) error {
   357  		return c.timeFunc("Destroy", func() error {
   358  			var err error
   359  
   360  			if err = m.beforeDestroy(c); err != nil {
   361  				return err
   362  			}
   363  			if err = c.Dialect.Destroy(c.Store, m); err != nil {
   364  				return err
   365  			}
   366  
   367  			return m.afterDestroy(c)
   368  		})
   369  	})
   370  }