自我加入正确的关联以用于口袋妖怪及其进化吗?

Is a self join the correct association to use for a pokemon and its evolutions?

我正在努力研究口袋妖怪与其进化之间的正确关联。我认为我接近正确答案,但我在尝试对此建模以完成时遇到问题。这是我努力实现的目标。

bulbasaur = Pokemon.find(1)
bulbasaur.evolved_forms ---> [ivysaur, venasaur]

ivysaur = Pokemon.find(2)
ivysaur.evolved_forms ----> [venusaur]
ivysaur.pre-evolved_forms ---> [bulbasaur]

venusaur = Pokemon.find(3)
venusaur.evolved_forms ----> []
venusaur.pre-evolved_forms ---> [ivysaur, bulbasaur]

我有以下型号。

class Pokemon < ApplicationRecord
   has_many :evolved_forms, class_name: "Pokemon", foreign_key: "pre-evolved_form_id"
   belongs_to :pre-evolved_form, class_name: "Pokemon", optional: true

   has_many :pre-evolved_forms, class_name: "Pokemon", foreign_key: "evolved_form_id"
   belongs_to :evolved_form, class_name: "Pokemon", optional: true
end

这似乎是可行的,但我觉得好像缺少了一些东西,好像应该有某种有很多通过关联。如果有人有更好的建模,我将不胜感激。谢谢。

我会说你走在正确的道路上,但还不完全正确。 对您的 objective 有用的是树结构(a.k.a。层次结构)。因此,口袋妖怪将具有许多进化形式,并且只有一种它所属的预进化形式(或相反)。仅基于这一关系,整个逻辑就可以工作,您可以访问任何单个口袋妖怪的所有进化形式和所有进化前形式。

class Pokemon < ApplicationRecord
   belongs_to :pre_evolved_form, class_name: 'Pokemon', foreign_key: 'pre_evolved_form_id', optional: true
end

通过自己实现,您将需要编写辅助方法来访问进化形式(后代对象)和预进化形式(上升对象)。 不过,我建议您查看一个名为 ancestry 的 gem,这应该会让事情变得更容易一些。他们的自述文件看起来写得很好,所以我不会试图比他们更好地解释它。

同时查看分层对象结构。

可能有更好的东西,但我会像下面那样(嵌套地)做。它也适用于 Eevie 和其他可以有多个进化线的口袋妖怪。

class Pokemon < ApplicationRecord
  belongs_to :previous_evolved_form, class_name: 'Pokemon', optional: true
  has_many :next_evolved_forms, class_name: 'Pokeomn', foreign_key: :previous_evolved_form_id

  def ancestor_evolved_forms
    forms = {}
    
    if previous_evolved_form.present?
      forms[previous_evolved_form] = previous_evolved_form.ancestor_evolved_forms
    end
  end

  def descendant_evolved_forms
    forms = {}
    
    next_evolved_forms.each do |next_evolved_form|
      forms[next_evolved_form] = next_evolved_form.descendant_evolved_forms
    end
  end
end

用法示例

oddish = Pokemon.find(1)
# => <Pokemon id: 1, name: "Oddish">

oddish.next_evolved_forms
# => [ <Pokemon id: 2, name: "Gloom"> ]

gloom = oddish.next_evolved_forms.first
# => <Pokemon id: 2, name: "Gloom">

gloom.next_evolved_forms
# => [ <Pokemon id: 3, name: "Vileplume">, <Pokemon id: 4, name: "Bellossom"> ]

vileplume = gloom.next_evolved_forms.first
# => <Pokemon id: 3, name: "Vileplume">

bellossom = gloom.next_evolved_forms.second
# => <Pokemon id: 4, name: "Bellossom">

# testing the nested methods:

oddish.ancestor_evolved_forms
=begin
{}
=end

oddish.descendant_evolved_forms
=begin
{
  <Pokemon id: 2, name: "Gloom"> => {
    <Pokemon id: 3, name: "Vileplume"> => {},
    <Pokemon id: 4, name: "Bellossom"> => {}
  }
}
=end


gloom.ancestor_evolved_forms
=begin
{
  <Pokemon id: 1, name: "Oddish"> => {}
}
=end

gloom.descendant_evolved_forms
=begin
{
  <Pokemon id: 3, name: "Vileplume"> => {},
  <Pokemon id: 4, name: "Bellossom"> => {}
}
=end


vileplume.ancestor_evolved_forms
=begin
{
  <Pokemon id: 2, name: "Gloom"> => {
    <Pokemon id: 1, name: "Oddish"> => {}
  }
}
=end

vileplume.descendant_evolved_forms
=begin
{}
=end


bellossom.ancestor_evolved_forms
=begin
{
  <Pokemon id: 2, name: "Gloom"> => {
    <Pokemon id: 1, name: "Oddish"> => {}
  }
}
=end

bellossom.descendant_evolved_forms
=begin
{}
=end

待办事项:

如果“进化”以某种方式循环回到起点(这永远不会发生),请向口袋妖怪添加模型验证以防止“StackLevel 错误”。

尽可能避免重新发明轮子总是好的。

您可能会在祖先 gem 中找到一些好处,它用于将模型组织成树结构,这正是您的目标。

https://github.com/stefankroes/ancestry