Gorm ORM in Go - Polymorphic associations - how to access the subtype idiomatically

Viewed 500

I'm using Gorm ORM and have a polymorphic association set up between Items and the subtypes Weapon/Armor/Jewelry.

type Item struct {
    models.Model
    // see https://gorm.io/docs/has_many.html#Polymorphism-Association
    SubID     string
    SubType   string
    CraftedBy string
}

type ItemWeaponSubtype struct {
    models.Model
    Items           []Item `gorm:"polymorphic:Sub;polymorphicValue:weapon"`
    Name            string
    Quality         string `gorm:"type:varchar(20)""`
    Material        string `gorm:"type:varchar(20)""`
    EquipmentSlotId string
    DamageBonuses
}

I want to be able to have a list of item names (e.g. for an inventory listing). Ultimately I want to be able to get out any other common attributes that are shared between all the subtypes (like maybe a weight, cost, etc).

I'm not happy with the "solution" that I have and I think that there has to be a better way to do this. Could anybody with more experience than me show me a pattern that accomplishes this?

My idea was to have a nested function that is able to build up the DTO that has the common attributes.

But I will need a switch statement for every item type that I want to support.

// ItemCommonDetails contains fields that all subtypes have and is useful for displaying inventory lists etc
type ItemCommonDetails struct {
    Name string
}

func (ir *ItemRepository) GetItemCommonDetailsFromId(itemId string) (ItemCommonDetails, error) {
    var item models.Item
    result := ir.db.First(&item, "id = ?", itemId)
    if 0 == result.RowsAffected {
        return ItemCommonDetails{Name: "Err!"}, &common_dto.StatusError{Code: http.StatusNotFound, Message: "Item [" + itemId + "] not found"}
    }

    defineReturn := func(result *gorm.DB, name string) (ItemCommonDetails, error) {
        if result.RowsAffected == 0 {
            return ItemCommonDetails{Name: "Err!"}, &common_dto.StatusError{Code: http.StatusNotFound, Message: "Item [" + itemId + "] not found"}
        }
        return ItemCommonDetails{Name: name}, nil
    }

    switch item.SubType {
    case "weapon":
        var weapon models.ItemWeaponSubtype
        result := ir.db.First(&weapon, "id = ?", item.SubID)
        return defineReturn(result, weapon.Name)
    case "armor":
        var armor models.ItemArmorSubtype
        result := ir.db.First(&armor, "id = ?", item.SubID)
        return defineReturn(result, armor.Name)
    case "jewelry":
        var jewelry models.ItemJewelrySubtype
        result := ir.db.First(&jewelry, "id = ?", item.SubID)
        return defineReturn(result, jewelry.Name)
    default:
        return ItemCommonDetails{Name: "Err!"}, &common_dto.StatusError{Code: http.StatusNotFound, Message: "Item [" + itemId + "] not found"}
    }
}

Is there a more general way to do this? I can't find anything in the Gorm documentation that lets you magically pull the subtype from the Item. I think this would be hard to type hint properly, but maybe some sort of reflection method exists that would let me pull out common attributes?

0 Answers
Related