2

I have a simple database with 2 models:

class Fighter < ActiveRecrod::Base
   has_many :fights
   has_many :opponents, through :fights
end

class Fight < ActiveRecord::Base
   belongs_to :fighter
   belongs_to :opponent, class_name: 'Fighter'
 end`

What should I add for this to work:

a = Fighter.create
b = Fighter.create
a.opponents << b
b.fights <- should return a value`

a.fights should return newly created Fight

b.fights should return newly created Fight as well

so far only a.fights returns the value

I came up with such idea:

class Fight < ActiveRecord::Base
  belongs_to :fighter
  belongs_to :opponent, class_name: 'Fighter'
  after_create :create_association

  def create_association
    Fighter.find(opponent_id).fights << self
  end
end

but calling another model from Fight doesn't seem right.

I also tried to overload has_man :fights in Fighter so that it takes lambda

has_many :fights, -> { where("fighter_id = ? OR opponent_id = ?", self.id, self.id) but I constantly get error - wrong number of arguments (0 for 2)

after researching a bit, I know I should use a bidirectional self-referential association, similar to this: Bidirectional self referential associations

but when follow this, then I receive two records of a fight in table Fight. One that is associated with one fighter and another that is associated with second fighter.

I would appreciate help or some hints in how to solve this issue

Community
  • 1
  • 1
beniutek
  • 1,672
  • 15
  • 32
  • can you also include your attempt to follow the post you linked? It would help understand what might be going wrong. – Damon Feb 26 '16 at 01:45

1 Answers1

3

If you don't want to define inverse relationships (as suggested in this Railscast), I don't think you can avoid creating two fights every time. Your idea of using after_create would work, just you need to add the opponent and not the fight itself, like so:

class Fight < ActiveRecord::Base
  belongs_to :fighter
  belongs_to :opponent, class_name: 'Fighter'

  after_create :create_association

  def create_association
    opponent = Fighter.find(opponent_id)
    unless opponent.opponents.include?(fighter)
      Fighter.find(opponent_id).opponents << fighter
    end
  end
end

Inverse relationships would be better in terms of performance and storage, but not as seamless to use:

class Fighter < ActiveRecord::Base
  has_many :fights
  has_many :opponents, through: :fights

  has_many :inverse_fights, :class_name => "Fight", :foreign_key => "opponent_id"
  has_many :inverse_opponents, :through => :inverse_fights, :source => :fighter
end

From here, if you want all opponents for a fighter, you should do a_fighter.opponents + a_fighter.inverse_opponents

taglia
  • 1,837
  • 2
  • 13
  • 16
  • it works! the inversed relationship works well for my case. Thanks for help. Marking it as an answer – beniutek Feb 26 '16 at 12:26