🦁

[Rails]いいね機能の作成(モデル編)

2025/02/01に公開

モデル

class Task < ApplicationRecord
  belongs_to :user
  has_many :comments, class_name: "TaskComment", dependent: :destroy
  has_many :favorites, dependent: :destroy

  def favorited_by?(user)
    favorites.exists?(user_id: user.id)
  end
end

下記部分について、疑問点が多かったので調べていく!

def favorited_by?(user)
    favorites.exists?(user_id: user.id)
end

favorited_by? はビューで「ユーザがその投稿をいいねしてたら、マークの色を変える」のに使う!

<% if post.favorited_by?(current_user) %>
  <button class="favorited">★ お気に入り済み</button>
<% else %>
  <button class="not-favorited">☆ お気に入り</button>
<% end %>

こうすることで、すでにいいねしている場合はボタンの見た目を変える ことができる!


なぜモデルで定義するのか?

ビューで使う機能なら、ビューやコントローラーに書いてもいい気がする。
なぜモデルに書くの?
モデルって「お気に入り機能がユーザやタスクなどほかのモデルとどのようにつながってるか?」「いいねモデルってどんな決まり・形式なのか?」を書くのであって、「ユーザがいいねしてるか」はモデルと関係なくない?

(1) ビジネスロジックをモデルにまとめるため

モデルは「ビジネスロジック」をまとめるもの。

ビジネスロジック
アプリケーションの「業務的なルール」や「データ処理のルール」。
データの取得・計算・判定など、アプリケーションの振る舞いを決定する処理。
そのアプリケーションならではのルールが「ビジネスロジック」。
例えば、今回のアプリケーションだと、ユーザはタスクを複数作成できる・タスク名は30字以内・ユーザがそのタスクをお気に入りしてるかどうか?

favorited_by?(user)「この投稿がユーザーにいいねされているか?」 という データに関する判断 をするメソッド。
このような 「データベースに保存されてる情報を元に、特定の条件を満たすかどうかのチェック」 は、コントローラーではなくモデルに書くのがRailsの設計として適切
Fat Modelの原則

Fat Model
⇒モデルが持つ責任を多くする。

下記みたいにコントローラーで書くこともできるけど、
show アクションなどに特定のアクション内に書くことになり、他のアクションやコントローラー内でも使いたくなったときに同じコードを何度も書く必要がある ので、モデルにメソッドとして定義しておく方が便利

@favorited = Favorite.exists?(post_id: @post.id, user_id: current_user.id)

モデル・コントローラ・ビューのそれぞれの役割

レイヤー 役割
ビジネスロジック (モデル) アプリケーションのデータのルール・処理 favorited_by?(user)
アプリケーションロジック (コントローラー) 画面やAPIにデータを渡す @post = Post.find(params[:id])
プレゼンテーションロジック (ビュー) 画面の表示に関する処理 if post.favorited_by?(current_user)

【それぞれの役割】

ビジネスロジック (モデル)

  • データの処理や業務ルールの実装
  • 例: 「ユーザーがいいねしているか判定する」

アプリケーションロジック (コントローラー)

  • どのデータをどこ(ビューやAPI)に渡すか?
  • 例: @favorited = @post.favorited_by?(current_user)

プレゼンテーションロジック (ビュー)

  • どこにどうやって表示させるか?
  • 例: if post.favorited_by?(current_user)

「ユーザーが特定の投稿をお気に入り登録しているか?」は、そのアプリの「お気に入り機能」のルール 。

「ユーザーが投稿をお気に入り登録できる」
「お気に入り登録しているかどうか判定できる」
これらのルールはアプリの動作に関わる 業務的な判断 なので、ビジネスロジック。


(2) コントローラーがシンプルになる

モデルに favorited_by?(user) を定義していれば、コントローラーはこんな感じでスッキリする。
Skinny Controllerの原則

Skinny Controller
⇒コントローラはできるだけシンプルにする

@favorited = @post.favorited_by?(current_user)

コントローラーの責務は「データの取得・画面に渡すこと」なので、ロジックはモデル側に寄せるのがベスト


favorited_by?(user)部分について

by には特別な意味はなく、単なるメソッド名の一部。
favorited_by?(user) というメソッド名は 「このオブジェクトが指定したユーザー(user)によってお気に入り登録されているか?」 を判定することを意図してる。

by の役割

英語的なニュアンスとして、by「~によって」 という意味を持つ前置詞です。そのため、favorited_by? という名前は 「(特定のユーザー)によってお気に入り登録されているか?」 という意味になります。

いいね機能のモデルをlikeで作成したなら、likedと書いてもいい。

例:

def liked_by?(user)
  likes.exists?(user_id: user.id)
end

アクションの過去形_by(xx)? の形にすると、「xx(ユーザーや特定の条件)によって、そのアクションが実行されたか?」 を判定するメソッドになる。

例1: 投稿が特定のユーザーによって作成されたかを判定

def created_by?(user)
  self.user_id == user.id
end

例2: ユーザーが特定の投稿を編集できるか判定

def editable_by?(user)
  user.admin? || self.user_id == user.id
end

favorites.exists?(user_id: user.id)部分について

def favorited_by?(user)
    favorites.exists?(user_id: user.id)
end

favorites には この投稿をお気に入り登録したすべてのユーザー情報が入ってる。
favorites テーブルに、user_id が今ログインしている user.id のレコードが存在するか?をチェックしてる。


思ったよりモデルだけで書くことが多くなってしまったので、
コントローラ・ビューについては明日以降まとめます!

MVCの役割が学べて面白かった!


参考文献

https://qiita.com/nashirox/items/edf5e8e9e7b8fc6891d3
https://zenn.dev/airiswim/articles/2215300ede2ff6
https://olegkrivtsov.github.io/using-zend-framework-3-book/html/en/Model_View_Controller/Skinny_Controllers__Fat_Models__Simple_Views.html
https://developer.mozilla.org/en-US/docs/Glossary/MVC

Discussion