github.com/dolanor/pop@v4.13.0+incompatible/associations/belongs_to_association.go (about)

     1  package associations
     2  
     3  import (
     4  	"fmt"
     5  	"reflect"
     6  
     7  	"github.com/gobuffalo/flect"
     8  	"github.com/gobuffalo/nulls"
     9  	"github.com/gobuffalo/pop/columns"
    10  	"github.com/gobuffalo/pop/internal/defaults"
    11  )
    12  
    13  // belongsToAssociation is the implementation for the belongs_to association type in a model.
    14  type belongsToAssociation struct {
    15  	ownerModel reflect.Value
    16  	ownerType  reflect.Type
    17  	ownerID    reflect.Value
    18  	primaryID  string
    19  	ownedModel interface{}
    20  	*associationSkipable
    21  	*associationComposite
    22  
    23  	primaryTableID string
    24  }
    25  
    26  func init() {
    27  	associationBuilders["belongs_to"] = belongsToAssociationBuilder
    28  }
    29  
    30  func belongsToAssociationBuilder(p associationParams) (Association, error) {
    31  	ownerVal := p.modelValue.FieldByName(p.field.Name)
    32  	tags := p.popTags
    33  	primaryIDField := defaults.String(tags.Find("primary_id").Value, "ID")
    34  	ownerIDField := defaults.String(tags.Find("fk_id").Value, fmt.Sprintf("%s%s", p.field.Name, "ID"))
    35  
    36  	// belongs_to requires an holding field for the foreign model ID.
    37  	if _, found := p.modelType.FieldByName(ownerIDField); !found {
    38  		return nil, fmt.Errorf("there is no '%s' defined in model '%s'", ownerIDField, p.modelType.Name())
    39  	}
    40  
    41  	// If ownerIDField is nil, this association will be skipped.
    42  	var skipped bool
    43  	f := p.modelValue.FieldByName(ownerIDField)
    44  	if fieldIsNil(f) || IsZeroOfUnderlyingType(f.Interface()) {
    45  		skipped = true
    46  	}
    47  	// associated model
    48  	ownerPk := "id"
    49  	if primaryIDField != "ID" {
    50  		ownerModel := reflect.Indirect(ownerVal)
    51  		ownerPrimaryField, found := ownerModel.Type().FieldByName(primaryIDField)
    52  		if !found {
    53  			return nil, fmt.Errorf("there is no primary field '%s' defined in model '%s'", primaryIDField, ownerModel.Type())
    54  		}
    55  		ownerPTags := columns.TagsFor(ownerPrimaryField)
    56  		ownerPk = defaults.String(ownerPTags.Find("db").Value, flect.Underscore(ownerPrimaryField.Name))
    57  	}
    58  
    59  	return &belongsToAssociation{
    60  		ownerModel: ownerVal,
    61  		ownerType:  ownerVal.Type(),
    62  		ownerID:    f,
    63  		primaryID:  primaryIDField,
    64  		ownedModel: p.model,
    65  		associationSkipable: &associationSkipable{
    66  			skipped: skipped,
    67  		},
    68  		associationComposite: &associationComposite{innerAssociations: p.innerAssociations},
    69  		primaryTableID:       ownerPk,
    70  	}, nil
    71  }
    72  
    73  func (b *belongsToAssociation) Kind() reflect.Kind {
    74  	if b.ownerType.Kind() == reflect.Ptr {
    75  		return b.ownerType.Elem().Kind()
    76  	}
    77  	return b.ownerType.Kind()
    78  }
    79  
    80  func (b *belongsToAssociation) Interface() interface{} {
    81  	if b.ownerModel.Kind() == reflect.Ptr {
    82  		val := reflect.New(b.ownerType.Elem())
    83  		b.ownerModel.Set(val)
    84  		return b.ownerModel.Interface()
    85  	}
    86  	return b.ownerModel.Addr().Interface()
    87  }
    88  
    89  // Constraint returns the content for a where clause, and the args
    90  // needed to execute it.
    91  func (b *belongsToAssociation) Constraint() (string, []interface{}) {
    92  	return fmt.Sprintf("%s = ?", b.primaryTableID), []interface{}{b.ownerID.Interface()}
    93  }
    94  
    95  func (b *belongsToAssociation) BeforeInterface() interface{} {
    96  	// if the owner field is set, don't try to create the association to prevent conflicts.
    97  	if !b.skipped {
    98  		return nil
    99  	}
   100  
   101  	m := b.ownerModel
   102  	if m.Kind() == reflect.Ptr && !m.IsNil() {
   103  		m = b.ownerModel.Elem()
   104  	}
   105  
   106  	if IsZeroOfUnderlyingType(m.Interface()) {
   107  		return nil
   108  	}
   109  
   110  	return m.Addr().Interface()
   111  }
   112  
   113  func (b *belongsToAssociation) BeforeSetup() error {
   114  	ownerID := reflect.Indirect(reflect.ValueOf(b.ownerModel.Interface())).FieldByName("ID")
   115  	if b.ownerID.CanSet() {
   116  		if n := nulls.New(b.ownerID.Interface()); n != nil {
   117  			b.ownerID.Set(reflect.ValueOf(n.Parse(ownerID.Interface())))
   118  		} else if b.ownerID.Kind() == reflect.Ptr {
   119  			b.ownerID.Set(ownerID.Addr())
   120  		} else {
   121  			b.ownerID.Set(ownerID)
   122  		}
   123  		return nil
   124  	}
   125  	return fmt.Errorf("could not set '%s' to '%s'", ownerID, b.ownerID)
   126  }