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 }