13
13

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Railsで「いいね!」機能を作る - ①アソシエーションに別名をつける

Last updated at Posted at 2020-08-30

背景

最近初学者さんにプログラミングを解説する機会があるのですが、使っているテキストに、まあまあ難しそうだなー。と思える記述がありました。

流暢に解説できるか不安なところもあったので、勉強がてら、噛み砕いてまとめてみようと思います。何回かに分けて作成していきますので、記事は徐々に増やしていきます。

▼続編出しました!
Railsで「いいね!」機能を作る - ②「いいね!」のcreateアクション
Railsで「いいね!」機能を作る - ③「いいね!」を解除できるようにする

▼番外編
RailsでAjaxで「いいね!」機能を実装する。

データ構造

基本のデータ構造は以下の通り。
Twitterみたいなアプリで、ユーザー(users)はたくさんの投稿(posts)を持っている。そして、自分の投稿も含む全ての投稿に「いいね!」(likes)を付けられる。という感じです。

Image from Gyazo

シンプルな構造なのですが、後半、ちょっとひねりが必要でした。

アソシエーションを書き起こしてみる

まずは、Railsの規則通りにシンプルに書けるところからアソシエーション(つながり・関係性)を書き起こしてみます。

models/user.rb
class User < ApplicationRecord
  has_many :posts
  has_many :likes
end
models/post.rb
class Post < ApplicationRecord
  belongs_to :user
  has_many :likes
end
models/like.rb
class Like < ApplicationRecord
  belongs_to :user
  belongs_to :post
end

とてもシンプルですね:slight_smile:
この時、例えばcontrollerで以下のように書くと、

controllers/users_controller.rb
class UsersController < ApplicationController
  def show
    @posts = User.find(params[:user_id]).posts
  end
end

**「そのユーザーの投稿一覧」**を取得できます。(今回は例としてユーザー詳細ページで一覧表示する形にしています)

問題点

この時、例えば**「そのユーザーが『いいね!』した投稿一覧」**を表示したいと思ったときには、少し工夫が必要です。

おそらく、下記@favorite_postsのように記載すれば**「そのユーザーが『いいね!』した投稿一覧」**は取得できるように予想しますが、

controllers/users_controller.rb
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は既に使われていて、

models/user.rb(再掲)
class User < ApplicationRecord
  has_many :posts
  has_many :likes
  # has_many :posts, through: :likes とは書けない。
end

has_many :posts, through: :likesと書くと、名前が重複してしまうからです。
ちなみに、through:を使わないで、has_many :postsから「いいね!」した投稿を取得する方法もあるにはあるのですが、

難しい上に、データの読み出しにコストがかかるので、お勧めはしません。

アソシエーションに別名をつけて解決

そんな時は、userlikespostsという関係の流れに別名をつけて解決します。
つまり、下記の図の赤枠で囲ったアソシエーション(つながり)に別名を付ければいいのです。

er.png

別名の付け方は下記の通りです。

models/user.rb
class User < ApplicationRecord
  has_many :posts
  has_many :likes
  has_many :favorites, through: :likes, source: :post
end

上記のコードではuserlikespostsというアソシエーションに、favoritesという別名をつけています。

ただし、そのままだとrailsはfavoritesというテーブルを探してしまうので、source: :postというオプションをつけてpostsテーブルを参照するようにします。

上記のように書くことで、user.favoritesと記載すると、**「ユーザーが『いいね!』した投稿」**を取得することができます。(下記は、こんな感じで使えますよーというサンプルです:slight_smile:

controllers/users_controller.rb
class UsersController < ApplicationController
  def show
    # ↓ユーザーの投稿一覧
    @posts = User.find(params[:user_id]).posts
    # ↓ユーザーがいいねした投稿一覧
    @favorite_posts = User.find(params[:user_id]).favorites
  end
end

早い人は、プログラミング初心者から数週間でこの内容を理解して卒業していくんですね。すごいなー。

私も自信持って教えられるように、次は「いいね!」をする、「やめる」のコードについても研究したいと思います。

▼続きはこちら
Railsで「いいね!」機能を作る - ②「いいね!」のcreateアクション

追記:逆の関係(投稿に「いいね!」したユーザーを取得)

なお、投稿に「いいね!」したユーザー(postlikeuserのアソシエーション)は次のような記述で定義することができます。

models/post.rb
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です^^

13
13
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
13
13

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?