github.com/paweljw/pop@v4.13.1+incompatible/associations/association.go (about)

     1  package associations
     2  
     3  import (
     4  	"reflect"
     5  
     6  	"github.com/gobuffalo/nulls"
     7  	"github.com/gobuffalo/pop/columns"
     8  )
     9  
    10  // Association represents a definition of a model association
    11  // field. It can represent a association of the type has_many
    12  // belongs_to or has_one, and other customized types.
    13  type Association interface {
    14  	Kind() reflect.Kind
    15  	Interface() interface{}
    16  	Constraint() (string, []interface{})
    17  	InnerAssociations() InnerAssociations
    18  	Skipped() bool
    19  }
    20  
    21  // associationSkipable is a helper struct that helps
    22  // to include skippable behavior in associations.
    23  type associationSkipable struct {
    24  	skipped bool
    25  }
    26  
    27  func (a *associationSkipable) Skipped() bool {
    28  	return a.skipped
    29  }
    30  
    31  // associationComposite adds the ability for a Association to
    32  // have nested associations.
    33  type associationComposite struct {
    34  	innerAssociations InnerAssociations
    35  }
    36  
    37  func (a *associationComposite) InnerAssociations() InnerAssociations {
    38  	return a.innerAssociations
    39  }
    40  
    41  // InnerAssociation is a struct that represents a deep level
    42  // association. per example Song.Composer, Composer is an inner
    43  // association for Song.
    44  type InnerAssociation struct {
    45  	Name   string
    46  	Fields string
    47  }
    48  
    49  // InnerAssociations is a group of InnerAssociation.
    50  type InnerAssociations []InnerAssociation
    51  
    52  // AssociationSortable allows a type to be sortable.
    53  type AssociationSortable interface {
    54  	OrderBy() string
    55  	Association
    56  }
    57  
    58  // AssociationBeforeCreatable allows an association to be created before
    59  // the parent structure.
    60  type AssociationBeforeCreatable interface {
    61  	BeforeInterface() interface{}
    62  	BeforeSetup() error
    63  	Association
    64  }
    65  
    66  // AssociationAfterCreatable allows an association to be created after
    67  // the parent structure.
    68  type AssociationAfterCreatable interface {
    69  	AfterInterface() interface{}
    70  	AfterSetup() error
    71  	AfterProcess() AssociationStatement
    72  	Association
    73  }
    74  
    75  // AssociationCreatableStatement a association that defines
    76  // create statements on database.
    77  type AssociationCreatableStatement interface {
    78  	Statements() []AssociationStatement
    79  	Association
    80  }
    81  
    82  // AssociationStatement a type that represents a statement to be
    83  // executed.
    84  type AssociationStatement struct {
    85  	Statement string
    86  	Args      []interface{}
    87  }
    88  
    89  // Empty is true if the containing Statement is empty.
    90  func (as AssociationStatement) Empty() bool {
    91  	return as.Statement == ""
    92  }
    93  
    94  // Associations a group of model associations.
    95  type Associations []Association
    96  
    97  // AssociationsBeforeCreatable returns all associations that implement AssociationBeforeCreatable
    98  // interface. Belongs To association is an example of this implementation.
    99  func (a Associations) AssociationsBeforeCreatable() []AssociationBeforeCreatable {
   100  	var before []AssociationBeforeCreatable
   101  	for i := range a {
   102  		if _, ok := a[i].(AssociationBeforeCreatable); ok {
   103  			before = append(before, a[i].(AssociationBeforeCreatable))
   104  		}
   105  	}
   106  	return before
   107  }
   108  
   109  // AssociationsAfterCreatable returns all associations that implement AssociationAfterCreatable
   110  // interface. Has Many and Has One associations are examples of this implementation.
   111  func (a Associations) AssociationsAfterCreatable() []AssociationAfterCreatable {
   112  	var after []AssociationAfterCreatable
   113  	for i := range a {
   114  		if _, ok := a[i].(AssociationAfterCreatable); ok {
   115  			after = append(after, a[i].(AssociationAfterCreatable))
   116  		}
   117  	}
   118  	return after
   119  }
   120  
   121  // AssociationsCreatableStatement returns all associations that implement AssociationCreatableStament
   122  // interface. Many To Many association is an example of this implementation.
   123  func (a Associations) AssociationsCreatableStatement() []AssociationCreatableStatement {
   124  	var stm []AssociationCreatableStatement
   125  	for i := range a {
   126  		if _, ok := a[i].(AssociationCreatableStatement); ok {
   127  			stm = append(stm, a[i].(AssociationCreatableStatement))
   128  		}
   129  	}
   130  	return stm
   131  }
   132  
   133  // associationParams is a wrapper for associations definition
   134  // and creation.
   135  type associationParams struct {
   136  	field             reflect.StructField // an association field defined in model.
   137  	modelType         reflect.Type        // the model type where this field is defined.
   138  	modelValue        reflect.Value       // the model value where this field is defined.
   139  	popTags           columns.Tags        // the tags defined in this association field.
   140  	model             interface{}         // the model, owner of the association.
   141  	innerAssociations InnerAssociations   // the data for the deep level associations.
   142  }
   143  
   144  // associationBuilder is a type representing an association builder implementation.
   145  // see the builder defined in ./has_many_association.go as a guide of how to use it.
   146  type associationBuilder func(associationParams) (Association, error)
   147  
   148  // fieldIsNil validates if a field has a nil reference. Also
   149  // it validates if a field implements nullable interface and
   150  // it has a nil value.
   151  func fieldIsNil(f reflect.Value) bool {
   152  	if n := nulls.New(f.Interface()); n != nil {
   153  		return n.Interface() == nil
   154  	}
   155  	if f.Kind() == reflect.Interface || f.Kind() == reflect.Ptr {
   156  		return f.IsNil()
   157  	}
   158  	return f.Interface() == nil
   159  }
   160  
   161  // IsZeroOfUnderlyingType will check if the value of anything is the equal to the Zero value of that type.
   162  func IsZeroOfUnderlyingType(x interface{}) bool {
   163  	return reflect.DeepEqual(x, reflect.Zero(reflect.TypeOf(x)).Interface())
   164  }