2011年9月22日木曜日

Rails3を初歩から学ぶ #22 関連の実装

この一連のエントリは
Ruby on Rails 3 Tutorial: Learn Rails by Example (Addison-Wesley Professional Ruby Series)
で参考に学んだことを凝縮してお送りしています。

前回作成したテストをパスできるよう実装を進めます。
まずはUserモデルを編集します。
validatesをしている辺りに以下の行を追加します。

has_many :fans, :foreign_key => "user_id",
:dependent => :destroy
has_many :movies, :through => :fans
has_manyは1対多関係における1側(多を持つ側)を表します。
Userモデル1つにつき複数のfanモデル、movieモデルを持つことを意味します。
has_manyのパラメータは複数形になります(fans, movies)。
これでuser.fansとするとfanオブジェクトの配列にアクセスできます。
foreign_keyは外部キーを指定します。
「:dependent => :destroy」はUserモデルを削除したときに連動して該当するuser_idのFanテーブルの項目を削除するように指定します。

2行目のhas_many文は「user.movies」とすることでそのユーザーが好きな映画の配列にアクセス可能とするための文です。userとmovieの間は直接ではなくFanテーブルを間に挟んでいます。user_idとmovie_idをフィールドに持つFanテーブルを通じてUserテーブルとMovieテーブルの多対多関係を表しています。
こうすることでuser.moviesとするだけで、そのユーザーの好きな映画情報にアクセスできます。

では次にapp/model/fan.rbを編集します。

class Fan < ActiveRecord::Base
belongs_to :user
belongs_to :movie
validates :user_id, :presence => true
validates :movie_id, :presence => true
end
1対多の多側(相手が1つ)なのでbelongs_toで相手側モデルを指定します。
belongs_toのパラメータは単数になります(user, movie)。
そしてMovieモデルを編集します。
以下の2行を追加します。

has_many :fans
has_many :users, :through => :fans
Userモデルで定義した内容と逆方向です。
これでmovie.usersとすることでその映画を好きだといっているユーザー配列にアクセスできます。
次にuserモデルに好きな映画を登録する機能を追加しましょう。
まずはspec/model/user_spec.rbにテストコードを追加します。
beforeブロックの赤字部分と、以降の検査コードを追加します。
describe "fans" do
before(:each) do
@user = Factory(:user)
@movie = Factory(:movie)
end
〜(省略)〜
it "favorite_movie!メソッドが機能すること" do
@user.should respond_to(:favorite_movie!)
end
it "unfavorite_movie!メソッドが機能すること" do
@user.should respond_to(:unfavorite_movie!)
end
it "favorite?メソッドが機能すること" do
@user.should respond_to(:favorite?)
end
it "追加した映画が登録されていること" do
@user.favorite_movie!(@movie)
@user.movies.should include(@movie)
end
it "削除した映画が登録されていないこと" do
@user.favorite_movie!(@movie)
@user.unfavorite_movie!(@movie)
@user.movies.should_not include(@movie)
end
ここでは好きな映画を登録する「favorite_movie」、解除する「unfavorite_movie」、さらにその映画が好きかどうかを返す「favorite?」メソッドが存在するかどうかといったことなどをチェックしています。
では対応するコードをapp/model/user.rbに定義しましょう。
def favorite_movie!(movie)
fans.create!(:movie_id => movie.id)
end
def unfavorite_movie!(movie)
fans.find_by_movie_id(movie).destroy
end
def pushed?(member)
members.include?(member)
end
「favorite_movie!」ではFanモデルのインスタンスを新たに生成しています。
「unfavorit_movie!」では渡されたmovieのidからfanインスタンスを検索して、該当するfanモデルを削除します。


0 件のコメント:

コメントを投稿