どうした?
ProgateのRailsレッスンにてSNSサイトを作成していくなかで、投稿に関するモデルとユーザーに関するモデルを紐づける方法を学びました。
本記事では
- 異なるコントローラを紐づける必要性
- コントローラ同士を紐づける方法
を初学者なりに整理したものです。
目標物の確認
準備したもの
今回の目標物を実現するために最低限必要なファイルは以下です。
モデル
Postモデル 投稿に関するモデル
id
カラム: 投稿idとして使用。モデルに情報が入力されると自動生成される。
content
カラム: 投稿内容
user_id
カラム: 投稿者のユーザーid。誰が投稿したか?を示す。
Userモデル ユーザーに関するモデル
id
カラム: ユーザーidとして使用。モデルに情報が入力されると自動生成される。
name
カラム: ユーザー名
image_name
カラム: ユーザーが登録したユーザーに紐づく画像ファイル
コントローラ
postsコントローラ
show
アクション: 投稿内容を表示する
showアクション内では、どのidの投稿かをparams[:id]
で受け取り、そのidをもつ投稿情報(インスタンス形式)を@post
に代入しています。
# showアクション内で
@post = Post.find_by(id: params[:id])
params[:id]
はurlから得られる値です。
(usersコントローラ)
もちろん存在はしますが、今回の機能実装に必要なアクションはありません。
ビューファイル
posts/show
目標物を表示するためのビューファイルです。
ルーティング
get "posts/:id" => "posts/show"
url内の:id
によってどの投稿を表示しているか?を示します。
異なるコントローラを紐づける必要性
今、投稿内容を表示するページを作るとします。
投稿内容の詳細を表示するshow.html.erb 内で、
- 投稿内容
- ユーザー名
- ユーザー画像
を表示したいとき、
投稿内容 を表示するにはビューファイルで
# 投稿内容
@post.content
で表します。
@post
はparams[:id]
によって定義されているので、投稿内容はページがもつ情報だけで表示することができます。
では、ユーザー名やユーザー画像はどのように表示させれば良いでしょうか?
paramsハッシュにはユーザーに関する情報はありませんので、ページが持つ情報だけでユーザーを定義することができません。
ページがもつ情報からユーザーを割り出すとき、投稿とユーザーを紐づける必要性が発生します。
コントローラ同士を紐づける方法
異なるコントローラを紐づける方法として、下記の3種類を紹介します。
- アクション内で2重構造の定義をする
- モデル内で定義したメソッドを変数に対して行う
- has_one、has_manyとbelongs_to
これらについてまとめます。
アクション内で2重構造の定義をする
2重構造の定義(私が勝手に使っている言葉です)とは、params情報だけで完結する変数を定義し、その変数を用いて別の変数を定義する方法です。
showアクション内でユーザー情報を定義するには
# showアクション内で
@post = Post.find_by(id: params[:id])
@user = User.find_by(id: @post.user_id)
と、@post
のuser_id
カラム情報をユーザーidに代入することで@user
を定義します。
これを用いると投稿詳細ページで
# ユーザー名
@user.name
# ユーザー画像
@user.image_name
で表示することができます。
.name
と.image_name
はUserポストが持つカラムに対応する値を呼び出すメソッドです。
モデル内で定義したメソッドを変数に対して行う
先ほど紹介した方法だと、すべてのアクションで@user
とPostモデルを紐づける定義をする必要があります。
また、投稿一覧を表示するために@posts = Post.all
に対してeach文を使うときに@user
の定義が複雑になります。
(@posts.each
で生成される変数post
は、Postモデルに紐づいているため)
そこで、Postモデル内でユーザー情報と紐づけるメソッドを定義します。
#クラス定義式内で
def user
return User.find_by(id: self.user_id)
end
ここで、self.user_id
はPostモデル自身が持つインスタンス変数(カラム)です。
これにより投稿詳細ページで
# ユーザー名
@post.user.name
# ユーザー画像
@post.user.image_name
により、変数@post
を使用してユーザー情報を出力できます。
解説
モデルファイル内で定義したuserメソッドを@post
に対して行うと、その@post
インスタンスが持つuser_id
の値と一致するidを持つuserに関するインスタンスを検索することができます。
文章だとなんのこっちゃという感じなので、図解します。
has_one、has_many、belongs_to
userとpostには主従関係があります。
userは複数のpostを持つことができるので、userが「主」、postが「従」の関係です。
「主」のモデルにはhas_one
もしくはhas_many
「従」のモデルにはbelongs_to
を設定することで、上で紹介したモデル内で定義したメソッドを変数に対して行うのと同じことができます。
#userモデルファイル内で
has_many :post
#postモデルファイル内で
belongs_to :user
を設定すると
# @postからユーザー名を出力
@post.user.name
# @userから投稿内容を全て出力
@user.post.each do |post|
post.content
end
# ただし、@user = User.find(params[:id]) とする
が可能です。
これにより投稿詳細ページで
# ユーザー名
@post.user.name
# ユーザー画像
@post.user.image_name
により、変数@post
を使用してユーザー情報を出力できます。
ビューページのコード自体はモデル内で定義したメソッドを変数に対して行う方法と同じです。
まとめ
本記事では
- 異なるコントローラを紐づける必要性
- コントローラ同士を紐づける方法
について記載しました。
コントローラ同士を紐づける方法として
- アクション内で2重構造の定義をする
- モデル内で定義したメソッドを変数に対して行う
- has_one、has_manyとbelongs_to
を紹介しました。
Progateや書籍で勉強していると、「そもそもこの処理はなんで必要なんだっけ?」と思うことが多いです。
処理の必要性を意識して勉強していくとより身につくと感じました。