背景
最近初学者さんにプログラミングを解説する機会があるのですが、使っているテキストに、まあまあ難しそうだなー。と思える記述がありました。
流暢に解説できるか不安なところもあったので、勉強がてら、噛み砕いてまとめてみようと思います。何回かに分けて作成していきますので、記事は徐々に増やしていきます。
▼続編出しました!
Railsで「いいね!」機能を作る - ②「いいね!」のcreateアクション
Railsで「いいね!」機能を作る - ③「いいね!」を解除できるようにする
▼番外編
RailsでAjaxで「いいね!」機能を実装する。
データ構造
基本のデータ構造は以下の通り。
Twitterみたいなアプリで、ユーザー(users
)はたくさんの投稿(posts
)を持っている。そして、自分の投稿も含む全ての投稿に「いいね!」(likes
)を付けられる。という感じです。
シンプルな構造なのですが、後半、ちょっとひねりが必要でした。
アソシエーションを書き起こしてみる
まずは、Railsの規則通りにシンプルに書けるところからアソシエーション(つながり・関係性)を書き起こしてみます。
class User < ApplicationRecord
has_many :posts
has_many :likes
end
class Post < ApplicationRecord
belongs_to :user
has_many :likes
end
class Like < ApplicationRecord
belongs_to :user
belongs_to :post
end
とてもシンプルですね
この時、例えばcontroller
で以下のように書くと、
class UsersController < ApplicationController
def show
@posts = User.find(params[:user_id]).posts
end
end
**「そのユーザーの投稿一覧」**を取得できます。(今回は例としてユーザー詳細ページで一覧表示する形にしています)
問題点
この時、例えば**「そのユーザーが『いいね!』した投稿一覧」**を表示したいと思ったときには、少し工夫が必要です。
おそらく、下記@favorite_posts
のように記載すれば**「そのユーザーが『いいね!』した投稿一覧」**は取得できるように予想しますが、
class UsersController < ApplicationController
def show
@posts = User.find(params[:user_id]).posts
@favorite_posts = User.find(params[:user_id]).likes.posts
# ↑こちらはあくまでも予想のコード、実際に動作しません。
end
end
User
モデルで、has_many :posts
は既に使われていて、
class User < ApplicationRecord
has_many :posts
has_many :likes
# has_many :posts, through: :likes とは書けない。
end
has_many :posts, through: :likes
と書くと、名前が重複してしまうからです。
ちなみに、through:
を使わないで、has_many :posts
から「いいね!」した投稿を取得する方法もあるにはあるのですが、
難しい上に、データの読み出しにコストがかかるので、お勧めはしません。
アソシエーションに別名をつけて解決
そんな時は、user
→likes
→posts
という関係の流れに別名をつけて解決します。
つまり、下記の図の赤枠で囲ったアソシエーション(つながり)に別名を付ければいいのです。
別名の付け方は下記の通りです。
class User < ApplicationRecord
has_many :posts
has_many :likes
has_many :favorites, through: :likes, source: :post
end
上記のコードではuser
→likes
→posts
というアソシエーションに、favorites
という別名をつけています。
ただし、そのままだとrailsはfavorites
というテーブルを探してしまうので、source: :post
というオプションをつけてposts
テーブルを参照するようにします。
上記のように書くことで、user.favorites
と記載すると、**「ユーザーが『いいね!』した投稿」**を取得することができます。(下記は、こんな感じで使えますよーというサンプルです)
class UsersController < ApplicationController
def show
# ↓ユーザーの投稿一覧
@posts = User.find(params[:user_id]).posts
# ↓ユーザーがいいねした投稿一覧
@favorite_posts = User.find(params[:user_id]).favorites
end
end
早い人は、プログラミング初心者から数週間でこの内容を理解して卒業していくんですね。すごいなー。
私も自信持って教えられるように、次は「いいね!」をする、「やめる」のコードについても研究したいと思います。
▼続きはこちら
Railsで「いいね!」機能を作る - ②「いいね!」のcreateアクション
追記:逆の関係(投稿に「いいね!」したユーザーを取得)
なお、投稿に「いいね!」したユーザー(post
→like
→user
のアソシエーション)は次のような記述で定義することができます。
class Post < ApplicationRecord
belongs_to :user
has_many :likes
has_many :users, through: :likes
end
先ほどつけた別名(favorites)は意識しなくてOKです。
なぜならユーザー(user)
と投稿(post)
の関係は、
-
user
が自分の作った投稿をもつpost belongs_to user
の関係と、 -
postが
自分に「いいね!」をしたユーザーを持つpost has_many users through likes
という二つの関係があるので
それそのまま記載すればOKです^^