みなさんこんばんは、まさです。
今日はついに、Railsチュートリアルの最終章、14章についてまとめていきます。
感想としては、さすが14章と言ったところでしょうか。
前回の記事でお話したとおり一つひとつ網羅的に要約はせず、僕がまとめておきたいポイントだけ書いていきます。
また、今回はActiveRecordの関連づけについてのみまとめていきます。
ActiveRecordの関連づけLevel1
Twitterを模したアプリケーションをつくるのがRailsチュートリアルの目標なので、フォローする、されるといった機能を実装しなければなりません。
そこでフォローしている人・フォローされる人を技術的にどう判断するかという話から、本章は始まります。ちなみに自分がフォローしている人たちのことをフォローイング(following)と呼び、自分をフォローしている人たちのことをフォロワー(follower)と呼びます。
フォローすることやされることをデータベースの設計から考える
前回までのマイクロポストに対する関連付けから考えると、ある一人のユーザーはフォローイング(またはフォロワー)を複数持っているという意味で一対多の関係ですので、has_many関連付けをfollowing(follower)テーブルで行えばいいんじゃないかっていうアイデアが真っ先に浮かびそうです。
ただ、これだとuserテーブルとかぶるカラムが多すぎだったり、同じくらい無駄が多いfollowerテーブルをつくらないといけなかったりして困ります。
ここで、解決策としてrelationshipsテーブルを作り、用途に応じてそのテーブルをactive_relationshipsと呼んだりpassive_relationshipsと呼ぶ方法を使います。直訳するとこれらは能動的関係(フォローしていること、following)、受動的関係(フォローされること、followers)を指します。
能動的関係について考える
まずは能動的関係(active_relationships)について考えていきます。
app/models/user.rb
class User < ApplicationRecord has_many :microposts, dependent: :destroy has_many :active_relationships, class_name: "Relationship", foreign_key: "follower_id", dependent: :destroy . . . end
コードは上のとおり。あるユーザーは複数の能動的関係を持っているということですね。ちなみに、active_relationshipsはこちらが勝手にRelationshipモデル(relationshipsテーブル)の一面をそのように「見立てて」いるだけなので、しっかりクラス名をオプションとして渡しています。
また、今回追加したコードの上にあるhas_many :micropostsという関連付けでは、マイクロポストにuser_idがあり、誰のマイクロポストなのかを特定できたことを思い出してください。つまり、あるモデルにおいてhas_many関連付けを行うと、関連付けられたモデルの中にその<モデル(クラス)名>_idを探すのです。あるモデルと他のモデルを繋ぐidを外部キーと呼びます。
しかし、今回はActiveRelationship(Relationship)モデルではuser_idを使うのではなく元々設計上あるfollower_idを外部キーとして宣言しています。
一方、Relationshipモデルの方は以下のような関連付けを行います。
app/models/relationship.rb
class Relationship < ApplicationRecord belongs_to :follower, class_name: "User" belongs_to :followed, class_name: "User" end
ここで僕はこのbelongs_to関連付けの意味を理解するのに時間がかかりました。そこで、このbelongs_to関連付けについてお話したいと思います。
例えば、Micropostモデルでは
class Micropost < ApplicationRecord belongs_to :user . . . end
のようにすることで、micropostsテーブルにuser_idという外部キーが作られるのでした。
さきほどのRelationshipモデルに関する関連付けをもう一度見てみると、
class Relationship < ApplicationRecord belongs_to :follower, class_name: "User" belongs_to :followed, class_name: "User" end
外部キーとしてfollower_idとfollowed_idを使いたいがために、belongs_to :follwer(followed)と宣言していますね。ただし、FollowerモデルもFollowedモデルも実際していないのでこれらはUserモデルのことを指しているよとオプションで伝えています(ちなみにfollowed_idは(仮想の)passive_relationshipsテーブルで使われます)。
has_many関連づけとbelongs_to関連づけ
これらを設定することで、どんなことが起こるのでしょうか?
例えば
user.active_relationships
はUserモデルのhas_many関連づけのおかげでuser.idとfollower_id(外部キー)が一致するactive_relationship(能動的関係)を全件取ってきます。
Relationship(今回はActiveRelationships)モデルで言えば
active_relationship.follower
とすることで、ある能動的関係のactive_relationship.follower_idとuser_id(外部キー)が一致するユーザー(すなわちフォロワー)を返します。
言い換えると、AモデルでBモデルに対してhas_many関連付けを行えばAモデルのidとBモデルの外部キーが一致するBモデルのレコード(データ)を取ってきます。
【A has_many Bの場合】
Aモデル(A.id)= Bモデル(A_id)←外部キー
で
Bモデルのデータを取ってくる
一方、CモデルでDモデルに対してbelongs_to関連付けを行えば、Cモデルの外部キーとDモデルのidが一致するCモデルのレコード(データ)を取ってくるのです。
【C belongs_to Dの場合】
Cモデル(D_id)←外部キー = Dモデル(D.id)
で
Cモデルのデータを取ってくる
直感的に捉えると、僕らの日常生活でも、サークルやクラブや企業などでは、あるグループに所属している方が「どこに所属しているのか」という証(会員証)を持っています。
ActiveRecordの関連づけの世界でも同じで、あるモデルに属している方が「どこに所属しているのか」という証(外部キー)を持っているわけです。
A has_many BならBがAに所属している(所有されている)ため、Bの方に外部キーがあります。
C belongs_to Dなら文字通りCがDに所属しているので、Cの方に外部キーがあります。
そして、あるグループに所属している人たち(会員)がグループにいてできることは自分が関わる範囲でグループについて知ることくらいです。逆にグループ側は新しく人を入会させたり、退会させたり、グループの構成員全体を知ることができます。
そう考えると、今回のUserモデルとRelationshipモデルの間ではhas_many関連づけとbelongs_to関連づけによって使えるようになったメソッドも理解しやすいかと思います。
ある能動的関係(active_relationship)はあるフォロワー(follower)やフォローされた人(followed)に属しているだけ(belongs_to)なので、この関係には誰がいるのかを参照するメソッドしか持ち合わせていません。
一方、あるユーザー(user)はいくつもの能動的関係(active_relationship)を所有している(has_many)ので、その関係を作ったり消したりすることができます。
ActiveRecordの関連づけLevel2
さて、ある能動的関係(active_relationship)を作ったり消したり、能動的関係からフォロワー(follower)やフォローされた人(followed)をを特定できるようになりました。
これらはフォローやアンフォローといった機能のために十分重要なものでしたが、それと同じくらい「誰があるユーザーをフォローしているのか」や「あるユーザーは誰をフォローしているのか」を表示できるようにすることもTwitterにおいては大切です。
そこで使われるのがhas_many through関連づけです。
has_many through関連づけ:following
これはhas_many関連づけとは似ても似つかない、全く別の関連づけです。この関連づけではRailsはモデル名 (単数形) に対応する外部キーを探します。
Userモデルにfollowingの関連づけをするには、以下のコードを書きます。
app/models/user.rb
class User < ApplicationRecord has_many :microposts, dependent: :destroy has_many :active_relationships, class_name: "Relationship", foreign_key: "follower_id", dependent: :destroy has_many :following, through: :active_relationships, source: :followed . . . end
あるユーザーはたくさんのfollowing(フォローしている人)を持っていて、それはそのユーザーのactive_relationshipsから知ることができる。そしてそれを知るための情報源はfollowed_idだぞっていうことですね。そしてactive_relationshipsを通して(through)ユーザーを返してくれます。超便利。
ちなみにhas_many through関連づけは
has_many :followeds, through: :active_relationships
のようにシンボルを渡せばfollowedsを単数形のfollowedと変換しfollowed_idをactive_relationshipsで探すことも可能なのですが、本章ではfollowedsという言葉は英文法的によろしくないのでTwitterを見習ってfollowingという名前をつけ、:sourceパラメーターを使用しています。
has_many through関連づけ:followers
同じように、全てのフォロワーを表示するfollowersや受動的関係を表すpassive_relationshipsは以下の通りになります。
app/models/user.rb
class User < ApplicationRecord has_many :microposts, dependent: :destroy has_many :active_relationships, class_name: "Relationship", foreign_key: "follower_id", dependent: :destroy has_many :passive_relationships, class_name: "Relationship", foreign_key: "followed_id", dependent: :destroy has_many :following, through: :active_relationships, source: :followed has_many :followers, through: :passive_relationships, source: :follower . . . end
このコードではfollowersに関するhas_many through関連づけのところでも:sourceパラメータを使っていますが、これはfollowingに関するhas_many through関連づけのコードと対応させるためにわざと書いているだけで、さきほど述べた通りこのケースでは:sourceパラメータは必要ないです。
これであるユーザーがフォローしているユーザーやあるユーザーをフォローしているユーザーを一度に表示できる準備が整いました。
少し長くなりましたが、これで本章におけるActiveRecordの関連づけに関するまとめを終えたいと思います。
また明日、本章の他のポイントに関する記事を書こうと思ってます。ではまた。