Problem z relacjami

Tym razem ugrzązłem na dobre i bez waszej pomocy chyba nie ruszę z miejsca. Mam dwa modele połączone relacjami:

[code=ruby]class Player < ActiveRecord::Base
has_many :black_games, :class_name=>‘Game’, :foreign_key=>‘black_id’
has_many :white_games, :class_name=>‘Game’, :foreign_key=>‘white_id’
(…)

class Game < ActiveRecord::Base
belongs_to :black, :class_name=>‘Player’
belongs_to :white, :class_name=>‘Player’
(…)[/code]
Na pierwszy rzut oka wygląda OK i dla gier granych czarnymi jest wszystko cacy. Ale dla przy zapytaniu o gry grane białymi …

player=Player.find(4) bg = player.black_games(:conditions=>{:accepted=>true}) wg = player.white_games(:conditions=>{:accepted=>true}) (z logów) SELECT * FROM `games` WHERE (`games`.black_id = 4 AND (`games`.`accepted` = 1)) SELECT * FROM `games` WHERE (`games`.white_id = 4 AND (`games`.black_id = 4 AND (`games`.`accepted` = 1)))
To oczywiście jest bzdura bo nie ma gier, które gracz rozgrywałby z samym sobą.
Jak sobie z tym poradzić?

Ja bym zrobił tak:

Player has_many :games
Game belongs_to :player

Następnie dla Game zdefiniował named scope’a black, white (musisz dodać odpowiednią kolumnę, która to przechowa) i accepted

Potem używał tego tak:

player = Player.first bg = player.games.black.accepted wg = player.games.white.accepted
IMO mniej namieszane, bardziej eleganckie i z pewnością odporne na błąd, który tu się pojawił. :slight_smile:

Chyba czegoś nie rozumiem. Czy przypadkiem “belongs_to :player” nie wymaga istnienia player_id? I co miałoby być w tym polu, przecież w grze bierze udział dwóch graczy więc który miałby trafić do player_id?
Chyba Twoja odpowiedź jest zbyt ogólna na moją wiedzę. Gdybyś zechciał trochę bardziej szczegółowo …

coś musiałeś namieszać, bo zgodnie z tym co ty zrobiłeś u mnie działa.
Utworzyłem sobie:
#<Player id: 1, name: “Biały”>
#<Player id: 2, name: “Czarny”>
#<Game id: 1, black_id: 2, white_id: 1, accepted: true>

pytanie:
white_game = player_one.white_games(:conditions => {:accepted=>true}) # zwraca grę pierwszą
black_game = player_two.black_games(:conditions => {:accepted=>true}) # zwraca grę pierwszą

oczywiście te nie zwrócą niczego:
nic = player_one.black_games(:conditions => {:accepted=>true})
nic = player_two.white_games(:conditions => {:accepted=>true})
jeśli będzie tylko ta jedna gra.

czy jesteś pewien, że to jest to co chciałeś?

pozdrawiam

Faktycznie troszkę zamotałem :frowning:
Ale i tak nie jest tak różowo. Dodaj jeszcze rekord:
#<Game id: 2, black_id: 2, white_id: 1, accepted: false>
Teraz wykonaj poprzednie zapytania … jeden rekord czy dwa?

A teraz takie zapytania:

white_game = player_one.white_games.find(:all,:conditions => {:accepted=>true}) black_game = player_two.black_games.find(:all,:conditions => {:accepted=>true})
Co ciekawe. Przywróciłem kod do stanu z wczorajszego dnia i sprawdziłem na konsoli powyższe pytania … działają. Ale zapisane w metodzie już nie

[code=ruby]def all_games(*args)

pl = self.class.find(self.id)

bg = pl.black_games.find(:all, *args)

wg = pl.white_games.find(:all, *args)

bg = black_games.find(:all, *args)
wg = white_games.find(:all, *args)
	
([bg]+[wg]).flatten.compact.sort { |a,b| a.date <=> b.date  }

end[/code]
Zostawiłem zakomentowane linie bo tak też próbowałem.
Sprawa jest dość istotna. Bo pytanie a wszystkie gry danego gracza spełniające określone warunki bardzo często pojawia się w aplikacji. Np. coś takiego (p-aktualny gracz, g-aktualnagra):

p.all_games(:conditions=>['`date` > :date and `accepted` = :accepted',{:date=>g.date,:accepted=>true}])

Wynik w logach:

SELECT * FROM `games` WHERE (`games`.black_id = 4 AND (`date` > '2009-10-13 10:31:00' and `accepted` = 1)) SELECT * FROM `games` WHERE (`games`.white_id = 4 AND (`games`.black_id = 4 AND (`date` > '2009-10-13 10:31:00' and `accepted` = 1)))
Czyli pierwsze zgodnie z oczekiwanie a drugie …

[EDYCJA]
OK. Znalazłem przyczynę problemu i rozwiązanie (?)

def all_games(*args) b = Marshal.load(Marshal.dump(args)) w = Marshal.load(Marshal.dump(args)) bg = black_games.find(:all, *b) wg = white_games.find(:all, *w) ([bg]+[wg]).flatten.compact.sort { |a,b| a.date <=> b.date } end
Problem tkwił w tym, że args było modyfikowane przy pierwszym wywołaniu (obliczenie bg) i do drugiego wywołania trafiała już zmodyfikowana lista parametrów :frowning:

A tak swoją drogą … Nie ma lepszej metody na kopiowanie tablic niż serializacja?