JeuWeb - Crée ton jeu par navigateur
[Rails] Association has_many :through - Version imprimable

+- JeuWeb - Crée ton jeu par navigateur (https://jeuweb.org)
+-- Forum : Discussions, Aide, Ressources... (https://jeuweb.org/forumdisplay.php?fid=38)
+--- Forum : Programmation, infrastructure (https://jeuweb.org/forumdisplay.php?fid=51)
+--- Sujet : [Rails] Association has_many :through (/showthread.php?tid=5679)



[Rails] Association has_many :through - popayan - 08-09-2011

Bonjour,

Une petite question sur un point pas très clair pour moi, tout d'abord mes classes:

class Player < ActiveRecord::Base

has_many :contracts
has_many :teams, :through => :contracts

def self.create_random_player
@p = Player.new
@p.name = "Toto"
@p.first_name = "James"
@p.birthday = "01/01/1901"
@p
end
end

class Team < ActiveRecord::Base
has_many :contracts
has_many :players, :through => :contracts

before_create :add_players

private

def add_players
(1..20).each do
self.players<<Player.create_random_player
end
end
end

class Contract < ActiveRecord::Base

belongs_to :team
belongs_to :player

end

En plus de ca, ma classe Contract possède une durée. La question est, dans mon code, où puis-je initialiser cette durée?


RE: [Rails] Association has_many :through - Sephi-Chan - 08-09-2011

Ton modèle de liaison Contact peut porter d'autres données que les foreign key. Tu peux lui donner des attributs comme pour n'importe quel modèle.

Si tu veux donner une valeur par défaut à des attributs, je te conseille la très bonne gem default_value_for. Elle est très souple à l'utilisation.

Si tu préfères le faire à la main, tu peux utiliser la méthode de callback after_initialize de ce modèle.
Autrement, tu peux définir une valeur par défaut au niveau de la base de données (via une migration change_column_default).


class Contract < ActiveRecord::Base
belongs_to :team
belongs_to :player

# Si tu optes pour la gem default_value_for.
default_value_for :duration, 3

# Si tu préfères faire ça à la main.
def after_initialize
# Attention à bien faire ||= pour ne pas surcharger s'il y a déjà une valeur.
self.duration ||= 3
end
end


Et ensuite, à l'utilisation :


player = Player.find(1)
team = Team.find(1)

# Le mode automatique. Pratique s'il n'y a pas de données annexes (ou si elles sont affectées par défaut).
team.players << player # Ça crée un objet en affectant player_id et team_id.
player.teams << team # Ça revient au même mais je trouve ça moins naturel.

# Le mode plus manuel, utile quand tu veux affecter des attributs annexes.
contract = team.contacts.create player_id: player.id,
duration: 3


Par ailleurs, je te déconseille de stocker la date de naissance dans un champ de type string, mais plutôt date ou datetime.
Voici une alternative à ta méthode create_random_player. L'utilisation d'une variable d'instance (@p) n'était pas utile. De plus, fais attention à ce genre de méthodes si tu y mets vraiment du random : mieux vaut générer les valeurs aléatoire et les passer en argument, ça rend le code plus facile à tester unitairement.


# Attention : c'est la notation des hash de Ruby 1.9, utilisable quand la clé est un symbole (bonne pratique).
# Quand la clé est une chaîne, il faut toujours faire "jambon" => "fumé".
def self.create_random_player
Player.new name: "Toto",
first_name: "James",
birthday: Date.new(2000, 12, 31)
end



RE: [Rails] Association has_many :through - popayan - 08-09-2011

Merci Sephi!

Je cherchais cette notation :

contract = team.contacts.create player_id: player.id,
duration: 3

j'ai eu beau avoir parcouru la doc, j'ai rien trouvé de semblable...
par contre le "contract =" devant est obligatoire?

Pour les autres remarques, merci c'est sympa de prendre du temps pour aider Smile



RE: [Rails] Association has_many :through - Viciousity - 08-09-2011

Bah c'est pas vraiment une notation spécifique à rails ;P
C'est en fait lié à la version 1.9 de Ruby qui permet de noter les hash-symbole de manière différente.

# on a une equivalence de
sym = { :one => "Premier", :twice => "Deuxieme" }
sym = { one: "premier", twice: "Deuxième" }



RE: [Rails] Association has_many :through - Sephi-Chan - 09-09-2011

(08-09-2011, 09:58 PM)popayan a écrit : Je cherchais cette notation :

contract = team.contacts.create player_id: player.id,
duration: 3

j'ai eu beau avoir parcouru la doc, j'ai rien trouvé de semblable...
par contre le "contract =" devant est obligatoire?

L'affectation sert juste à mettre la valeur retournée — ici, une instance de la classe Contract — dans une variable. Smile

Concernant la notation, create (qui sauvegarde l'objet en base, contrairement à build, qui affecte juste les différents attributs) peut prendre en argument un hash d'attributs. En appelant cette méthode sur l'association, Rails affecte automatiquement la foreign key (puisqu'on construit un contrat pour la team, il sait quel est le team_id). Cf. ActiveRecord::Associations::ClassMethods où tu verras les méthodes pour chaque type d'association (belongs_to, has_one, has_many) ou le guide sur les associations.

En Ruby, les parenthèses et accolades peuvent être optionnelles (tant que ce n'est pas ambigu). Ainsi, les notations suivantes sont rigoureusement équivalentes.


team.contacts.create({ player_id: player.id, duration: 3 })
team.contacts.create(player_id: player.id, duration: 3)
team.contacts.create { player_id: player.id, duration: 3 }
team.contacts.create player_id: player.id, duration: 3