github.com/paweljw/pop@v4.13.1+incompatible/associations/has_one_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/internal/defaults"
    10  )
    11  
    12  // hasOneAssociation is a 1 to 1 kind of association. It's used on
    13  // the side the association foreign key is not defined.
    14  //
    15  // See the belongsToAssociation for the other side of the relation.
    16  type hasOneAssociation struct {
    17  	ownedTableName string
    18  	ownedModel     reflect.Value
    19  	ownedType      reflect.Type
    20  	ownerID        interface{}
    21  	ownerName      string
    22  	owner          interface{}
    23  	fkID           string
    24  	*associationSkipable
    25  	*associationComposite
    26  }
    27  
    28  func init() {
    29  	associationBuilders["has_one"] = hasOneAssociationBuilder
    30  }
    31  
    32  func hasOneAssociationBuilder(p associationParams) (Association, error) {
    33  	// Validates if ownerIDField is nil, this association will be skipped.
    34  	var skipped bool
    35  	ownerID := p.modelValue.FieldByName("ID")
    36  	if fieldIsNil(ownerID) {
    37  		skipped = true
    38  	}
    39  
    40  	ownerName := p.modelType.Name()
    41  	fk := defaults.String(p.popTags.Find("fk_id").Value, flect.Underscore(ownerName)+"_id")
    42  
    43  	fval := p.modelValue.FieldByName(p.field.Name)
    44  	return &hasOneAssociation{
    45  		owner:          p.model,
    46  		ownedTableName: flect.Pluralize(p.popTags.Find("has_one").Value),
    47  		ownedModel:     fval,
    48  		ownedType:      fval.Type(),
    49  		ownerID:        ownerID.Interface(),
    50  		ownerName:      ownerName,
    51  		fkID:           fk,
    52  		associationSkipable: &associationSkipable{
    53  			skipped: skipped,
    54  		},
    55  		associationComposite: &associationComposite{innerAssociations: p.innerAssociations},
    56  	}, nil
    57  }
    58  
    59  func (h *hasOneAssociation) Kind() reflect.Kind {
    60  	if h.ownedType.Kind() == reflect.Ptr {
    61  		return h.ownedType.Elem().Kind()
    62  	}
    63  	return h.ownedType.Kind()
    64  }
    65  
    66  func (h *hasOneAssociation) Interface() interface{} {
    67  	if h.ownedModel.Kind() == reflect.Ptr {
    68  		val := reflect.New(h.ownedType.Elem())
    69  		h.ownedModel.Set(val)
    70  		return h.ownedModel.Interface()
    71  	}
    72  	return h.ownedModel.Addr().Interface()
    73  }
    74  
    75  // Constraint returns the content for the WHERE clause, and the args
    76  // needed to execute it.
    77  func (h *hasOneAssociation) Constraint() (string, []interface{}) {
    78  	return fmt.Sprintf("%s = ?", h.fkID), []interface{}{h.ownerID}
    79  }
    80  
    81  func (h *hasOneAssociation) AfterSetup() error {
    82  	om := h.ownedModel
    83  	if fieldIsNil(om) {
    84  		return nil
    85  	}
    86  	ownerID := reflect.Indirect(reflect.ValueOf(h.owner)).FieldByName("ID").Interface()
    87  	if om.Kind() == reflect.Ptr {
    88  		om = om.Elem()
    89  	}
    90  	fval := om.FieldByName(h.ownerName + "ID")
    91  	if fval.CanSet() {
    92  		if n := nulls.New(fval.Interface()); n != nil {
    93  			fval.Set(reflect.ValueOf(n.Parse(ownerID)))
    94  		} else {
    95  			fval.Set(reflect.ValueOf(ownerID))
    96  		}
    97  		return nil
    98  	}
    99  
   100  	return fmt.Errorf("could not set '%s' to '%s'", ownerID, fval)
   101  }
   102  
   103  // AfterInterface gets the value of the model to create after
   104  // creating the parent model. It returns nil if its value is
   105  // not set.
   106  func (h *hasOneAssociation) AfterInterface() interface{} {
   107  	m := h.ownedModel
   108  	if fieldIsNil(m) {
   109  		return nil
   110  	}
   111  	if m.Kind() == reflect.Ptr {
   112  		return m.Interface()
   113  	}
   114  	if IsZeroOfUnderlyingType(m.Interface()) {
   115  		return nil
   116  	}
   117  
   118  	return m.Addr().Interface()
   119  }
   120  
   121  func (h *hasOneAssociation) AfterProcess() AssociationStatement {
   122  	belongingIDFieldName := "ID"
   123  	om := h.ownedModel
   124  	if om.Kind() == reflect.Ptr {
   125  		om = om.Elem()
   126  	}
   127  	// Skip if the related model is not set
   128  	if IsZeroOfUnderlyingType(om) {
   129  		return AssociationStatement{
   130  			Statement: "",
   131  			Args:      []interface{}{},
   132  		}
   133  	}
   134  	id := om.FieldByName(belongingIDFieldName).Interface()
   135  	if IsZeroOfUnderlyingType(id) {
   136  		return AssociationStatement{
   137  			Statement: "",
   138  			Args:      []interface{}{},
   139  		}
   140  	}
   141  
   142  	ownerIDFieldName := "ID"
   143  	ownerID := reflect.Indirect(reflect.ValueOf(h.owner)).FieldByName(ownerIDFieldName).Interface()
   144  
   145  	ids := []interface{}{ownerID}
   146  	ids = append(ids, id)
   147  
   148  	ret := fmt.Sprintf("UPDATE %s SET %s = ? WHERE %s = ?", h.ownedTableName, h.fkID, belongingIDFieldName)
   149  
   150  	return AssociationStatement{
   151  		Statement: ret,
   152  		Args:      ids,
   153  	}
   154  }