GOM ORM在GO中 - 多态关联 - 如何惯用亚型访问

发布于 2025-01-18 09:00:15 字数 2486 浏览 0 评论 0原文

我正在使用 Gorm ORM,并在物品和子类型武器/盔甲/珠宝之间设置了多态关联。

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
}

我希望能够有一个项目名称列表(例如库存列表)。最终,我希望能够找出所有子类型之间共享的任何其他公共属性(例如重量、成本等)。

我对现有的“解决方案”并不满意,我认为必须有更好的方法来做到这一点。有比我更有经验的人可以向我展示一种实现这一目标的模式吗?

我的想法是拥有一个嵌套函数,能够构建具有公共属性的 DTO。

但对于我想要支持的每种项目类型,我都需要一个 switch 语句。

// 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"}
    }
}

有没有更通用的方法来做到这一点?我在 Gorm 文档中找不到任何可以让您神奇地从 Item 中提取子类型的内容。我认为这很难正确输入提示,但也许存在某种反射方法可以让我提取公共属性?

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?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

青萝楚歌 2025-01-25 09:00:15

这是一个相当晚的回复,但我建议您也许应该再看看您的表关系。从数据库设计的角度来看,如果您在多个表上共享属性,那么可能值得将这些属性提取到另一个表并让每个原始表引用该表。简单的例子:

// originally, we have Car and Bike sharing the attributes NumWheels and Owner
type Car struct {
  NumWheels int
  Owner string
  Mileage int
  Make string
  Model string
}

type Bike struct {
  NumWheels int
  Owner string
  Color string
  Brand string
}

// one refactoring later...
type Vehicle struct {
  NumWheels int
  Owner string
}

type Car struct {
  Vehicle
  Mileage int
  Make string
  Model string
}

type Bike struct {
  Vehicle
  Color string
  Brand string
}

好的,这是一个简单的多态示例,其中汽车和自行车都是车辆,所以我们可以只使用 go 的结构嵌​​入语法。但如果您考虑项目,也许它们总是具有重量、成本等。您可以简单地将这些属性移至您的 Item 模型中,而不是将它们包含在子类型中。或者,您可以在结构上定义方法(不是 100% Go 用来描述这个概念的语言),以便您可以利用接口:

type Itemlike interface {
  GetWeight() int
  GetCost() int
  // etc
}

func GetProperties(i *Itemlike) map[string]int {
  res := map[string]int{"weight": i.GetWeight(), "cost": i.GetCost()}
  return res
}

显然,后面的示例对查询没有帮助,只有查询后才能使用这个GetProperties函数。因此,您可以查询库存中所有内容的完整列表,然后对该列表中的每个项目调用 GetProperties (或类似)函数

This is a pretty late response, but I would offer that perhaps you should take another look at your table relationship. From a database design perspective, if you have shared properties on multiple tables, then it may be worth extracting those properties out to another table and having each of the original tables reference that table. Simple example:

// originally, we have Car and Bike sharing the attributes NumWheels and Owner
type Car struct {
  NumWheels int
  Owner string
  Mileage int
  Make string
  Model string
}

type Bike struct {
  NumWheels int
  Owner string
  Color string
  Brand string
}

// one refactoring later...
type Vehicle struct {
  NumWheels int
  Owner string
}

type Car struct {
  Vehicle
  Mileage int
  Make string
  Model string
}

type Bike struct {
  Vehicle
  Color string
  Brand string
}

Okay, so that's a simple polymorphic example where car and bike are both vehicles, so we can just use go's struct embedding syntax. But if you consider items, perhaps they always have a weight, a cost, etc. You could simply move those attributes into your Item model instead of containing them in your sub types. Alternatively, you can define methods (not 100% of the language Go uses to describe this concept) on your structs so that you can utilize an interface:

type Itemlike interface {
  GetWeight() int
  GetCost() int
  // etc
}

func GetProperties(i *Itemlike) map[string]int {
  res := map[string]int{"weight": i.GetWeight(), "cost": i.GetCost()}
  return res
}

Obviously, this later example doesn't help with querying, only after querying are you able to use this GetProperties function. So you could query for the full list of everything in the inventory, then call the your GetProperties (or similar) function on each item in that list

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文