Hatena::Diary

篳篥日記 このページをアンテナに追加 RSSフィード

2008-11-16 Railsで作るTwitterもどき

[] Railsで作るTwitterもどき(序) 00:19  Railsで作るTwitterもどき(序) - 篳篥日記 のブックマークコメント

吉見さんのスクリーンキャストから1年近くたった。

Ruby on Railsで10分で作るTwitterもどき - スクリーンキャスト - ZDNet Japan

ということで、初心者向けにスクリーンキャストだけではちょっと分からない部分について書いてみようかと。

モデル設計などはなるべく沿うつもりで。

[] Railsで作るTwitterもどき(下準備編) 00:19  Railsで作るTwitterもどき(下準備編) - 篳篥日記 のブックマークコメント

とりあえず、今回は認証回りについてはログインができれば良いということで作る。

$ rails rtwitter

でプロジェクトを作って、以下、ユーザモデルや認証部分の作成は、

2008-10-13 - 篳篥日記

を参考にどうぞ。

[] Railsで作るTwitterもどき(モデル編) 00:19  Railsで作るTwitterもどき(モデル編) - 篳篥日記 のブックマークコメント

ではモデル設計から。

  • ユーザはつぶやきができる
  • ユーザは他のユーザをフォローできる (あなたがフォロー)
  • 他のユーザも自分をフォローできる (あなたをフォロー)

とりあえず、つぶやきについては後回しにするとして、フォローについてモデルだけ作ってしまおう。

f:id:hichiriki:20081116232129p:image

上は簡略化した図だが、ここでは自分がフォローしている人達をfriends、自分をフォローしてくれている人達をfollowersと表すことにする。

ユーザとフォロワーは多対多の関係となる。Railsで多対多の関連を張る場合には、関連テーブルを間に挟む方法が取られる。この関連テーブルをFriendshipとしておく。

f:id:hichiriki:20081116232746p:image

関連テーブル作成

$ ./script/generate model Friendship user_id:integer friend_id:integer

たいていのhas_many :throughだと、モデルの左右から関連を張らなければいけない場合が多いのだが、まずは、図でいうと左から右の、片側だけ関連を張ることにする。関連は必ず両方から張らなければいけないものではないので、このように片側だけ張るということもある。

関連名は下図のように、Userから見たhas_manyの方をfriendships, Friendshipから見たbelongs_toをfriendとする。

f:id:hichiriki:20081116233028p:image

# app/models/user.rb
class User < ActiveRecord::Base
  has_many :friendships, :dependent => :destroy

  # 以下略
end

":dependent => :destroy" で、ユーザが消えたら関連テーブルも消すようにしておく。

# app/models/friendship.rb
class Friendship < ActiveRecord::Base
  belongs_to :user
  belongs_to :friend, :class_name => 'User', :foreign_key => :friend_id
end

Rails初心者だと、関連名はテーブル名の単数形をアンダースコア化したものじゃないと付けられないとか、foreign_keyもテーブル名の単数形_idじゃないといけないとか思いがちだが、別にそんなことはない。自由に付けられる。

上の friendという関連も、関連先のクラスはUserで、外部キーはfriend_idにしている。


ここまで普通にhas_many, belongs_toの関連が張れたら、has_many :throughの関連をUserモデルに追加する。

# app/models/user.rb
class User < ActiveRecord::Base
  # あなたがフォロー
  has_many :friendships, :dependent => :destroy
  has_many :friends, :through => :friendships # 追加

  # 略
end

これで、ユーザに対して friendsという関連が張られ、current_user.friendsとすることで、自分がフォローしているユーザ(「あなたがフォロー」)が取得できるようになった。

f:id:hichiriki:20081116233753p:image

この状態で他のユーザをフォローするには、friendsにフォローしたいユーザモデルを追加する方法と、関連テーブルを直接作る方法とがある。これは状況に応じて使い分けると良い。

# 関連にモデルを追加
current_user.friends << user

# または関連テーブルを直接つくる
Friendship.create(:user_id => current_user.id, :friend_id => user.id)

片側の関連が張れたので、今度は逆側の関連を張ろう。

逆側の関連を張ることで、「あなたをフォロー」のユーザ一覧が取得できるようになる。

逆からの関連名は、followershipsとuserで。

f:id:hichiriki:20081116234909p:image

Userモデルに関連を追加する。

# app/models/user.rb
class User < ActiveRecord::Base
  # あなたがフォロー
  has_many :friendships, :dependent => :destroy
  has_many :friends, :through => :friendships

  # あなたをフォロー
  has_many :followerships, :class_name => 'Friendship', :foreign_key => :friend_id, :dependent => :destroy # (1)
  has_many :followers, :through => :followerships, :source => :user # (2)

  # 略
end

まず (1) から。

関連名(:followerships)とクラス名(Friendship)が異なっているので、:class_nameでクラス名を指定。foreign_key は:friend_idを使う。

次に(2)。

":source => :user" の部分には、関連テーブルから先のモデルにアクセスするための(関連モデルから見た)関連名を入れる。ここでは、Friendshipモデルからその先のユーザ(user_idで繋がっている)に行くための関連名のシンボル :userを入れておく。


これで逆側の関連も張れた。

f:id:hichiriki:20081116235318p:image

current_user.followers で、自分をフォローしているユーザの一覧が取得できる。


では次に、つぶやきについて見てみよう。

  • ユーザはつぶやきができる (User has_many Tweets)

なので、そのまま has_many で関連を張ることにする。これは簡単。

もしも、「お気に入り」を実装するのであれば、下図のように関連テーブルを使うのもひとつの手だ。

f:id:hichiriki:20081116235914p:image

ここではそのように作ってみる。つぶやき(Tweet)モデルと、お気に入りの関連モデル(Favoriteship)を作成。

$ ./script/generate model Tweet user_id:integer body:string

$ ./script/generate model Favoriteship user_id:integer tweet_id:integer

関連を張る。


まずユーザ。

# app/models/user.rb
class User < ActiveRecord::Base
  # あなたがフォロー
  has_many :friendships, :dependent => :destroy
  has_many :friends, :through => :friendships

  # あなたをフォロー
  has_many :followerships, :class_name => 'Friendship', :foreign_key => :friend_id, :d
ependent => :destroy
  has_many :followers, :through => :followerships, :source => :user

  # つぶやき
  has_many :tweets, :dependent => :destroy

  # お気に入り
  has_many :favoriteships, :dependent => :destroy
  has_many :favorite_tweets, :through => :favoriteships, :source => :tweet

  # 略
end

次につぶやき。

# app/models/tweet.rb
class Tweet < ActiveRecord::Base
  belongs_to :user
  has_many :favoriteships, :dependent => :destroy
  has_many :favorite_users, :through => :favoriteships, :source => :user
end

最後はお気に入り。

class Favoriteship < ActiveRecord::Base
  belongs_to :user
  belongs_to :tweet
end

お気に入りの追加や参照は以下のようにする。

# 自分のお気に入りを参照
current_user.favorite_tweets

# お気に入りを追加
current_user.favorite_tweets << tweet

# あるつぶやきをお気に入りにしているユーザを取得
tweet.favorite_users

これで、Twitterもどきに必要なフォローの関連と、つぶやき、お気に入りの関連を張ることができた。

Railsで作るTwitterもどき (2) に続く。